diff options
161 files changed, 22034 insertions, 11862 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 71c1ae4ac..44ed18531 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -34,11 +34,16 @@ jobs: - uses: actions/checkout@v1 - name: Create .ccache dir run: mkdir -p ~/.ccache + - name: prepare timestamp + id: get-timestamp + run: echo "timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT + shell: bash - name: ccache cache files uses: actions/cache@v4 with: path: ~/.ccache - key: ${{ runner.os }}-${{ matrix.config.cacheid }}-ccache + key: ${{ runner.os }}-${{ matrix.config.cacheid }}-ccache-${{ steps.get-timestamp.outputs.timestamp }} + restore-keys: ${{ runner.os }}-${{ matrix.config.cacheid }}-ccache - name: Pull the Docker Image run: docker-compose pull ${{ matrix.config.image }} - name: Print ccache stats @@ -110,11 +115,16 @@ jobs: - uses: actions/checkout@v1 - name: Create .ccache dir run: mkdir -p ~/.ccache + - name: prepare timestamp + id: get-timestamp + run: echo "timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT + shell: bash - name: ccache cache files uses: actions/cache@v4 with: path: ~/.ccache - key: ${{ runner.os }}-${{ matrix.config.cacheid }}-ccache + key: ${{ runner.os }}-${{ matrix.config.cacheid }}-ccache-${{ steps.get-timestamp.outputs.timestamp }} + restore-keys: ${{ runner.os }}-${{ matrix.config.cacheid }}-ccache - name: Pull the Docker Image run: docker-compose pull ${{ matrix.config.image }} - name: Print ccache stats @@ -157,11 +167,16 @@ jobs: - uses: actions/checkout@v1 - name: Create .ccache dir run: mkdir -p ~/.ccache + - name: prepare timestamp + id: get-timestamp + run: echo "timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT + shell: bash - name: ccache cache files uses: actions/cache@v4 with: path: ~/.ccache - key: ${{ runner.os }}-ccache + key: ${{ runner.os }}-clang-ccache-${{ steps.get-timestamp.outputs.timestamp }} + restore-keys: ${{ runner.os }}-clang-ccache - name: Install required packages run: | brew install ccache p7zip @@ -207,12 +222,29 @@ jobs: QT_ASSUME_STDERR_HAS_CONSOLE: 1 steps: - uses: actions/checkout@v1 + - name: prepare timestamp + id: get-timestamp + run: echo "timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT + shell: bash - name: clcache cache files uses: actions/cache@v4 with: path: ~/clcache - key: ${{ runner.os }}-msvc-clcache - - name: Set up Python ${{ matrix.python-version }} + key: ${{ runner.os }}-msvc-clcache-${{ steps.get-timestamp.outputs.timestamp }} + restore-keys: ${{ runner.os }}-msvc-clcache + - name: Install Conan + run: | + choco install conan + choco install ninja + echo "C:\\Program Files\\conan\\conan" >> $GITHUB_PATH + echo "C:\\ProgramData\\chocolatey\\lib\\ninja\\tools" >> $GITHUB_PATH + shell: bash + - name: Configure Conan + run: | + conan config install src/conan/ + ./scripts/setup-conan-profiles.sh + shell: bash + - name: Set up Python uses: actions/setup-python@v5 with: python-version: 3.8 @@ -264,12 +296,17 @@ jobs: CCACHE_DIR: ${{ github.workspace }}\ccache steps: - uses: actions/checkout@v1 + - name: prepare timestamp + id: get-timestamp + run: echo "timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT + shell: bash - name: ccache cache files uses: actions/cache@v4 with: - path: ${{ github.workspace }}/ccache - key: ${{ runner.os }}-mingw-ccache - - name: Set up Python ${{ matrix.python-version }} + path: ~/.ccache + key: ${{ runner.os }}-mingw-ccache-${{ steps.get-timestamp.outputs.timestamp }} + restore-keys: ${{ runner.os }}-mingw-ccache + - name: Set up Python uses: actions/setup-python@v5 with: python-version: 3.8 @@ -678,7 +715,7 @@ jobs: target: 'desktop', toolchain: 'clang_64', xcodeVersion: '', - testProfile: 'xcode_13_4_1-macosx-x86_64', + testProfile: 'clang', qtVersion: '6.5.0', script: './scripts/test-qbs.sh', } @@ -695,6 +732,20 @@ jobs: run: echo "./release/install-root/usr/local/bin" >> $GITHUB_PATH - name: Install required packages run: brew install capnp ccache grpc icoutils makensis protobuf p7zip + - name: Set up Python + uses: actions/setup-python@v5 + if: matrix.config.toolchain == 'clang_64' + with: + python-version: 3.12 + - name: Install Conan + if: matrix.config.toolchain == 'clang_64' + uses: turtlebrowser/get-conan@main + - name: Setup Conan + if: matrix.config.toolchain == 'clang_64' + run: | + brew install ninja + conan config install src/conan/ + ./scripts/setup-conan-profiles.sh - name: Install Host Qt if: matrix.config.toolchain == 'ios' uses: ./.github/actions/download-qt @@ -810,6 +861,26 @@ jobs: version: ${{ matrix.config.qtVersion }} - name: Install MinGW uses: ./.github/actions/download-mingw + - name: Install Ninja + if: matrix.config.toolchain == 'win64_msvc2019_64' + run: | + choco install ninja + echo "C:\\ProgramData\\chocolatey\\lib\\ninja\\tools" >> $GITHUB_PATH + shell: bash + - name: Install Conan + if: matrix.config.toolchain == 'win64_msvc2019_64' + run: | + choco install conan + choco install ninja + echo "C:\\Program Files\\conan\\conan" >> $GITHUB_PATH + echo "C:\\ProgramData\\chocolatey\\lib\\ninja\\tools" >> $GITHUB_PATH + shell: bash + - name: Configure Conan + if: matrix.config.toolchain == 'win64_msvc2019_64' + run: | + conan config install src/conan/ + ./scripts/setup-conan-profiles.sh + shell: bash - name: Setup Qbs run: | qbs setup-toolchains --detect diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1b65762e7..168518385 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,13 +35,16 @@ jobs: - uses: actions/checkout@v1 - name: Create .ccache dir run: mkdir -p ~/.ccache - - name: test - run: echo ${{ github.ref }} + - name: prepare timestamp + id: get-timestamp + run: echo "timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT + shell: bash - name: ccache cache files uses: actions/cache@v4 with: path: ~/.ccache - key: ${{ runner.os }}-${{ matrix.config.cacheid }}-ccache + key: ${{ runner.os }}-${{ matrix.config.cacheid }}-ccache-${{ steps.get-timestamp.outputs.timestamp }} + restore-keys: ${{ runner.os }}-${{ matrix.config.cacheid }}-ccache - name: Pull the Docker Image run: docker-compose pull jammy-qt6 - name: Print ccache stats @@ -77,11 +80,16 @@ jobs: - uses: actions/checkout@v1 - name: Create .ccache dir run: mkdir -p ~/.ccache + - name: prepare timestamp + id: get-timestamp + run: echo "timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT + shell: bash - name: ccache cache files uses: actions/cache@v4 with: path: ~/.ccache - key: ${{ runner.os }}-release-ccache + key: ${{ runner.os }}-release-ccache-${{ steps.get-timestamp.outputs.timestamp }} + restore-keys: ${{ runner.os }}-release-ccache - name: Install required packages run: | brew install ccache p7zip @@ -124,11 +132,16 @@ jobs: - name: Create .ccache dir run: mkdir -p ~/.ccache shell: bash + - name: prepare timestamp + id: get-timestamp + run: echo "timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")" >> $GITHUB_OUTPUT + shell: bash - name: clcache cache files uses: actions/cache@v4 with: - path: ~/.ccache - key: ${{ runner.os }}-release-msvc-docker-clcache + path: ~/clcache + key: ${{ runner.os }}-release-msvc-docker-clcache-${{ steps.get-timestamp.outputs.timestamp }} + restore-keys: ${{ runner.os }}-release-msvc-docker-clcache - name: Pull the Windows Image run: docker-compose pull windows - name: Print clcache stats @@ -1 +1 @@ -2.4.0 +2.5.0 diff --git a/changelogs/changes-2.3.0.md b/changelogs/changes-2.3.0.md index 9062b27ff..364248ee1 100644 --- a/changelogs/changes-2.3.0.md +++ b/changelogs/changes-2.3.0.md @@ -17,7 +17,7 @@ * Module properties are now accessible for groups in modules (QBS-1770). * Fixed pathList properties in Probes (QBS-1785). * The qbspkgconfig.mergeDependencies property was removed. -* ModuleProviders now support the 'allowedValues' property of the PropertyDeclaration item +* ModuleProviders now support the 'allowedValues' property of the PropertyOptions item (QBS-1748). # Apple diff --git a/changelogs/changes-2.3.1.md b/changelogs/changes-2.3.1.md index 6c4b6c0ad..df079ada6 100644 --- a/changelogs/changes-2.3.1.md +++ b/changelogs/changes-2.3.1.md @@ -2,7 +2,7 @@ * Fixed look-up of qbs properties in module providers via probes (QBS-1742). # Apple support -* Fixed codesing module when multiplexing over build variants (QBS-1775). +* Fixed codesign module when multiplexing over build variants (QBS-1775). # Qt support * Fixed retrieving minimum macOS/iOS versions for Qt 6.7.1. diff --git a/changelogs/changes-2.4.0.md b/changelogs/changes-2.4.0.md new file mode 100644 index 000000000..862908280 --- /dev/null +++ b/changelogs/changes-2.4.0.md @@ -0,0 +1,20 @@ +# General +* Added a Conan module provider (QBS-1665). +* Added a flatbuffers module (QBS-1666). +* Rules trying to create artifacts outside the build directory is now a hard error (QBS-1268). +* More details are now printed when a command times out (QBS-1750). +* Updated the bundled quickjs library. + +# Language +* The pkg-config based fallback provider was removed. +* It is no longer allowed to attach a QML id to a module item. +* Property evaluation now detects infinite recursion (QBS-1793). + +# Documentation +* Added more tutorials. + +# Contributors +* Christian Kandeler +* Ivan Komissarov +* Kai Dohmen +* Raphael Cotty diff --git a/doc/qbs.qdoc b/doc/qbs.qdoc index cbe33f98a..136c7eb9f 100644 --- a/doc/qbs.qdoc +++ b/doc/qbs.qdoc @@ -72,7 +72,7 @@ \endlist \li \l{Tutorial} \list - \li \l{tutorial-1.html}{Application} + \li \l{tutorial-1.html}{Console Application} \li \l{tutorial-2.html}{Static Library} \li \l{tutorial-3.html}{Dynamic Library} \li \l{tutorial-4.html}{Convenience Items} @@ -1900,8 +1900,80 @@ For instance, the overall set of modules related to a certain task might depend on some information present on the local platform. - \note Module providers are an advanced concept that you will rarely need to use directly. - Reading this section is not required for most people's everyday work. + Use Module Providers to create new modules during the \c resolve stage, for example when + locating external dependencies such as libraries. + + Consider the following example: + + \code + CppApplication { + files: "main.cpp" + Depends { name: "zlib" } + qbsModuleProviders: ["conan", "qbspkgconfig"] + } + \endcode + Here, we want to use the \l{https://www.zlib.net}{zlib} compression library in our application. + Since there is no pre-defined module called \c "zlib", \QBS will try to create it using + Module Providers. \QBS will invoke all providers specified in the + \l{Product::qbsModuleProviders}{qbsModuleProviders} property and those providers will create + the requested module if possible. Providers contribute to the \l{Product::}{qbsSearchPaths} + in the order specified by this property, so modules generated by providers specified earlier + are prioritised. In the example above, modules created by the \l conan provider have higher + priority than modules created by \c qbspkgconfig. + + \section1 Lookup Based on Module Name + Originally, the \l{Qt} module provider was a the only provider in \QBS and we wanted to make + its usage transparent to users. Thus, it was implemented by automatically kicking in when \QBS + saw the dependency on the \c Qt module such as "Qt.core". It is also possible to specify the + order of providers explicitly: + \code + CppApplication { + files: "main.cpp" + Depends { name: "Qt.core" } + qbsModuleProviders: ["Qt", "qbspkgconfig"] + } + \endcode + That way, users have more control over the module priorities. + + Note that setting the \c qbsModuleProviders property disables the lookup based on the + module name entirely. + + \section1 Provider Eagerness + Historically, providers were implemented in an \e eager way meaning that once a provider is + called, it creates as many modules as it can. For example, when called, the \l{qbspkgconfig} + provider created a module for each .pc file found in the system. Even though providers are + quite fast, this violates the zero-overhead principle (you don't pay for what you don't use). + Thus, we introduced non-eager providers which only create one module at a time when that + module is requested by the \l{Depends} item. This behavior is controlled by the + \l{ModuleProvider::isEager}{isEager} property. We advise against using eager providers in + new code. + + \section1 Selecting Module Providers + + As described above, you can select which providers to run using the + \l{Product::qbsModuleProviders}{qbsModuleProviders} property. This property can be set on the + Product as well as the \l{Project::qbsModuleProviders}{Project} level: + + \code + $ qbs resolve project.qbsModuleProviders:providerA \ # sets property globally for the Project + projects.SomeProject.qbsModuleProviders:providerB \ # overrides property for the specific Project + products.SomeProduct.qbsModuleProviders:providerC \ # overrides property for the specific Product + \endcode + + \section1 Parameterizing Module Providers + + You can pass information to module providers from the command line, via profiles or + from within a product, in a similar way as you would do for modules. For instance, the + following invocation of \QBS passes information to two module providers \c a and \c b: + \code + $ qbs moduleProviders.a.p1:true moduleProviders.a.p2:true moduleProviders.b.p:false + \endcode + \QBS will set the properties of the respective module providers accordingly. + In the above example, module provider \c a needs to declare two boolean properties \c p1 + and \c p2, and they will be set to \c true and \c false, respectively. + + \note The following section contains some implementation details. Reading this section is not + required for most people's everyday work. \section1 How \QBS Uses Module Providers @@ -1929,32 +2001,6 @@ of whether it created any modules or not. \endlist - \section1 Selecting Module Providers - - As described above, it is possible to select which providers to run using the - \l{Product::qbsModuleProviders}{qbsModuleProviders} property. Providers contribute to the - \l{Product::}{qbsSearchPaths} in the order specified by this property, so modules generated - by providers specified earlier are prioritized. This property can be set on the - Product as well as the \l{Project::qbsModuleProviders}{Project} level: - - \code - $ qbs resolve project.qbsModuleProviders:providerA \ # sets property globally for the Project - projects.SomeProject.qbsModuleProviders:providerB \ # overrides property for the specific Project - products.SomeProduct.qbsModuleProviders:providerC \ # overrides property for the specific Product - \endcode - - \section1 Parameterizing Module Providers - - You can pass information to module providers from the command line, via profiles or - from within a product, in a similar way as you would do for modules. For instance, the - following invocation of \QBS passes information to two module providers \c a and \c b: - \code - $ qbs moduleProviders.a.p1:true moduleProviders.a.p2:true moduleProviders.b.p:false - \endcode - \QBS will set the properties of the respective module providers accordingly. - In the above example, module provider \c a needs to declare two boolean properties \c p1 - and \c p2, and they will be set to \c true and \c false, respectively. - */ /*! diff --git a/doc/reference/items/probe/conanfile-probe.qdoc b/doc/reference/items/probe/conanfile-probe.qdoc index cc91b9237..f1b30f0c3 100644 --- a/doc/reference/items/probe/conanfile-probe.qdoc +++ b/doc/reference/items/probe/conanfile-probe.qdoc @@ -43,6 +43,9 @@ properties in products. \c ConanfileProbe can also be used to run other Conan generators and to retrieve their output. + \note This probe only works with Conan 1.x since the \c json generator was removed in Conan + 2.0. Use the \l{conan}{conan} module provider and \c QbsDeps generator with Conan 2.0 instead. + \section1 Examples In the following examples we assume that our project contains a \c conanfile.py. diff --git a/doc/reference/module-providers/conan-module-provider.qdoc b/doc/reference/module-providers/conan-module-provider.qdoc new file mode 100644 index 000000000..4edba94d8 --- /dev/null +++ b/doc/reference/module-providers/conan-module-provider.qdoc @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com) +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \qmltype conan + \inqmlmodule QbsModuleProviders + \since 2.4 + + \brief Module provider for the Conan package manager. + + This module provider allows integration with the \l{https://conan.io}{Conan} package manager. + + \section1 Prerequisites + In order to use this provider, you will need to install the + \l{https://github.com/qbs/qbs/blob/master/src/conan/extensions/generators/qbsdeps.py}{QbsDeps generator} + first. In order to do so, clone the \QBS repository and run the following command from the \QBS + source directory: + \code + $ conan config install src/conan/ + \endcode + This will copy the generator to \c .conan2/extensions/generators/qbsdeps.py in the user's home + directory. + Alternatively, you can use \c curl to download the file: + \code + $ curl 'https://github.com/qbs/qbs/raw/master/src/conan/extensions/generators/qbsdeps.py' -o ~/.conan2/extensions/generators/qbsdeps.py + \endcode + + \section1 Example + For details on how to setup a project to use with Conan, see the \ + l{https://github.com/qbs/qbs/blob/master/examples/protobuf/addressbook_conan}{addressbook_conan} + folder in examples. + First, you will need a \l{https://docs.conan.io/2/reference/conanfile_txt.html}{conanfile} as + shown below. + \code + [requires] + protobuf/3.21.12 + [tool_requires] + protobuf/3.21.12 + [generators] + QbsDeps + \endcode + We use the text version for simplicity, but you can use the Python conanfile as well. + + Next, set the \l{Product::qbsModuleProviders}{qbsModuleProviders} property to \c "conan": + \snippet ../examples/protobuf/addressbook_conan/addressbook_conan.qbs 0 + + Install Conan dependencies and run the QbsDeps generator from the \c addressbook_conan dir: + \code + $ conan install . -g=QbsDeps --output-folder=build --build missing + \endcode + This will create the \c{./build/qbs-deps} directory contaning files for provider. Now you can + pass the conan install directory to the provider: + \code + $ qbs moduleProviders.conan.installDirectory:build + \endcode + You should see the following output if everything is correct: + \code + Build graph does not yet exist for configuration 'default'. Starting from scratch. + Resolving project for configuration default + Setting up Conan module 'protobuflib' + Setting up Conan module 'zlib' + ... + Build done for configuration default. + \endcode +*/ + +/*! + \qmlproperty string conan::installDirectory + + The path to the conan install installDirectory. + + \QBS searches for files created by the QbsDeps generator in that directory. + + If not set, the provider will not be run. + + \defaultvalue undefined +*/ diff --git a/doc/reference/modules/flatbuf-c-module.qdoc b/doc/reference/modules/flatbuf-c-module.qdoc new file mode 100644 index 000000000..d486ab3bf --- /dev/null +++ b/doc/reference/modules/flatbuf-c-module.qdoc @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com) +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \qmltype flatbuf.c + \inqmlmodule QbsModules + \since Qbs 2.4 + + \brief Provides support for \l{https://google.github.io/flatbuffers/}{FlatBuffers} for the + C language. + + The \c flatbuf.c module provides support for generating C headers + and sources from flatbuffers definition files using the \c flatcc tool. + + \section2 Relevant File Tags + + \table + \header + \li Tag + \li Auto-tagged File Names + \li Since + \li Description + \row + \li \c{"flatbuffers.input"} + \li \c{*.fbs} + \li 2.4.0 + \li Source files with this tag are considered inputs to the \c flatcc compiler. + \endtable + + \section2 Dependencies + This module depends on the \c flatcc module which can be created via the \l{conan}{Conan} + module provider. +*/ + +/*! + \qmlproperty string flatbuf.c::compilerName + + The name of the \c flatcc binary. + + \defaultvalue \c "flatcc" +*/ + +/*! + \qmlproperty string flatbuf.c::compilerPath + + The path to the \c flatcc binary. + + Use this property to override the auto-detected location. + + \defaultvalue \c auto-detected +*/ + +/*! + \qmlproperty pathList flatbuf.c::importPaths + + The list of import paths that are passed to the \c flatcc tool via the \c -I option. + + \defaultvalue \c [] +*/ diff --git a/doc/reference/modules/flatbuf-cpp-module.qdoc b/doc/reference/modules/flatbuf-cpp-module.qdoc new file mode 100644 index 000000000..a4b6915ab --- /dev/null +++ b/doc/reference/modules/flatbuf-cpp-module.qdoc @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com) +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \qmltype flatbuf.cpp + \inqmlmodule QbsModules + \since Qbs 2.4 + + \brief Provides support for \l{https://google.github.io/flatbuf/}{flatbuf} for the + C++ language. + + The \c flatbuf.cpp module provides support for generating C++ headers + and sources from flatbuf definition files using the \c flatc tool. + + A simple qbs file that uses flatbuf can be written as follows: + \code + CppApplication { + Depends { name: "flatbuf.cpp" } + files: ["foo.fbs", "main.cpp"] + } + \endcode + A generated header now can be included in the C++ sources: + \code + #include <foo_generated.h> + + int main(int argc, char* argv[]) { + flatbuf::FlatBufferBuilder builder; + + auto foo = QbsTest::CreateFoo(builder, 42); + return 0; + } + \endcode + + \section2 Relevant File Tags + + \table + \header + \li Tag + \li Auto-tagged File Names + \li Since + \li Description + \row + \li \c{"flatbuf.input"} + \li \c{*.fbs} + \li 2.4.0 + \li Source files with this tag are considered inputs to the \c flatc compiler. + \endtable + + \section2 Dependencies + This module depends on the \c flatbuffers module which can be created via the \l{conan}{Conan} + module provider. +*/ + +/*! + \qmlproperty string flatbuf.cpp::compilerName + + The name of the \c flatc binary. + + \defaultvalue \c "flatc" +*/ + +/*! + \qmlproperty string flatbuf.cpp::compilerPath + + The path to the \c flatc binary. + + Use this property to override the auto-detected location. + + \defaultvalue \c auto-detected +*/ + +/*! + \qmlproperty string flatbuf.cpp::filenameExtension + + The extension appended to the generated file names. If not specified, the default extension + (\c ".h") is used. + + This property corresponds to the \c --filename-ext option of the \c flatc tool. + + \nodefaultvalue +*/ + +/*! + \qmlproperty string flatbuf.cpp::filenameSuffix + + The suffix appended to the generated file names. If not specified, the default suffix + (\c "_generated") is used. + + This property corresponds to the \c --filename-suffix option of the \c flatc tool. + + \nodefaultvalue +*/ + +/*! + \qmlproperty pathList flatbuf.cpp::importPaths + + The list of import paths that are passed to the \c flatc tool via the \c -I option. + + \defaultvalue \c [] +*/ + +/*! + \qmlproperty string flatbuf.cpp::includePrefix + + Prefix path prepended to any generated include statements. + + This property corresponds to the \c --include-prefix option of the \c flatc tool. + + \nodefaultvalue +*/ + +/*! + \qmlproperty bool flatbuf.cpp::keepPrefix + + Whether to keep original prefix of schema include statement. + + This property corresponds to the \c --keep-prefix option of the \c flatc tool. + + \defaultvalue \c false +*/ diff --git a/doc/reference/modules/qt-core-module.qdoc b/doc/reference/modules/qt-core-module.qdoc index 6aedf2a69..85d1c66c6 100644 --- a/doc/reference/modules/qt-core-module.qdoc +++ b/doc/reference/modules/qt-core-module.qdoc @@ -174,6 +174,14 @@ */ /*! + \qmlproperty stringList Qt.core::disabledFeatures + + Corresponds to cmake's \c QT_DISABLED_PUBLIC_FEATURES variable. + + \readonly +*/ + +/*! \qmlproperty path Qt.core::docPath The path in which the Qt documentation is located. @@ -182,6 +190,14 @@ */ /*! + \qmlproperty stringList Qt.core::enabledFeatures + + Corresponds to cmake's \c QT_ENABLED_PUBLIC_FEATURES variable. + + \readonly +*/ + +/*! \qmlproperty bool Qt.core::enableKeywords Set this property to \c false if you do not want Qt to define the symbols diff --git a/doc/tutorial.qdoc b/doc/tutorial.qdoc index 7d336b5b3..b17cf4faf 100644 --- a/doc/tutorial.qdoc +++ b/doc/tutorial.qdoc @@ -36,7 +36,7 @@ concepts. \list - \li \l{tutorial-1.html}{Application} + \li \l{tutorial-1.html}{Console Application} \li \l{tutorial-2.html}{Static Library} \li \l{tutorial-3.html}{Dynamic Library} \li \l{tutorial-4.html}{Convenience Items} @@ -53,7 +53,7 @@ \page tutorial-1.html \nextpage tutorial-2.html - \title Application + \title Console Application Let's start with a mandatory Hello World example. There is a Hello World example in the \l{overview.html#well-defined-language}{overview section}, but this time we will create a diff --git a/examples/flatbuffers/.clang-format b/examples/flatbuffers/.clang-format new file mode 100644 index 000000000..47a38a93f --- /dev/null +++ b/examples/flatbuffers/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: Never diff --git a/examples/flatbuffers/monster-c/conanfile.txt b/examples/flatbuffers/monster-c/conanfile.txt new file mode 100644 index 000000000..613119ec2 --- /dev/null +++ b/examples/flatbuffers/monster-c/conanfile.txt @@ -0,0 +1,6 @@ +[requires] +flatcc/0.6.1 +[tool_requires] +flatcc/0.6.1 +[generators] +QbsDeps diff --git a/examples/flatbuffers/monster-c/monster-c.qbs b/examples/flatbuffers/monster-c/monster-c.qbs new file mode 100644 index 000000000..e6abe749e --- /dev/null +++ b/examples/flatbuffers/monster-c/monster-c.qbs @@ -0,0 +1,12 @@ +CppApplication { + Depends { name: "flatbuf.c"; required: false } + + name: "monster_c" + consoleApplication: true + condition: flatbuf.c.present && qbs.targetPlatform === qbs.hostPlatform + + files: [ + "monster.c", + "../shared/monster.fbs" + ] +} diff --git a/examples/flatbuffers/monster-c/monster.c b/examples/flatbuffers/monster-c/monster.c new file mode 100644 index 000000000..6bf49d628 --- /dev/null +++ b/examples/flatbuffers/monster-c/monster.c @@ -0,0 +1,367 @@ +/* + * Copyright 2019 dvidelabs. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Example on how to build a Monster FlatBuffer. + + +// Note: while some older C89 compilers are supported when +// -DFLATCC_PORTABLE is defined, this particular sample is known not to +// not work with MSVC 2010 (MSVC 2013 is OK) due to inline variable +// declarations. These are easily move to the start of code blocks, but +// since we follow the step-wise tutorial, it isn't really practical +// in this case. The comment style is technically also in violation of C89. + + +#include "monster_builder.h" +// <string.h> and <assert.h> already included. + +// Convenient namespace macro to manage long namespace prefix. +// The ns macro makes it possible to write `ns(Monster_create(...))` +// instead of `MyGame_Sample_Monster_create(...)` +#undef ns +#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Sample, x) // Specified in the schema. + +// A helper to simplify creating vectors from C-arrays. +#define c_vec_len(V) (sizeof(V)/sizeof((V)[0])) + +// This allows us to verify result in optimized builds. +#define test_assert(x) do { if (!(x)) { assert(0); return -1; }} while (0) + +// Bottom-up approach where we create child objects and store these +// in temporary references before a parent object is created with +// these references. +int create_monster_bottom_up(flatcc_builder_t *B, int flexible) +{ + flatbuffers_string_ref_t weapon_one_name = flatbuffers_string_create_str(B, "Sword"); + uint16_t weapon_one_damage = 3; + + flatbuffers_string_ref_t weapon_two_name = flatbuffers_string_create_str(B, "Axe"); + uint16_t weapon_two_damage = 5; + + // Use the `MyGame_Sample_Weapon_create` shortcut to create Weapons + // with all the fields set. + // + // In the C-API, verbs (here create) always follow the type name + // (here Weapon), prefixed by the namespace (here MyGame_Sample_): + // MyGame_Sample_Weapon_create(...), or ns(Weapone_create(...)). + ns(Weapon_ref_t) sword = ns(Weapon_create(B, weapon_one_name, weapon_one_damage)); + ns(Weapon_ref_t) axe = ns(Weapon_create(B, weapon_two_name, weapon_two_damage)); + + // Serialize a name for our monster, called "Orc". + // The _str suffix indicates the source is an ascii-z string. + flatbuffers_string_ref_t name = flatbuffers_string_create_str(B, "Orc"); + + // Create a `vector` representing the inventory of the Orc. Each number + // could correspond to an item that can be claimed after he is slain. + uint8_t treasure[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + flatbuffers_uint8_vec_ref_t inventory; + // `c_vec_len` is the convenience macro we defined earlier. + inventory = flatbuffers_uint8_vec_create(B, treasure, c_vec_len(treasure)); + + // Here we use a top-down approach locally to build a Weapons vector + // in-place instead of creating a temporary external vector to use + // as argument like we did with the `inventory` earlier on, but the + // overall approach is still bottom-up. + ns(Weapon_vec_start(B)); + ns(Weapon_vec_push(B, sword)); + ns(Weapon_vec_push(B, axe)); + ns(Weapon_vec_ref_t) weapons = ns(Weapon_vec_end(B)); + + + // Create a `Vec3`, representing the Orc's position in 3-D space. + ns(Vec3_t) pos = { 1.0f, 2.0f, 3.0f }; + + + // Set his hit points to 300 and his mana to 150. + uint16_t hp = 300; + // The default value is 150, so we will never store this field. + uint16_t mana = 150; + + // Create the equipment union. In the C++ language API this is given + // as two arguments to the create call, or as two separate add + // operations for the type and the table reference. Here we create + // a single union value that carries both the type and reference. + ns(Equipment_union_ref_t) equipped = ns(Equipment_as_Weapon(axe)); + + if (!flexible) { + // Finally, create the monster using the `Monster_create` helper function + // to set all fields. + // + // Note that the Equipment union only take up one argument in C, where + // C++ takes a type and an object argument. + ns(Monster_create_as_root(B, &pos, mana, hp, name, inventory, ns(Color_Red), + weapons, equipped)); + + // Unlike C++ we do not use a Finish call. Instead we use the + // `create_as_root` action which has better type safety and + // simplicity. + // + // However, we can also express this as: + // + // ns(Monster_ref_t) orc = ns(Monster_create(B, ...)); + // flatcc_builder_buffer_create(orc); + // + // In this approach the function should return the orc and + // let a calling function handle the flatcc_buffer_create call + // for a more composable setup that is also able to create child + // monsters. In general, `flatcc_builder` calls are best isolated + // in a containing driver function. + + } else { + + // A more flexible approach where we mix bottom-up and top-down + // style. We still create child objects first, but then create + // a top-down style monster object that we can manipulate in more + // detail. + + // It is important to pair `start_as_root` with `end_as_root`. + ns(Monster_start_as_root(B)); + ns(Monster_pos_create(B, 1.0f, 2.0f, 3.0f)); + // or alternatively + //ns(Monster_pos_add(&pos); + + ns(Monster_hp_add(B, hp)); + // Notice that `Monser_name_add` adds a string reference unlike the + // add_str and add_strn variants. + ns(Monster_name_add(B, name)); + ns(Monster_inventory_add(B, inventory)); + ns(Monster_color_add(B, ns(Color_Red))); + ns(Monster_weapons_add(B, weapons)); + ns(Monster_equipped_add(B, equipped)); + // Complete the monster object and make it the buffer root object. + ns(Monster_end_as_root(B)); + + // We could also drop the `as_root` suffix from Monster_start/end(B) + // and add the table as buffer root later: + // + // ns(Monster_ref_t) orc = ns(Monster_start(B)); + // ... + // ns(Monster_ref_t) orc = ns(Monster_end(B)); + // flatcc_builder_buffer_create(orc); + // + // It is best to keep the `flatcc_builder` calls in a containing + // driver function for modularity. + } + return 0; +} + +// Alternative top-down approach where parent objects are created before +// their children. We only need to save one reference because the `axe` +// object is used in two places effectively making the buffer object +// graph a DAG. +int create_monster_top_down(flatcc_builder_t *B) +{ + uint8_t treasure[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + size_t treasure_count = c_vec_len(treasure); + ns(Weapon_ref_t) axe; + + // NOTE: if we use end_as_root, we MUST also start as root. + ns(Monster_start_as_root(B)); + ns(Monster_pos_create(B, 1.0f, 2.0f, 3.0f)); + ns(Monster_hp_add(B, 300)); + //ns(Monster_mana_add(B, 150)); + // We use create_str instead of add because we have no existing string reference. + ns(Monster_name_create_str(B, "Orc")); + // Again we use create because we no existing vector object, only a C-array. + ns(Monster_inventory_create(B, treasure, treasure_count)); + ns(Monster_color_add(B, ns(Color_Red))); + if (1) { + ns(Monster_weapons_start(B)); + ns(Monster_weapons_push_create(B, flatbuffers_string_create_str(B, "Sword"), 3)); + // We reuse the axe object later. Note that we dereference a pointer + // because push always returns a short-term pointer to the stored element. + // We could also have created the axe object first and simply pushed it. + axe = *ns(Monster_weapons_push_create(B, flatbuffers_string_create_str(B, "Axe"), 5)); + ns(Monster_weapons_end(B)); + } else { + // We can have more control with the table elements added to a vector: + // + ns(Monster_weapons_start(B)); + ns(Monster_weapons_push_start(B)); + ns(Weapon_name_create_str(B, "Sword")); + ns(Weapon_damage_add(B, 3)); + ns(Monster_weapons_push_end(B)); + ns(Monster_weapons_push_start(B)); + ns(Weapon_name_create_str(B, "Axe")); + ns(Weapon_damage_add(B, 5)); + axe = *ns(Monster_weapons_push_end(B)); + ns(Monster_weapons_end(B)); + } + // Unions can get their type by using a type-specific add/create/start method. + ns(Monster_equipped_Weapon_add(B, axe)); + + ns(Monster_end_as_root(B)); + return 0; +} + +// This isn't strictly needed because the builder already included the reader, +// but we would need it if our reader were in a separate file. +#include "monster_reader.h" + +#undef ns +#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(MyGame_Sample, x) // Specified in the schema. + +int access_monster_buffer(const void *buffer) +{ + // Note that we use the `table_t` suffix when reading a table object + // as opposed to the `ref_t` suffix used during the construction of + // the buffer. + ns(Monster_table_t) monster = ns(Monster_as_root(buffer)); + + // Note: root object pointers are NOT the same as the `buffer` pointer. + + // Make sure the buffer is accessible. + test_assert(monster != 0); + + uint16_t hp = ns(Monster_hp(monster)); + uint16_t mana = ns(Monster_mana(monster)); + // This is just a const char *, but it also supports a fast length operation. + flatbuffers_string_t name = ns(Monster_name(monster)); + size_t name_len = flatbuffers_string_len(name); + + test_assert(hp == 300); + // Since 150 is the default, we are reading a value that wasn't stored. + test_assert(mana == 150); + test_assert(0 == strcmp(name, "Orc")); + test_assert(name_len == strlen("Orc")); + + int hp_present = ns(Monster_hp_is_present(monster)); // 1 + int mana_present = ns(Monster_mana_is_present(monster)); // 0 + test_assert(hp_present); + test_assert(!mana_present); + + ns(Vec3_struct_t) pos = ns(Monster_pos(monster)); + // Make sure pos has been set. + test_assert(pos != 0); + float x = ns(Vec3_x(pos)); + float y = ns(Vec3_y(pos)); + float z = ns(Vec3_z(pos)); + + // The literal `f` suffix is important because double literals does + // not always map cleanly to 32-bit represention even with only a few digits: + // `1.0 == 1.0f`, but `3.2 != 3.2f`. + test_assert(x == 1.0f); + test_assert(y == 2.0f); + test_assert(z == 3.0f); + + // We can also read the position into a C-struct. We have to copy + // because we generally do not know if the native endian format + // matches the one stored in the buffer (pe: protocol endian). + ns(Vec3_t) pos_vec; + // `pe` indicates endian conversion from protocol to native. + ns(Vec3_copy_from_pe(&pos_vec, pos)); + test_assert(pos_vec.x == 1.0f); + test_assert(pos_vec.y == 2.0f); + test_assert(pos_vec.z == 3.0f); + + // This is a const uint8_t *, but it shouldn't be accessed directly + // to ensure proper endian conversion. However, uint8 (ubyte) are + // not sensitive endianness, so we *could* have accessed it directly. + // The compiler likely optimizes this so that it doesn't matter. + flatbuffers_uint8_vec_t inv = ns(Monster_inventory(monster)); + size_t inv_len = flatbuffers_uint8_vec_len(inv); + // Make sure the inventory has been set. + test_assert(inv != 0); + // If `inv` were absent, the length would 0, so the above test is redundant. + test_assert(inv_len == 10); + // Index 0 is the first, index 2 is the third. + // NOTE: C++ uses the `Get` terminology for vector elemetns, C use `at`. + uint8_t third_item = flatbuffers_uint8_vec_at(inv, 2); + test_assert(third_item == 2); + + ns(Weapon_vec_t) weapons = ns(Monster_weapons(monster)); + size_t weapons_len = ns(Weapon_vec_len(weapons)); + test_assert(weapons_len == 2); + // We can use `const char *` instead of `flatbuffers_string_t`. + const char *second_weapon_name = ns(Weapon_name(ns(Weapon_vec_at(weapons, 1)))); + uint16_t second_weapon_damage = ns(Weapon_damage(ns(Weapon_vec_at(weapons, 1)))); + test_assert(second_weapon_name != 0 && strcmp(second_weapon_name, "Axe") == 0); + test_assert(second_weapon_damage == 5); + + // Access union type field. + if (ns(Monster_equipped_type(monster)) == ns(Equipment_Weapon)) { + // Cast to appropriate type: + // C does not require the cast to Weapon_table_t, but C++ does. + ns(Weapon_table_t) weapon = (ns(Weapon_table_t)) ns(Monster_equipped(monster)); + const char *weapon_name = ns(Weapon_name(weapon)); + uint16_t weapon_damage = ns(Weapon_damage(weapon)); + + test_assert(0 == strcmp(weapon_name, "Axe")); + test_assert(weapon_damage == 5); + } + return 0; +} + +#include <stdio.h> + +int main(int argc, char *argv[]) +{ + // Create a `FlatBufferBuilder`, which will be used to create our + // monsters' FlatBuffers. + flatcc_builder_t builder; + void *buf; + size_t size; + + // Silence warnings. + (void)argc; + (void)argv; + + // Initialize the builder object. + flatcc_builder_init(&builder); + test_assert(0 == create_monster_bottom_up(&builder, 0)); + + // Allocate and extract a readable buffer from internal builder heap. + // The returned buffer must be deallocated using `free`. + // NOTE: Finalizing the buffer does NOT change the builder, it + // just creates a snapshot of the builder content. + // NOTE2: finalize_buffer uses malloc while finalize_aligned_buffer + // uses a portable aligned allocation method. Often the malloc + // version is sufficient, but won't work for all schema on all + // systems. If the buffer is written to disk or network, but not + // accessed in memory, `finalize_buffer` is also sufficient. + buf = flatcc_builder_finalize_aligned_buffer(&builder, &size); + //buf = flatcc_builder_finalize_buffer(&builder, &size); + + // We now have a FlatBuffer we can store on disk or send over a network. + // ** file/network code goes here :) ** + // Instead, we're going to access it right away (as if we just received it). + //access_monster_buffer(buf); + + // prior to v0.5.0, use `aligned_free` + flatcc_builder_aligned_free(buf); + //free(buf); + // + // The builder object can optionally be reused after a reset which + // is faster than creating a new builder. Subsequent use might + // entirely avoid temporary allocations until finalizing the buffer. + flatcc_builder_reset(&builder); + test_assert(0 == create_monster_bottom_up(&builder, 1)); + buf = flatcc_builder_finalize_aligned_buffer(&builder, &size); + access_monster_buffer(buf); + flatcc_builder_aligned_free(buf); + flatcc_builder_reset(&builder); + create_monster_top_down(&builder); + buf = flatcc_builder_finalize_buffer(&builder, &size); + test_assert(0 == access_monster_buffer(buf)); + free(buf); + // Eventually the builder must be cleaned up: + flatcc_builder_clear(&builder); + + printf("The FlatBuffer was successfully created and accessed!\n"); + + return 0; +} + diff --git a/examples/flatbuffers/monster-cpp/conanfile.txt b/examples/flatbuffers/monster-cpp/conanfile.txt new file mode 100644 index 000000000..188da5897 --- /dev/null +++ b/examples/flatbuffers/monster-cpp/conanfile.txt @@ -0,0 +1,6 @@ +[requires] +flatbuffers/24.3.25 +[tool_requires] +flatbuffers/24.3.25 +[generators] +QbsDeps diff --git a/examples/flatbuffers/monster-cpp/monster-cpp.qbs b/examples/flatbuffers/monster-cpp/monster-cpp.qbs new file mode 100644 index 000000000..fdcc3fcfd --- /dev/null +++ b/examples/flatbuffers/monster-cpp/monster-cpp.qbs @@ -0,0 +1,12 @@ +CppApplication { + Depends { name: "flatbuf.cpp"; required: false } + + name: "monster_cpp" + consoleApplication: true + condition: flatbuf.cpp.present && qbs.targetPlatform === qbs.hostPlatform + + files: [ + "monster.cpp", + "../shared/monster.fbs" + ] +} diff --git a/examples/flatbuffers/monster-cpp/monster.cpp b/examples/flatbuffers/monster-cpp/monster.cpp new file mode 100644 index 000000000..d2348c820 --- /dev/null +++ b/examples/flatbuffers/monster-cpp/monster.cpp @@ -0,0 +1,104 @@ +/* + * Copyright 2015 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "monster_generated.h" // Already includes "flatbuffers/flatbuffers.h". + +using namespace MyGame::Sample; + +// Example how to use FlatBuffers to create and read binary buffers. + +int main(int /*argc*/, const char * /*argv*/ []) { + // Build up a serialized buffer algorithmically: + flatbuffers::FlatBufferBuilder builder; + + // First, lets serialize some weapons for the Monster: A 'sword' and an 'axe'. + auto weapon_one_name = builder.CreateString("Sword"); + short weapon_one_damage = 3; + + auto weapon_two_name = builder.CreateString("Axe"); + short weapon_two_damage = 5; + + // Use the `CreateWeapon` shortcut to create Weapons with all fields set. + auto sword = CreateWeapon(builder, weapon_one_name, weapon_one_damage); + auto axe = CreateWeapon(builder, weapon_two_name, weapon_two_damage); + + // Create a FlatBuffer's `vector` from the `std::vector`. + std::vector<flatbuffers::Offset<Weapon>> weapons_vector; + weapons_vector.push_back(sword); + weapons_vector.push_back(axe); + auto weapons = builder.CreateVector(weapons_vector); + + // Second, serialize the rest of the objects needed by the Monster. + auto position = Vec3(1.0f, 2.0f, 3.0f); + + auto name = builder.CreateString("MyMonster"); + + unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + auto inventory = builder.CreateVector(inv_data, 10); + + // Shortcut for creating monster with all fields set: + auto orc = CreateMonster(builder, &position, 150, 80, name, inventory, + Color_Red, weapons, Equipment_Weapon, axe.Union()); + + builder.Finish(orc); // Serialize the root of the object. + + // We now have a FlatBuffer we can store on disk or send over a network. + + // ** file/network code goes here :) ** + // access builder.GetBufferPointer() for builder.GetSize() bytes + + // Instead, we're going to access it right away (as if we just received it). + + // Get access to the root: + auto monster = GetMonster(builder.GetBufferPointer()); + + // Get and test some scalar types from the FlatBuffer. + assert(monster->hp() == 80); + assert(monster->mana() == 150); // default + assert(monster->name()->str() == "MyMonster"); + + // Get and test a field of the FlatBuffer's `struct`. + auto pos = monster->pos(); + assert(pos); + assert(pos->z() == 3.0f); + (void)pos; + + // Get a test an element from the `inventory` FlatBuffer's `vector`. + auto inv = monster->inventory(); + assert(inv); + assert(inv->Get(9) == 9); + (void)inv; + + // Get and test the `weapons` FlatBuffers's `vector`. + std::string expected_weapon_names[] = { "Sword", "Axe" }; + short expected_weapon_damages[] = { 3, 5 }; + auto weps = monster->weapons(); + for (unsigned int i = 0; i < weps->size(); i++) { + assert(weps->Get(i)->name()->str() == expected_weapon_names[i]); + assert(weps->Get(i)->damage() == expected_weapon_damages[i]); + } + (void)expected_weapon_names; + (void)expected_weapon_damages; + + // Get and test the `Equipment` union (`equipped` field). + assert(monster->equipped_type() == Equipment_Weapon); + auto equipped = static_cast<const Weapon *>(monster->equipped()); + assert(equipped->name()->str() == "Axe"); + assert(equipped->damage() == 5); + (void)equipped; + + printf("The FlatBuffer was successfully created and verified!\n"); +} diff --git a/examples/flatbuffers/shared/monster.fbs b/examples/flatbuffers/shared/monster.fbs new file mode 100644 index 000000000..247b81771 --- /dev/null +++ b/examples/flatbuffers/shared/monster.fbs @@ -0,0 +1,32 @@ +// Example IDL file for our monster's schema. + +namespace MyGame.Sample; + +enum Color:byte { Red = 0, Green, Blue = 2 } + +union Equipment { Weapon } // Optionally add more tables. + +struct Vec3 { + x:float; + y:float; + z:float; +} + +table Monster { + pos:Vec3; + mana:short = 150; + hp:short = 100; + name:string; + friendly:bool = false (deprecated); + inventory:[ubyte]; + color:Color = Blue; + weapons:[Weapon]; + equipped:Equipment; +} + +table Weapon { + name:string; + damage:short; +} + +root_type Monster; diff --git a/examples/protobuf/addressbook_conan/addressbook_conan.qbs b/examples/protobuf/addressbook_conan/addressbook_conan.qbs new file mode 100644 index 000000000..c496d14bd --- /dev/null +++ b/examples/protobuf/addressbook_conan/addressbook_conan.qbs @@ -0,0 +1,20 @@ +//![0] + +import qbs.Host + +CppApplication { + consoleApplication: true + condition: protobuf.cpp.present && qbs.targetPlatform === Host.platform() + + Depends { name: "cpp" } + cpp.minimumMacosVersion: "11.0" + + Depends { name: "protobuf.cpp"; required: false } + + files: [ + "../shared/addressbook.proto", + "main.cpp", + ] + qbsModuleProviders: "conan" +} +//![0] diff --git a/examples/protobuf/addressbook_conan/conanfile.txt b/examples/protobuf/addressbook_conan/conanfile.txt new file mode 100644 index 000000000..e7d849b1a --- /dev/null +++ b/examples/protobuf/addressbook_conan/conanfile.txt @@ -0,0 +1,6 @@ +[requires] +protobuf/3.21.12 +[tool_requires] +protobuf/3.21.12 +[generators] +QbsDeps diff --git a/examples/protobuf/addressbook_conan/main.cpp b/examples/protobuf/addressbook_conan/main.cpp new file mode 100644 index 000000000..76e1e0d74 --- /dev/null +++ b/examples/protobuf/addressbook_conan/main.cpp @@ -0,0 +1,180 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <ctime> +#include <fstream> +#include <iostream> +#include <string> + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#include <google/protobuf/util/time_util.h> +#pragma GCC diagnostic pop +#else +#include <google/protobuf/util/time_util.h> +#endif // __GNUC__ + +#include "addressbook.pb.h" + +using google::protobuf::util::TimeUtil; + +int printUsage(char *argv0) +{ + std::cerr << "Usage: " << argv0 << "add|list ADDRESS_BOOK_FILE" << std::endl; + return -1; +} + +std::string readString(const std::string &promt) +{ + std::string result; + std::cout << promt; + std::getline(std::cin, result); + return result; +} + +// This function fills in a Person message based on user input. +void promptForAddress(tutorial::Person *person) +{ + std::cout << "Enter person ID number: "; + int id; + std::cin >> id; + person->set_id(id); + std::cin.ignore(256, '\n'); + + *person->mutable_name() = readString("Enter name: "); + + const auto email = readString("Enter email address (blank for none): "); + if (!email.empty()) + person->set_email(email); + + while (true) { + const auto number = readString("Enter a phone number (or leave blank to finish): "); + if (number.empty()) + break; + + tutorial::Person::PhoneNumber *phone_number = person->add_phones(); + phone_number->set_number(number); + + const auto type = readString("Is this a mobile, home, or work phone? "); + if (type == "mobile") + phone_number->set_type(tutorial::Person::MOBILE); + else if (type == "home") + phone_number->set_type(tutorial::Person::HOME); + else if (type == "work") + phone_number->set_type(tutorial::Person::WORK); + else + std::cout << "Unknown phone type. Using default." << std::endl; + } + *person->mutable_last_updated() = TimeUtil::SecondsToTimestamp(time(NULL)); +} + +// Iterates though all people in the AddressBook and prints info about them. +void listPeople(const tutorial::AddressBook &address_book) +{ + for (int i = 0; i < address_book.people_size(); i++) { + const tutorial::Person &person = address_book.people(i); + + std::cout << "Person ID: " << person.id() << std::endl; + std::cout << " Name: " << person.name() << std::endl; + if (!person.email().empty()) { + std::cout << " E-mail address: " << person.email() << std::endl; + } + + for (int j = 0; j < person.phones_size(); j++) { + const tutorial::Person::PhoneNumber &phone_number = person.phones(j); + + switch (phone_number.type()) { + case tutorial::Person::MOBILE: + std::cout << " Mobile phone #: "; + break; + case tutorial::Person::HOME: + std::cout << " Home phone #: "; + break; + case tutorial::Person::WORK: + std::cout << " Work phone #: "; + break; + default: + std::cout << " Unknown phone #: "; + break; + } + std::cout << phone_number.number() << std::endl; + } + if (person.has_last_updated()) { + std::cout << " Updated: " << TimeUtil::ToString(person.last_updated()) << std::endl; + } + } +} + +int main(int argc, char *argv[]) +{ + // Verify that the version of the library that we linked against is + // compatible with the version of the headers we compiled against. + GOOGLE_PROTOBUF_VERIFY_VERSION; + + if (argc != 3) + return printUsage(argv[0]); + + tutorial::AddressBook address_book; + + // Read the existing address book. + std::fstream input(argv[2], std::ios::in | std::ios::binary); + if (!input) { + std::cout << argv[2] << ": File not found." << std::endl; + } else if (!address_book.ParseFromIstream(&input)) { + std::cerr << "Failed to parse address book." << std::endl; + return -1; + } + + const std::string mode(argv[1]); + if (mode == "add") { + // Add an address. + promptForAddress(address_book.add_people()); + + if (!input) + std::cout << "Creating a new file." << std::endl; + + // Write the new address book back to disk. + std::fstream output(argv[2], std::ios::out | std::ios::trunc | std::ios::binary); + if (!address_book.SerializeToOstream(&output)) { + std::cerr << "Failed to write address book." << std::endl; + return -1; + } + } else if (mode == "list") { + listPeople(address_book); + } else { + return printUsage(argv[0]); + } + + // Optional: Delete all global objects allocated by libprotobuf. + google::protobuf::ShutdownProtobufLibrary(); + + return 0; +} diff --git a/qbs-resources/imports/QbsLibrary.qbs b/qbs-resources/imports/QbsLibrary.qbs index f0769dd6c..1538735e4 100644 --- a/qbs-resources/imports/QbsLibrary.qbs +++ b/qbs-resources/imports/QbsLibrary.qbs @@ -5,7 +5,7 @@ QbsProduct { Depends { name: "cpp" } Depends { name: "Exporter.pkgconfig"; condition: generatePkgConfigFile } Depends { name: "Exporter.qbs"; condition: generateQbsModule } - Depends { name: "cpp" } + Depends { name: "span" } property string visibilityType: staticBuild ? "static" : "dynamic" property string headerInstallPrefix: "/include/qbs" @@ -59,6 +59,7 @@ QbsProduct { Export { Depends { name: "cpp" } Depends { name: "Qt"; submodules: ["core"] } + Depends { name: "span" } Properties { condition: exportingProduct.hasExporter diff --git a/scripts/conan-profiles/mac_x64/qbs-test b/scripts/conan-profiles/mac_x64/qbs-test new file mode 100644 index 000000000..6a3d0572f --- /dev/null +++ b/scripts/conan-profiles/mac_x64/qbs-test @@ -0,0 +1,8 @@ +[settings] +arch=x86_64 +build_type=Release +compiler=apple-clang +compiler.cppstd=17 +compiler.libcxx=libc++ +compiler.version=13 +os=Macos diff --git a/scripts/conan-profiles/win_x64/qbs-test b/scripts/conan-profiles/win_x64/qbs-test new file mode 100644 index 000000000..71b8e706f --- /dev/null +++ b/scripts/conan-profiles/win_x64/qbs-test @@ -0,0 +1,9 @@ +[settings] +arch=x86_64 +build_type=Release +compiler=msvc +compiler.cppstd=14 +compiler.runtime=dynamic +compiler.runtime_type=Release +compiler.version=193 +os=Windows diff --git a/scripts/setup-conan-profiles.sh b/scripts/setup-conan-profiles.sh new file mode 100755 index 000000000..d7da6846a --- /dev/null +++ b/scripts/setup-conan-profiles.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +############################################################################# +## +## Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com). +## 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$ +## +############################################################################# +set -eu + +case "$OSTYPE" in + *darwin*) + HOST_OS=mac_x64 + ;; + msys) + HOST_OS=win_x64 + ;; + *) + HOST_OS= + ;; +esac + +if [ -z "${HOST_OS}" ]; then + exit 0 +fi + +echo $HOST_OS + +mkdir -p "${HOME}/.conan2/profiles" +SCRIPT_DIR=$( cd "$(dirname "$0")" ; pwd -P ) +cp ${SCRIPT_DIR}/conan-profiles/${HOST_OS}/* "${HOME}/.conan2/profiles" diff --git a/share/qbs/imports/qbs/PathTools/path-tools.js b/share/qbs/imports/qbs/PathTools/path-tools.js index a857a7139..c928c33e4 100644 --- a/share/qbs/imports/qbs/PathTools/path-tools.js +++ b/share/qbs/imports/qbs/PathTools/path-tools.js @@ -232,3 +232,25 @@ function prependOrSetPath(path, pathList, separator) { return path; return path + separator + pathList; } + +function librarySuffixes(targetOS, types, forImport) { + if (targetOS.includes("windows")) { + if (forImport) + return [".lib"]; + return [].concat(types.includes("shared") ? [".dll"] : []); + } + if (targetOS.includes("darwin")) { + return [] + .concat(types.includes("shared") ? [".dylib"] : []) + .concat(types.includes("static") ? [".a"] : []); + } + return [] + .concat(types.includes("shared") ? [".so"] : []) + .concat(types.includes("static") ? [".a"] : []); +} + +function libraryNameFilter(targetOS) { + if (targetOS.contains("unix")) + return function(name) { return "lib" + name; } + return function(name) { return name; } +} diff --git a/share/qbs/imports/qbs/Probes/LibraryProbe.qbs b/share/qbs/imports/qbs/Probes/LibraryProbe.qbs index 7631eb441..d67a81372 100644 --- a/share/qbs/imports/qbs/Probes/LibraryProbe.qbs +++ b/share/qbs/imports/qbs/Probes/LibraryProbe.qbs @@ -28,15 +28,11 @@ ** ****************************************************************************/ +import qbs.PathTools + PathProbe { property string endianness - nameSuffixes: { - if (qbs.targetOS.contains("windows")) - return [".lib"]; - if (qbs.targetOS.contains("macos")) - return [".dylib", ".a"]; - return [".so", ".a"]; - } + nameSuffixes: PathTools.librarySuffixes(qbs.targetOS, ["shared", "static"], true) platformSearchPaths: { var result = []; if (qbs.targetOS.contains("unix")) { @@ -65,17 +61,7 @@ PathProbe { return result; } - nameFilter: { - if (qbs.targetOS.contains("unix")) { - return function(name) { - return "lib" + name; - } - } else { - return function(name) { - return name; - } - } - } + nameFilter: PathTools.libraryNameFilter(qbs.targetOS) platformEnvironmentPaths: { if (qbs.targetOS.contains("windows")) return [ "PATH" ]; diff --git a/share/qbs/imports/qbs/Probes/qmake-probe.js b/share/qbs/imports/qbs/Probes/qmake-probe.js index 746914e20..c9a569223 100644 --- a/share/qbs/imports/qbs/Probes/qmake-probe.js +++ b/share/qbs/imports/qbs/Probes/qmake-probe.js @@ -120,7 +120,33 @@ function configVariable(configContent, key) { } function configVariableItems(configContent, key) { - return splitNonEmpty(configVariable(configContent, key), ' '); + var list = []; + var configContentLines = configContent.split('\n'); + var regexp = new RegExp("^\\s*" + key + "\\s*([+-]?=)(.*)"); + for (var i = 0; i < configContentLines.length; ++i) { + var line = configContentLines[i]; + var match = regexp.exec(line); + if (!match) + continue; + var op = match[1]; + var lineList = splitNonEmpty(match[2], ' '); + if (op === '=') { + list = lineList; + continue; + } + if (op === '+=') { + list = list.concat(lineList); + continue; + } + if (op === '-=') { + for (var j = 0; j < lineList.length; ++j) { + var idx = list.indexOf(lineList[j]); + if (idx !== -1) + list.splice(idx, 1); + } + } + } + return list; } function msvcCompilerVersionForYear(year) { @@ -271,6 +297,8 @@ function getQtProperties(qmakeFilePath) { || configVariable(qconfigContent, "QT_ARCH") || "x86"; qtProps.configItems = configVariableItems(qconfigContent, "CONFIG"); qtProps.qtConfigItems = configVariableItems(qconfigContent, "QT_CONFIG"); + qtProps.enabledFeatures = configVariableItems(qconfigContent, "QT.global.enabled_features"); + qtProps.disabledFeatures = configVariableItems(qconfigContent, "QT.global.disabled_features"); // retrieve the mkspec if (qtProps.qtMajorVersion >= 5) { diff --git a/share/qbs/module-providers/Qt/setup-qt.js b/share/qbs/module-providers/Qt/setup-qt.js index 9a314822b..781251c4b 100644 --- a/share/qbs/module-providers/Qt/setup-qt.js +++ b/share/qbs/module-providers/Qt/setup-qt.js @@ -213,6 +213,8 @@ function replaceSpecialValues(content, module, qtProps, abi) { targetPlatform: ModUtils.toJSLiteral(qbsTargetPlatformFromQtMkspec(qtProps)), config: ModUtils.toJSLiteral(qtProps.configItems), qtConfig: ModUtils.toJSLiteral(qtProps.qtConfigItems), + enabledFeatures: ModUtils.toJSLiteral(qtProps.enabledFeatures), + disabledFeatures: ModUtils.toJSLiteral(qtProps.disabledFeatures), binPath: ModUtils.toJSLiteral(qtProps.binaryPath), installPath: ModUtils.toJSLiteral(qtProps.installPath), installPrefixPath: ModUtils.toJSLiteral(qtProps.installPrefixPath), diff --git a/share/qbs/module-providers/Qt/templates/core.qbs b/share/qbs/module-providers/Qt/templates/core.qbs index 485402716..b44edbeb0 100644 --- a/share/qbs/module-providers/Qt/templates/core.qbs +++ b/share/qbs/module-providers/Qt/templates/core.qbs @@ -40,6 +40,8 @@ Module { property string libInfix: @libInfix@ property stringList config: @config@ property stringList qtConfig: @qtConfig@ + readonly property stringList enabledFeatures: @enabledFeatures@ + readonly property stringList disabledFeatures: @disabledFeatures@ property path binPath: @binPath@ property path installPath: @installPath@ property path incPath: @incPath@ @@ -444,6 +446,10 @@ Module { "-o", output.filePath]; if (input.Qt.core.enableBigResources) args.push("-pass", "1"); + if (product.Qt.core.disabledFeatures.contains("zstd") + && Utilities.versionCompare(product.Qt.core.version, "5.13") >= 0) { + args.push("--no-zstd"); + } var cmd = new Command(Rcc.fullPath(product), args); cmd.description = "rcc " + (input.Qt.core.enableBigResources ? "(pass 1) " : "") diff --git a/share/qbs/module-providers/conan.js b/share/qbs/module-providers/conan.js new file mode 100644 index 000000000..a44af7eec --- /dev/null +++ b/share/qbs/module-providers/conan.js @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2024 Kai Dohmen +** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com). +** 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$ +** +****************************************************************************/ + +var File = require("qbs.File"); +var FileInfo = require("qbs.FileInfo"); +var ModUtils = require("qbs.ModUtils"); +var PathProbe = require("../imports/qbs/Probes/path-probe.js") +var PathTools = require("qbs.PathTools"); +var TextFile = require("qbs.TextFile"); +var Utilities = require("qbs.Utilities"); + +const architectureMap = { + 'x86': 'x86', + 'x86_64': 'x86_64', + 'ppc32be': 'ppc', + 'ppc32': 'ppc', + 'ppc64le': 'ppc64', + 'ppc64': 'ppc64', + 'armv4': 'arm', + 'armv4i': 'arm', + 'armv5el': 'arm', + 'armv5hf': 'arm', + 'armv6': 'arm', + 'armv7': 'arm', + 'armv7hf': 'arm', + 'armv7s': 'arm', + 'armv7k': 'arm', + 'armv8': 'arm64', + 'armv8_32': 'arm64', + 'armv8.3': 'arm64', + 'sparc': 'sparc', + 'sparcv9': 'sparc64', + 'mips': 'mips', + 'mips64': 'mips64', + 'avr': 'avr', + 's390': 's390x', + 's390x': 's390x', + 'sh4le': 'sh' +} + +const platformMap = { + 'Windows': 'windows', + 'WindowsStore': 'windows', + 'WindowsCE': 'windows', + 'Linux': 'linux', + 'Macos': 'macos', + 'Android': 'android', + 'iOS': 'ios', + 'watchOS': 'watchos', + 'tvOS': 'tvos', + 'FreeBSD': 'freebsd', + 'SunOS': 'solaris', + 'AIX': 'aix', + 'Emscripten': undefined, + 'Arduino': 'none', + 'Neutrino': 'qnx', + 'baremetal': 'none', + 'VxWorks': 'vxworks', +} + +function findLibs(libnames, libdirs, libtypes, targetOS, forImport) { + var result = []; + if (libnames === null || libdirs === null) + return result; + libnames.forEach(function(libraryName) { + const suffixes = PathTools.librarySuffixes(targetOS, libtypes, forImport); + const lib = PathProbe.configure( + undefined, // selectors + [libraryName], + suffixes, + PathTools.libraryNameFilter(targetOS), + undefined, // candidateFilter + libdirs, // searchPaths + undefined, // pathSuffixes + [], // platformSearchPaths + undefined, // environmentPaths + undefined // platformEnvironmentPaths + ); + if (lib.found) + result.push(lib.files[0].filePath); + }); + return result; +} + +function getLibraryTypes(options) { + if (options !== undefined && options !== null) { + if (options.shared === undefined) + return ["shared", "static"]; + else if (options.shared === "True") + return ["shared"]; + else if (options.shared === "False") + return ["static"]; + } + return ["shared", "static"]; +} + +function configure(installDirectory, moduleName, outputBaseDir, jsonProbe) { + const moduleMapping = {"protobuflib": "protobuf"} + const realModuleName = moduleMapping[moduleName] || moduleName; + + const moduleFilePath = + FileInfo.joinPaths(installDirectory, "conan-qbs-deps", realModuleName + ".json") + if (!File.exists(moduleFilePath)) + return []; + + var reverseMapping = {} + for (var key in moduleMapping) + reverseMapping[moduleMapping[key]] = key + + console.info("Setting up Conan module '" + moduleName + "'"); + + var moduleFile = new TextFile(moduleFilePath, TextFile.ReadOnly); + const moduleInfo = JSON.parse(moduleFile.readAll()); + + const outputDir = FileInfo.joinPaths(outputBaseDir, "modules", moduleName.replace(".", "/")); + File.makePath(outputDir); + const outputFilePath = FileInfo.joinPaths(outputDir, "module.qbs"); + + const cppInfo = moduleInfo.cpp_info; + + var moduleContent = ""; + + // function write(data) { moduleContent = moduleContent + data } + function writeLine(data) { moduleContent = moduleContent + data + "\n"; } + + function writeProperty(propertyName, propertyValue) { + if (propertyValue === undefined || propertyValue === null) + propertyValue = []; + writeLine(" readonly property stringList " + propertyName + + ": " + ModUtils.toJSLiteral(propertyValue)); + } + function writeCppProperty(propertyName, propertyValue) { + writeProperty(propertyName, propertyValue); + // skip empty props for simplicity of the module file + if (propertyValue !== undefined && propertyValue != null && propertyValue.length !== 0) { + writeLine(" cpp." + propertyName + ": " + propertyName); + } + } + + writeLine("Module {"); + + writeLine(" version: " + ModUtils.toJSLiteral(moduleInfo.version)); + + const architecture = architectureMap[moduleInfo.settings.arch]; + const platform = platformMap[moduleInfo.settings.os]; + const targetOS = Utilities.canonicalPlatform(platform); + + writeLine(" readonly property string architecture: " + ModUtils.toJSLiteral(architecture)); + writeLine(" readonly property string platform: " + ModUtils.toJSLiteral(platform)); + writeLine(" condition: true"); + if (architecture !== undefined) { + writeLine(" && (!qbs.architecture || qbs.architecture === architecture)"); + } + if (platform !== undefined) { + if (["ios", "tvos", "watchos"].includes(platform)) { + writeLine(" && (qbs.targetPlatform === platform || qbs.targetPlatform === platform + \"-simulator\")"); + } else { + writeLine(" && qbs.targetPlatform === platform"); + } + } + + writeLine(" Depends { name: 'cpp' }"); + + moduleInfo.dependencies.forEach(function(dep) { + const realDepName = reverseMapping[dep.name] || dep.name; + writeLine(" Depends { name: " + ModUtils.toJSLiteral(realDepName) + + "; version: " + ModUtils.toJSLiteral(dep.version) + "}"); + }); + + writeLine(" readonly property stringList hostBinDirs: (" + ModUtils.toJSLiteral(moduleInfo.build_bindirs) + ")"); + // target bindirs + writeLine(" readonly property stringList binDirs: (" + ModUtils.toJSLiteral(cppInfo.bindirs) + ")"); + + // TODO: there's a weird issue with system include dirs with xcode-less clang - incorrect include order? + writeCppProperty("includePaths", cppInfo.includedirs); + writeCppProperty("frameworkPaths", cppInfo.frameworkdirs); + writeCppProperty("frameworks", cppInfo.frameworks); + writeCppProperty("defines", cppInfo.defines); + writeCppProperty("cFlags", cppInfo.cflags); + writeCppProperty("cxxFlags", cppInfo.cxxflags); + writeCppProperty("linkerFlags", (cppInfo.sharedlinkflags || []).concat(cppInfo.exelinkflags || [])); + + writeProperty("resources", cppInfo.resdirs); + + const isForImport = targetOS.includes("windows") + const libraryTypes = getLibraryTypes(moduleInfo.options); + libraryTypes.forEach(function(libraryType){ + const cppInfoLibs = cppInfo.libs === null ? [] : cppInfo.libs; + const libs = findLibs(cppInfoLibs, cppInfo.libdirs, libraryType, targetOS, isForImport) + .concat(cppInfo.system_libs === null ? [] : cppInfo.system_libs); + const property = libraryType === "shared" ? "dynamicLibraries" : "staticLibraries"; + writeCppProperty(property, libs); + }); + + writeLine("}"); + + var moduleFile = new TextFile(outputFilePath, TextFile.WriteOnly); + moduleFile.write(moduleContent); + moduleFile.close(); + + return ""; +} diff --git a/share/qbs/module-providers/conan.qbs b/share/qbs/module-providers/conan.qbs new file mode 100644 index 000000000..6391eeb65 --- /dev/null +++ b/share/qbs/module-providers/conan.qbs @@ -0,0 +1,12 @@ +import "conan.js" as ConanHelper + +ModuleProvider { + /* input */ + property path installDirectory + + isEager: false + + relativeSearchPaths: { + return ConanHelper.configure(installDirectory, moduleName, outputBaseDir); + } +} diff --git a/share/qbs/modules/capnproto/capnprotobase.qbs b/share/qbs/modules/capnproto/capnprotobase.qbs index 56d542770..916e53a51 100644 --- a/share/qbs/modules/capnproto/capnprotobase.qbs +++ b/share/qbs/modules/capnproto/capnprotobase.qbs @@ -28,6 +28,7 @@ ** ****************************************************************************/ +import qbs.FileInfo import qbs.Probes import "capnproto.js" as HelperFunctions @@ -42,14 +43,18 @@ Module { property string outputDir: product.buildDirectory + "/capnp" + property var _searchPaths + Probes.BinaryProbe { id: compilerProbe names: compilerName ? [compilerName] : [] + searchPaths: _searchPaths } Probes.BinaryProbe { id: pluginProbe names: pluginName ? [pluginName] : [] + searchPaths: _searchPaths } FileTagger { diff --git a/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs b/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs index bccfca192..83192b7de 100644 --- a/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs +++ b/share/qbs/modules/capnproto/cpp/capnprotocpp.qbs @@ -40,6 +40,7 @@ CapnProtoBase { pluginName: "capnpc-c++" version: capnp.version + _searchPaths: capnp.hostBinDirs cpp.systemIncludePaths: outputDir cpp.cxxLanguageVersion: "c++14" diff --git a/share/qbs/modules/flatbuf/c/flatbuffers-c.qbs b/share/qbs/modules/flatbuf/c/flatbuffers-c.qbs new file mode 100644 index 000000000..9f5b2b70c --- /dev/null +++ b/share/qbs/modules/flatbuf/c/flatbuffers-c.qbs @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 http://www.qt.io/terms-conditions. For further information +** use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.FileInfo + +import "../flatbuffers.js" as Flatbuf +import "../flatbuffersbase.qbs" as FlatbufBase + +FlatbufBase { + Depends { name: "cpp" } + Depends { name: "flatcc" } + + compilerName: "flatcc" + + _outputDir: FileInfo.joinPaths(product.buildDirectory, "flatbuf", "c") + _searchPaths: flatbuffers.hostBinDirs + + cpp.systemIncludePaths: base.concat([_outputDir]) + + Rule { + inputs: ["flatbuf.input"] + outputFileTags: ["hpp"] + outputArtifacts: [Flatbuf.artifactC(input.flatbuf.c, input, "hpp", "_generated.h")] + + prepare: { + return Flatbuf.doPrepareC(input.flatbuf.c, input); + } + } + + validate: { + Flatbuf.validateCompiler(compilerName, compilerPath); + } +} diff --git a/share/qbs/modules/flatbuf/cpp/flatbuffers-cpp.qbs b/share/qbs/modules/flatbuf/cpp/flatbuffers-cpp.qbs new file mode 100644 index 000000000..4614d4949 --- /dev/null +++ b/share/qbs/modules/flatbuf/cpp/flatbuffers-cpp.qbs @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 http://www.qt.io/terms-conditions. For further information +** use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.FileInfo + +import "../flatbuffers.js" as Flatbuf +import "../flatbuffersbase.qbs" as FlatbufBase + +FlatbufBase { + Depends { name: "cpp" } + Depends { name: "flatbuffers" } + + property string filenameExtension: "h" + property string filenameSuffix: "_generated" + property string includePrefix // TODO: test + property bool keepPrefix: false + + _outputDir: FileInfo.joinPaths(product.buildDirectory, "flatbuf", "cpp") + _searchPaths: flatbuffers.hostBinDirs + + cpp.cxxLanguageVersion: "c++11" + cpp.systemIncludePaths: base.concat([_outputDir]) + + Rule { + inputs: ["flatbuf.input"] + outputFileTags: ["hpp"] + outputArtifacts: { + var module = input.flatbuf.cpp; + var fullSuffix = module.filenameSuffix + "." + module.filenameExtension; + return [ Flatbuf.artifact(module, input, "hpp", fullSuffix) ]; + } + + prepare: { + return Flatbuf.doPrepareCpp(input.flatbuf.cpp, input, output); + } + } + + validate: { + Flatbuf.validateCompiler(compilerName, compilerPath); + } +} diff --git a/share/qbs/modules/flatbuf/flatbuffers.js b/share/qbs/modules/flatbuf/flatbuffers.js new file mode 100644 index 000000000..24fd91168 --- /dev/null +++ b/share/qbs/modules/flatbuf/flatbuffers.js @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 http://www.qt.io/terms-conditions. For further information +** use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +var File = require("qbs.File"); + +function validateCompiler(compilerName, compilerPath) { + if (!File.exists(compilerPath)) { + throw "Can't find '" + compilerName + "' binary. Please set the compilerPath property or " + + "make sure the compiler is found in PATH"; + } +} + +function getOutputDir(module, input) { + var outputDir = module._outputDir; + if (!module.keepPrefix) + return outputDir; + var importPaths = module.importPaths; + if (importPaths !== undefined && importPaths.length !== 0) { + var canonicalInput = File.canonicalFilePath(FileInfo.path(input.filePath)); + for (var i = 0; i < importPaths.length; ++i) { + var path = File.canonicalFilePath(importPaths[i]); + + if (canonicalInput.startsWith(path)) { + return outputDir + "/" + FileInfo.relativePath(path, canonicalInput); + } + } + } + return outputDir; +} + +function artifactC(module, input, tag, suffix) { + var outputDir = module._outputDir; + return { + fileTags: [tag], + filePath: outputDir + "/" + FileInfo.baseName(input.fileName) + suffix, + cpp: { warningLevel: "none"} + }; +} + +function doPrepareC(module, input) +{ + var args = []; + args.push("-a") // write all + args.push("-o", input.flatbuf.c._outputDir) // output dir + + var importPaths = module.importPaths; + importPaths.forEach(function(path) { + args.push("-I", path); + }); + + args.push(input.filePath); + + var cmd = new Command(module.compilerPath, args); + cmd.workingDirectory = FileInfo.path(module._outputDir) + cmd.highlight = "codegen"; + cmd.description = "generating C files for " + input.fileName; + return [cmd]; +} + +function artifact(module, input, tag, suffix) { + var outputDir = getOutputDir(module, input); + return { + fileTags: [tag], + filePath: outputDir + "/" + FileInfo.baseName(input.fileName) + suffix, + cpp: { warningLevel: "none" } + }; +} + +function doPrepareCpp(module, input, output) +{ + var outputDir = FileInfo.path(output.filePath); + var result = []; + + var args = []; + args.push("--cpp") + + var importPaths = module.importPaths; + importPaths.forEach(function(path) { + args.push("-I", path); + }); + + args.push("--filename-ext", module.filenameExtension); + args.push("--filename-suffix", module.filenameSuffix); + + if (module.includePrefix) + args.push("--include-prefix", module.includePrefix); + + if (module.keepPrefix) + args.push("--keep-prefix"); + + args.push(input.filePath); + var cmd = new Command(input.flatbuf.cpp.compilerPath, args); + cmd.workingDirectory = outputDir; + cmd.highlight = "codegen"; + cmd.description = "generating C++ files for " + input.fileName; + result.push(cmd); + + return result; +} diff --git a/share/qbs/modules/flatbuf/flatbuffersbase.qbs b/share/qbs/modules/flatbuf/flatbuffersbase.qbs new file mode 100644 index 000000000..98eb2f344 --- /dev/null +++ b/share/qbs/modules/flatbuf/flatbuffersbase.qbs @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** 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 http://www.qt.io/terms-conditions. For further information +** use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.File +import qbs.FileInfo +import qbs.Probes + +import "flatbuffers.js" as HelperFunctions + +Module { + property string compilerName: "flatc" + property string compilerPath: compilerProbe.filePath + + property pathList importPaths: [] + + property string _outputDir: FileInfo.joinPaths(product.buildDirectory, "flatbuf") + + property stringList _searchPaths + + FileTagger { + patterns: ["*.fbs"] + fileTags: ["flatbuf.input"]; + } + + Probes.BinaryProbe { + id: compilerProbe + names: [compilerName] + searchPaths: _searchPaths + } +} diff --git a/share/qbs/modules/protobuf/cpp/protobufcpp.qbs b/share/qbs/modules/protobuf/cpp/protobufcpp.qbs index b5dab2372..bd4a08557 100644 --- a/share/qbs/modules/protobuf/cpp/protobufcpp.qbs +++ b/share/qbs/modules/protobuf/cpp/protobufcpp.qbs @@ -13,6 +13,9 @@ ProtobufBase { property string _cxxLanguageVersion: "c++17" + _searchPaths: protobuflib.present ? protobuflib.hostBinDirs : undefined + property stringList _grpcSearchPaths: grpcpp.present ? grpcpp.hostBinDirs : undefined + cpp.includePaths: outputDir Depends { name: "cpp" } @@ -34,6 +37,7 @@ ProtobufBase { condition: useGrpc id: grpcPluginProbe names: "grpc_cpp_plugin" + searchPaths: _grpcSearchPaths } cpp.cxxLanguageVersion: _cxxLanguageVersion diff --git a/share/qbs/modules/protobuf/protobufbase.qbs b/share/qbs/modules/protobuf/protobufbase.qbs index e302d3758..e32ee30b9 100644 --- a/share/qbs/modules/protobuf/protobufbase.qbs +++ b/share/qbs/modules/protobuf/protobufbase.qbs @@ -6,6 +6,7 @@ import "protobuf.js" as HelperFunctions Module { property string compilerName: "protoc" property string compilerPath: compilerProbe.filePath + property var _searchPaths property pathList importPaths: [] @@ -19,5 +20,6 @@ Module { Probes.BinaryProbe { id: compilerProbe names: [compilerName] + searchPaths: _searchPaths } } diff --git a/src/app/qbs/commandlinefrontend.cpp b/src/app/qbs/commandlinefrontend.cpp index 4a4781207..200740145 100644 --- a/src/app/qbs/commandlinefrontend.cpp +++ b/src/app/qbs/commandlinefrontend.cpp @@ -490,9 +490,22 @@ QString CommandLineFrontend::buildDirectory(const QString &profileName) const } QString projectName(QFileInfo(m_parser.projectFilePath()).baseName()); + QString originalBuildDir = buildDir; buildDir.replace(BuildDirectoryOption::magicProjectString(), projectName); + const QString buildDirPlaceHolderMsgTemplate = Tr::tr( + "You must provide the path to the project file when using build directory " + "placeholder '%1'."); + if (buildDir != originalBuildDir && projectName.isEmpty()) { + throw ErrorInfo( + buildDirPlaceHolderMsgTemplate.arg(BuildDirectoryOption::magicProjectString())); + } QString projectDir(QFileInfo(m_parser.projectFilePath()).path()); + originalBuildDir = buildDir; buildDir.replace(BuildDirectoryOption::magicProjectDirString(), projectDir); + if (buildDir != originalBuildDir && projectDir.isEmpty()) { + throw ErrorInfo( + buildDirPlaceHolderMsgTemplate.arg(BuildDirectoryOption::magicProjectDirString())); + } if (!QFileInfo(buildDir).isAbsolute()) buildDir = QDir::currentPath() + QLatin1Char('/') + buildDir; buildDir = QDir::cleanPath(buildDir); diff --git a/src/conan/extensions/generators/qbsdeps.py b/src/conan/extensions/generators/qbsdeps.py new file mode 100644 index 000000000..77d875aa8 --- /dev/null +++ b/src/conan/extensions/generators/qbsdeps.py @@ -0,0 +1,218 @@ +############################################################################ +## +## Copyright (C) 2024 Ivan Komissarov (abbapoh@gmail.com). +## 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$ +## +############################################################################ + +from conan.tools.files import save +from conan.errors import ConanException +from conans.model.dependencies import get_transitive_requires +import json +import os + + +class _QbsDepsModuleFile(): + def __init__(self, qbsdeps, dep, component, deps, module_name): + self._qbsdeps = qbsdeps + self._dep = dep + self._component = component + self._deps = deps + self._module_name = module_name + self._build_bindirs = qbsdeps._build_bindirs + + def _get_component_version(dep, component): + return (component.get_property("component_version") or + component.get_property("system_package_version") or + dep.ref.version) + self._version = _get_component_version(self._dep, self._component) + + @property + def filename(self): + return self._module_name + '.json' + + @property + def version(self): + return self._version + + def get_content(self): + return { + 'package_name': self._dep.ref.name, + 'package_dir': self._get_package_dir(), + 'version': str(self._version), + 'cpp_info': self._component.serialize(), + 'build_bindirs': self._build_bindirs, + 'dependencies': [{'name': n, "version": str(v)} for n, v in self._deps], + 'settings': {k: v for k, v in self._dep.settings.items()}, + 'options': {k: v for k, v in self._dep.options.items()} + } + + def _get_package_dir(self): + # If editable, package_folder can be None + return self._dep.recipe_folder if self._dep.package_folder is None \ + else self._dep.package_folder + + def render(self): + return json.dumps(self.get_content(), indent=4) + + +class _QbsDepGenerator: + ''' Handles a single package, can create multiple modules in case of several components ''' + def __init__(self, conanfile, dep, build_bindirs): + self._conanfile = conanfile + self._dep = dep + self._build_bindirs = build_bindirs + + @property + def content(self): + qbs_files = {} + transitive_reqs = get_transitive_requires(self._conanfile, self._dep) + + def _get_package_name(dep): + # TODO: pkgconfig uses suffix, do we need it? see: + # https://github.com/conan-io/conan/blob/develop2/conan/tools/gnu/pkgconfigdeps.py#L319 + return dep.cpp_info.get_property("pkg_config_name") or dep.ref.name + + def _get_component_name(dep, comp_name): + if comp_name not in dep.cpp_info.components: + if dep.ref.name == comp_name: + return _get_package_name(dep) + raise ConanException("Component '{name}::{cname}' not found in '{name}' " + "package requirement".format(name=dep.ref.name, + cname=comp_name)) + + # TODO: pkgconfig uses suffix, do we need it? + # We re-use pkg_config_name for compatiblitity with the Qbs pkg-config provider: + # in that case, Qbs/its users do not need to do additional mapping on their side + pkg_config_name = dep.cpp_info.components[comp_name].get_property("pkg_config_name") + return pkg_config_name or comp_name + + def _get_name_with_namespace(namespace, name): + """ + Build a name with a namespace, e.g., openssl-crypto + """ + return f"{namespace}-{name}" + + def get_components(dep): + ret = {} + for comp_ref_name, info in dep.cpp_info.get_sorted_components().items(): + comp_name = _get_component_name(dep, comp_ref_name) + ret[comp_name] = info + return ret + + # copy & paste from pkgconfig deps + def get_cpp_info_requires_names(dep, cpp_info): + ret = [] + dep_ref_name = dep.ref.name + for req in cpp_info.requires: + pkg_ref_name, comp_ref_name = ( + req.split("::") if "::" in req else (dep_ref_name, req) + ) + + if dep_ref_name != pkg_ref_name: + try: + req_conanfile = transitive_reqs[pkg_ref_name] + except KeyError: + # If the dependency is not in the transitive, might be skipped + continue + # For instance, dep == "hello/1.0" and req == "hello::cmp1" -> hello == hello + else: + req_conanfile = dep + + comp_name = _get_component_name(req_conanfile, comp_ref_name) + if not comp_name: + pkg_name = _get_package_name(req_conanfile) + # Creating a component name with namespace, e.g., dep-comp1 + comp_name = _get_name_with_namespace(pkg_name, comp_ref_name) + ret.append((comp_name, req_conanfile.ref.version)) + return ret + + if not self._dep.cpp_info.has_components: + module_name = _get_package_name(self._dep) + requires = get_cpp_info_requires_names(self._dep, self._dep.cpp_info._package) + if not requires: + # If no requires were found, let's try to get all the direct visible + # dependencies, e.g., requires = "other_pkg/1.0" + for deprequire, _ in self._dep.dependencies.direct_host.items(): + requires.append((deprequire.ref.name, deprequire.ref.version)) + file = _QbsDepsModuleFile( + self, self._dep, self._dep.cpp_info._package, requires, module_name + ) + qbs_files[file.filename] = file + else: + full_requires = [] + for module_name, component in get_components(self._dep).items(): + requires = get_cpp_info_requires_names(self._dep, component) + file = _QbsDepsModuleFile(self, self._dep, component, requires, module_name) + qbs_files[file.filename] = file + full_requires.append((module_name, file.version)) + module_name = _get_package_name(self._dep) + file = _QbsDepsModuleFile( + self, self._dep, self._dep.cpp_info._package, full_requires, module_name) + # We create the root package's module file ONLY + # if it does not already exist in components + # An example is a grpc package where they have a "grpc" component + if file.filename not in qbs_files: + qbs_files[file.filename] = file + + return qbs_files + + +class QbsDeps: + ''' Handles multiple packages ''' + def __init__(self, conanfile): + self._conanfile = conanfile + + @property + def content(self): + qbs_files = {} + + build_bindirs = { + dep.ref.name: dep.cpp_info.bindirs + for _, dep in self._conanfile.dependencies.build.items()} + + for require, dep in self._conanfile.dependencies.items(): + + # skip build deps for now + if require.build: + continue + + dep_build_bindirs = build_bindirs.get(dep.ref.name, []) + qbs_files.update(_QbsDepGenerator(self._conanfile, dep, dep_build_bindirs).content) + return qbs_files + + def generate(self): + for file_name, qbs_deps_file in self.content.items(): + save(self._conanfile, os.path.join('conan-qbs-deps', file_name), qbs_deps_file.render()) diff --git a/src/lib/corelib/CMakeLists.txt b/src/lib/corelib/CMakeLists.txt index 97b5e6fad..ff9d9e428 100644 --- a/src/lib/corelib/CMakeLists.txt +++ b/src/lib/corelib/CMakeLists.txt @@ -456,6 +456,7 @@ add_qbs_library(qbscore qbsquickjs PUBLIC_DEPENDS Qt${QT_VERSION_MAJOR}::Core + span ${EXTERNAL_DEPENDS} INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/../.." diff --git a/src/lib/corelib/buildgraph/abstractcommandexecutor.cpp b/src/lib/corelib/buildgraph/abstractcommandexecutor.cpp index 16c3621b6..ee82de43d 100644 --- a/src/lib/corelib/buildgraph/abstractcommandexecutor.cpp +++ b/src/lib/corelib/buildgraph/abstractcommandexecutor.cpp @@ -60,13 +60,12 @@ AbstractCommandExecutor::AbstractCommandExecutor(Logger logger, QObject *parent) , m_logger(std::move(logger)) { m_watchdog.setSingleShot(true); - connect(&m_watchdog, &QTimer::timeout, - this, [this]() { - cancel(ErrorInfo{Tr::tr("Command cancelled because it exceeded the timeout.")}); + connect(&m_watchdog, &QTimer::timeout, this, [this]() { + cancel(ErrorInfo{Tr::tr("Command cancelled because it exceeded the timeout: %1") + .arg(m_command->descriptionForCancelMessage( + m_transformer->product()->fullDisplayName()))}); }); - connect(this, &AbstractCommandExecutor::finished, - &m_watchdog, &QTimer::stop); - + connect(this, &AbstractCommandExecutor::finished, &m_watchdog, &QTimer::stop); } void AbstractCommandExecutor::start(Transformer *transformer, AbstractCommand *cmd) diff --git a/src/lib/corelib/buildgraph/buildgraph.cpp b/src/lib/corelib/buildgraph/buildgraph.cpp index 10aae5991..d8a7cfa64 100644 --- a/src/lib/corelib/buildgraph/buildgraph.cpp +++ b/src/lib/corelib/buildgraph/buildgraph.cpp @@ -535,8 +535,8 @@ bool findPath(BuildGraphNode *u, BuildGraphNode *v, QList<BuildGraphNode *> &pat */ void connect(BuildGraphNode *p, BuildGraphNode *c) { - QBS_CHECK(p != c); qCDebug(lcBuildGraph).noquote() << "connect" << p->toString() << "->" << c->toString(); + QBS_CHECK(p != c); if (c->type() == BuildGraphNode::ArtifactNodeType) { auto const ac = static_cast<Artifact *>(c); for (const Artifact *child : filterByType<Artifact>(p->children)) { diff --git a/src/lib/corelib/buildgraph/qtmocscanner.cpp b/src/lib/corelib/buildgraph/qtmocscanner.cpp index 7df84e52c..fb9f08694 100644 --- a/src/lib/corelib/buildgraph/qtmocscanner.cpp +++ b/src/lib/corelib/buildgraph/qtmocscanner.cpp @@ -109,7 +109,7 @@ QtMocScanner::QtMocScanner(const ResolvedProductPtr &product, ScriptEngine *engi 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); + setJsProperty(engine->context(), scannerObj, std::string_view("apply"), applyFunction); } QtMocScanner::~QtMocScanner() @@ -269,10 +269,13 @@ JSValue QtMocScanner::apply(ScriptEngine *engine, const Artifact *artifact) 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)); + setJsProperty(ctx, obj, std::string_view("hasQObjectMacro"), JS_NewBool(ctx, hasQObjectMacro)); + setJsProperty(ctx, obj, std::string_view("mustCompile"), JS_NewBool(ctx, mustCompile)); + setJsProperty( + ctx, + obj, + std::string_view("hasPluginMetaDataMacro"), + JS_NewBool(ctx, hasPluginMetaDataMacro)); engine->setUsesIo(); return obj; } diff --git a/src/lib/corelib/buildgraph/rulecommands.cpp b/src/lib/corelib/buildgraph/rulecommands.cpp index 73d05eaca..be90c2fd5 100644 --- a/src/lib/corelib/buildgraph/rulecommands.cpp +++ b/src/lib/corelib/buildgraph/rulecommands.cpp @@ -135,6 +135,11 @@ QString AbstractCommand::fullDescription(const QString &productName) const return description() + QLatin1String(" [") + productName + QLatin1Char(']'); } +QString AbstractCommand::descriptionForCancelMessage(const QString &productName) const +{ + return fullDescription(productName); +} + void AbstractCommand::load(PersistentPool &pool) { serializationOp<PersistentPool::Load>(pool); @@ -335,6 +340,12 @@ void ProcessCommand::fillFromScriptValue(JSContext *ctx, const JSValue *scriptVa applyCommandProperties(ctx, scriptValue); } +QString ProcessCommand::descriptionForCancelMessage(const QString &productName) const +{ + return description() + QLatin1String(" (") + QDir::toNativeSeparators(m_program) + + QLatin1String(") [") + productName + QLatin1Char(']'); +} + QStringList ProcessCommand::relevantEnvVars() const { QStringList vars = m_relevantEnvVars; diff --git a/src/lib/corelib/buildgraph/rulecommands.h b/src/lib/corelib/buildgraph/rulecommands.h index 7b08d1015..4296146d2 100644 --- a/src/lib/corelib/buildgraph/rulecommands.h +++ b/src/lib/corelib/buildgraph/rulecommands.h @@ -78,6 +78,7 @@ public: virtual bool equals(const AbstractCommand *other) const; virtual void fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue, const CodeLocation &codeLocation); + virtual QString descriptionForCancelMessage(const QString &productName) const; QString fullDescription(const QString &productName) const; const QString description() const { return m_description; } @@ -129,6 +130,7 @@ public: bool equals(const AbstractCommand *otherAbstractCommand) const override; void fillFromScriptValue(JSContext *ctx, const JSValue *scriptValue, const CodeLocation &codeLocation) override; + QString descriptionForCancelMessage(const QString &productName) const override; const QString program() const { return m_program; } const QStringList arguments() const { return m_arguments; } const QString workingDir() const { return m_workingDir; } diff --git a/src/lib/corelib/jsextensions/domxml.cpp b/src/lib/corelib/jsextensions/domxml.cpp index 35cff186b..e065124e6 100644 --- a/src/lib/corelib/jsextensions/domxml.cpp +++ b/src/lib/corelib/jsextensions/domxml.cpp @@ -549,5 +549,5 @@ void initializeJsExtensionXml(qbs::Internal::ScriptEngine *engine, JSValue exten JSValue contextObject = engine->newObject(); XmlDomNode<QDomNode>::registerClass(engine, contextObject); XmlDomNode<QDomDocument>::registerClass(engine, contextObject); - setJsProperty(engine->context(), extensionObject, QLatin1String("Xml"), contextObject); + setJsProperty(engine->context(), extensionObject, std::string_view("Xml"), contextObject); } diff --git a/src/lib/corelib/jsextensions/moduleproperties.cpp b/src/lib/corelib/jsextensions/moduleproperties.cpp index 2c73f2c53..f3583c872 100644 --- a/src/lib/corelib/jsextensions/moduleproperties.cpp +++ b/src/lib/corelib/jsextensions/moduleproperties.cpp @@ -234,7 +234,7 @@ static void initModuleProperties(ScriptEngine *engine, JSValue objectWithPropert { JSContext * const ctx = engine->context(); JSValue func = JS_NewCFunction(ctx, js_moduleProperty<ProductOrArtifact>, "moduleProperty", 2); - setJsProperty(ctx, objectWithProperties, QStringLiteral("moduleProperty"), func); + setJsProperty(ctx, objectWithProperties, std::string_view("moduleProperty"), func); } static JSValue setupBaseModuleScriptValue(ScriptEngine *engine, const ResolvedModule *module) diff --git a/src/lib/corelib/language/evaluator.cpp b/src/lib/corelib/language/evaluator.cpp index 084f2f4a9..288816bb6 100644 --- a/src/lib/corelib/language/evaluator.cpp +++ b/src/lib/corelib/language/evaluator.cpp @@ -464,10 +464,15 @@ static int getEvalPropertyNames(JSContext *ctx, JSPropertyEnum **ptab, uint32_t class PropertyStackManager { public: - PropertyStackManager(const Item *itemOfProperty, const QString &name, const Value *value, - std::stack<QualifiedId> &requestedProperties, - PropertyDependencies &propertyDependencies) + PropertyStackManager( + const Item *itemOfProperty, + const QString &name, + const Value *value, + std::stack<QualifiedId> &requestedProperties, + PropertyDependencies &propertyDependencies, + Evaluator::EvalStack &evalStack) : m_requestedProperties(requestedProperties) + , m_evalStack(evalStack) { if (value->type() == Value::JSSourceValueType && (itemOfProperty->type() == ItemType::ModuleInstance @@ -484,16 +489,31 @@ public: propertyDependencies[fullPropName].insert(requestedProperties.top()); m_requestedProperties.push(fullPropName); } + const auto matcher = [value](const std::pair<QString, const Value *> &prop) { + return prop.second == value; + }; + if (auto it = std::find_if(m_evalStack.cbegin(), m_evalStack.cend(), matcher); + it != m_evalStack.cend()) { + ErrorInfo error(Tr::tr("Property '%1' refers to itself.").arg(name), value->location()); + for (auto it2 = std::next(it); it2 != m_evalStack.cend(); ++it2) { + error.append( + QString::fromLatin1(" via '%1'").arg(it2->first), it2->second->location()); + } + throw ErrorInfo(error); + } + m_evalStack.emplace_back(name, value); } ~PropertyStackManager() { if (m_stackUpdate) m_requestedProperties.pop(); + m_evalStack.pop_back(); } private: std::stack<QualifiedId> &m_requestedProperties; + Evaluator::EvalStack &m_evalStack; bool m_stackUpdate = false; }; @@ -904,9 +924,13 @@ static EvalResult getEvalProperty(ScriptEngine *engine, JSValue obj, const Item if (!value) continue; const Item * const itemOfProperty = item; // The item that owns the property. - PropertyStackManager propStackmanager(itemOfProperty, name, value.get(), - evaluator->requestedProperties(), - evaluator->propertyDependencies()); + PropertyStackManager propStackmanager( + itemOfProperty, + name, + value.get(), + evaluator->requestedProperties(), + evaluator->propertyDependencies(), + evaluator->evalStack()); JSValue result; if (evaluator->cachingEnabled()) { data->evaluator->clearCacheIfInvalidated(*data); diff --git a/src/lib/corelib/language/evaluator.h b/src/lib/corelib/language/evaluator.h index d86e08eb1..b27b2e4c6 100644 --- a/src/lib/corelib/language/evaluator.h +++ b/src/lib/corelib/language/evaluator.h @@ -48,10 +48,12 @@ #include <QtCore/qhash.h> +#include <deque> #include <functional> #include <mutex> #include <optional> #include <stack> +#include <utility> namespace qbs { namespace Internal { @@ -111,6 +113,9 @@ public: std::stack<QualifiedId> &requestedProperties() { return m_requestedProperties; } + using EvalStack = std::deque<std::pair<QString, const Value *>>; + EvalStack &evalStack() { return m_evalStack; } + void handleEvaluationError(const Item *item, const QString &name); QString pathPropertiesBaseDir() const { return m_pathPropertiesBaseDir; } @@ -131,6 +136,7 @@ private: QString m_pathPropertiesBaseDir; PropertyDependencies m_propertyDependencies; std::stack<QualifiedId> m_requestedProperties; + EvalStack m_evalStack; std::mutex m_cacheInvalidationMutex; Set<const Item *> m_invalidatedCaches; bool m_valueCacheEnabled = false; diff --git a/src/lib/corelib/language/scriptengine.cpp b/src/lib/corelib/language/scriptengine.cpp index 9131db7f5..6c0e4d770 100644 --- a/src/lib/corelib/language/scriptengine.cpp +++ b/src/lib/corelib/language/scriptengine.cpp @@ -137,18 +137,25 @@ LookupResult ScriptEngine::doExtraScopeLookup(JSContext *ctx, JSAtom prop) ScriptEngine * const engine = engineForContext(ctx); engine->m_lastLookupWasSuccess = false; - JSValueList scopes; - 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); + auto handleScope = [ctx, prop, engine](const JSValue &scope) -> LookupResult { + const JSValue v = JS_GetProperty(ctx, scope, prop); if (!JS_IsUndefined(v) || engine->m_lastLookupWasSuccess) { engine->m_lastLookupWasSuccess = false; - return {v, *it, true}; + return {v, scope, true}; + } + return fail; + }; + + if (!engine->m_scopeChains.empty()) { + const JSValueList &scopes = engine->m_scopeChains.back().get(); + for (auto it = scopes.rbegin(); it != scopes.rend(); ++it) { + const auto res = handleScope(*it); + if (res.useResult) + return res; } } + if (JS_IsObject(engine->m_globalObject)) + return handleScope(engine->m_globalObject); return fail; } @@ -510,8 +517,7 @@ JSValue ScriptEngine::asJsValue(const QVariant &v, quintptr id, bool frozen) case QMetaType::Bool: return JS_NewBool(m_context, v.toBool()); case QMetaType::QDateTime: - return JS_NewDate( - m_context, v.toDateTime().toString(Qt::ISODateWithMs).toUtf8().constData()); + return JS_NewDate(m_context, v.toDateTime().toMSecsSinceEpoch()); case QMetaType::QVariantMap: return asJsValue(v.toMap(), id, frozen); default: @@ -538,7 +544,7 @@ JSValue ScriptEngine::asJsValue(const QString &s) JSValue ScriptEngine::asJsValue(const QStringList &l) { JSValue array = JS_NewArray(m_context); - setJsProperty(m_context, array, QLatin1String("length"), JS_NewInt32(m_context, l.size())); + setJsProperty(m_context, array, std::string_view("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; @@ -574,7 +580,7 @@ JSValue ScriptEngine::asJsValue(const QVariantList &l, quintptr id, bool frozen) return JS_DupValue(m_context, it.value()); frozen = id || frozen; JSValue array = JS_NewArray(m_context); - setJsProperty(m_context, array, QLatin1String("length"), JS_NewInt32(m_context, l.size())); + setJsProperty(m_context, array, std::string_view("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), 0, frozen)); if (frozen) @@ -805,14 +811,14 @@ JSValue ScriptEngine::newArray(int length, JsValueOwner owner) JSValue ScriptEngine::evaluate(JsValueOwner resultOwner, const QString &code, const QString &filePath, int line, const JSValueList &scopeChain) { - m_scopeChains << scopeChain; + m_scopeChains.emplace_back(scopeChain); const QByteArray &codeStr = code.toUtf8(); m_evalPositions.emplace(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(); + m_scopeChains.pop_back(); if (resultOwner == JsValueOwner::ScriptEngine && JS_VALUE_HAS_REF_COUNT(v)) ++m_evalResults[v]; return v; diff --git a/src/lib/corelib/language/scriptengine.h b/src/lib/corelib/language/scriptengine.h index 4a55392e3..8efa2d482 100644 --- a/src/lib/corelib/language/scriptengine.h +++ b/src/lib/corelib/language/scriptengine.h @@ -392,7 +392,7 @@ private: std::unordered_map<const ResolvedModule *, JSValue> m_moduleArtifactsMapScriptValues; std::unordered_map<const ResolvedProject *, JSValue> m_projectScriptValues; std::unordered_map<const ResolvedModule *, JSValue> m_baseModuleScriptValues; - QList<JSValueList> m_scopeChains; + std::vector<std::reference_wrapper<const JSValueList>> m_scopeChains; JSValueList m_contextStack; QHash<JSClassID, JSClassExoticMethods> m_exoticMethods; QHash<QString, JSClassID> m_classes; diff --git a/src/lib/corelib/tools/codelocation.cpp b/src/lib/corelib/tools/codelocation.cpp index f04041e14..7ab1deb13 100644 --- a/src/lib/corelib/tools/codelocation.cpp +++ b/src/lib/corelib/tools/codelocation.cpp @@ -53,64 +53,12 @@ namespace qbs { -class CodeLocation::CodeLocationPrivate : public QSharedData -{ -public: - void load(Internal::PersistentPool &pool) - { - pool.load(filePath); - pool.load(line); - pool.load(column); - } - - void store(Internal::PersistentPool &pool) const - { - pool.store(filePath); - pool.store(line); - pool.store(column); - } - - QString filePath; - int line = 0; - int column = 0; -}; - -CodeLocation::CodeLocation() = default; - CodeLocation::CodeLocation(const QString &aFilePath, int aLine, int aColumn, bool checkPath) - : d(new CodeLocationPrivate) { QBS_ASSERT(!checkPath || Internal::FileInfo::isAbsolute(aFilePath), qDebug() << aFilePath); - d->filePath = aFilePath; - d->line = aLine; - d->column = aColumn; -} - -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; - -QString CodeLocation::filePath() const -{ - return d ? d->filePath : QString(); -} - -int CodeLocation::line() const -{ - return d ? d->line : -1; -} - -int CodeLocation::column() const -{ - return d ? d->column : -1; -} - -bool CodeLocation::isValid() const -{ - return !filePath().isEmpty(); + m_filePath = aFilePath; + m_line = aLine; + m_column = aColumn; } QString CodeLocation::toString() const @@ -145,15 +93,18 @@ void CodeLocation::load(Internal::PersistentPool &pool) const bool isValid = pool.load<bool>(); if (!isValid) return; - d = new CodeLocationPrivate; - pool.load(*d); + pool.load(m_filePath); + pool.load(m_line); + pool.load(m_column); } void CodeLocation::store(Internal::PersistentPool &pool) const { - if (d) { + if (isValid()) { pool.store(true); - pool.store(*d); + pool.store(m_filePath); + pool.store(m_line); + pool.store(m_column); } else { pool.store(false); } @@ -161,10 +112,8 @@ void CodeLocation::store(Internal::PersistentPool &pool) const bool operator==(const CodeLocation &cl1, const CodeLocation &cl2) { - if (cl1.d == cl2.d) - return true; return cl1.filePath() == cl2.filePath() && cl1.line() == cl2.line() - && cl1.column() == cl2.column(); + && cl1.column() == cl2.column(); } bool operator!=(const CodeLocation &cl1, const CodeLocation &cl2) diff --git a/src/lib/corelib/tools/codelocation.h b/src/lib/corelib/tools/codelocation.h index afcd2e075..aa82ab4e1 100644 --- a/src/lib/corelib/tools/codelocation.h +++ b/src/lib/corelib/tools/codelocation.h @@ -57,21 +57,17 @@ namespace Internal { class PersistentPool; } class QBS_EXPORT CodeLocation { friend QBS_EXPORT bool operator==(const CodeLocation &cl1, const CodeLocation &cl2); + public: - CodeLocation(); - 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; - int line() const; - int column() const; - - bool isValid() const; + CodeLocation() = default; + explicit CodeLocation( + const QString &aFilePath, int aLine = -1, int aColumn = -1, bool checkPath = true); + + const QString &filePath() const noexcept { return m_filePath; } + int line() const noexcept { return m_line; } + int column() const noexcept { return m_column; } + + bool isValid() const noexcept { return !m_filePath.isEmpty(); } QString toString() const; QJsonObject toJson() const; @@ -79,8 +75,9 @@ public: void store(Internal::PersistentPool &pool) const; private: - class CodeLocationPrivate; - QExplicitlySharedDataPointer<CodeLocationPrivate> d; + QString m_filePath; + int m_line = 0; + int m_column = 0; }; QBS_EXPORT bool operator==(const CodeLocation &cl1, const CodeLocation &cl2); diff --git a/src/lib/corelib/tools/scripttools.cpp b/src/lib/corelib/tools/scripttools.cpp index e89e4a0ad..6aa44adbb 100644 --- a/src/lib/corelib/tools/scripttools.cpp +++ b/src/lib/corelib/tools/scripttools.cpp @@ -136,10 +136,15 @@ JSValue getJsProperty(JSContext *ctx, JSValue obj, const QString &prop) return JS_GetPropertyStr(ctx, obj, prop.toUtf8().constData()); } +void setJsProperty(JSContext *ctx, JSValueConst obj, std::string_view prop, JSValue val) +{ + JS_SetPropertyStr(ctx, obj, prop.data(), val); +} + void setJsProperty(JSContext *ctx, JSValue obj, const QString &prop, JSValue val) { - if (JS_SetPropertyStr(ctx, obj, prop.toUtf8().constData(), val) <= 0) - qDebug() << "Oje!"; + const auto propUtf8 = prop.toUtf8(); + setJsProperty(ctx, obj, std::string_view{propUtf8.data(), size_t(propUtf8.size())}, val); } void setJsProperty(JSContext *ctx, JSValue obj, const QString &prop, const QString &val) diff --git a/src/lib/corelib/tools/scripttools.h b/src/lib/corelib/tools/scripttools.h index ac7ed9928..20964cae5 100644 --- a/src/lib/corelib/tools/scripttools.h +++ b/src/lib/corelib/tools/scripttools.h @@ -50,6 +50,7 @@ #include <QtCore/qstringlist.h> #include <QtCore/qvariant.h> +#include <string_view> #include <utility> #include <vector> @@ -62,6 +63,7 @@ using JSValueList = std::vector<JSValue>; 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, std::string_view prop, JSValue val); 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); diff --git a/src/lib/corelib/tools/span.h b/src/lib/corelib/tools/span.h new file mode 100644 index 000000000..e5553115c --- /dev/null +++ b/src/lib/corelib/tools/span.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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_SPAN_H +#define QBS_SPAN_H + +#include <qcompilerdetection.h> + +// The (Apple) Clang implementation of span is incomplete until LLVM 15 / Xcode 14.3 / macOS 13 +#if __cplusplus >= 202002L \ + && !(defined(__apple_build_version__) && __apple_build_version__ < 14030022) +#include <span> + +namespace qbs { +namespace Internal { +using std::as_bytes; +using std::as_writable_bytes; +using std::get; +using std::span; +} // namespace Internal +} // namespace qbs + +#else +QT_WARNING_PUSH + +#if defined(Q_CC_MSVC) +#pragma system_header +#elif defined(Q_CC_GNU) || defined(Q_CC_CLANG) +#pragma GCC system_header +#endif + +// disable automatic usage of std::span in span-lite +// since we make that decision ourselves at the top of this header +#define span_CONFIG_SELECT_SPAN span_SPAN_NONSTD + +#include <span.hpp> +namespace qbs { +namespace Internal { +using namespace nonstd; +} // namespace Internal +} // namespace qbs + +QT_WARNING_POP +#endif + +#endif // QBS_SPAN_H
\ No newline at end of file diff --git a/src/lib/corelib/tools/stringutils.h b/src/lib/corelib/tools/stringutils.h index 59acdccbd..93b419b00 100644 --- a/src/lib/corelib/tools/stringutils.h +++ b/src/lib/corelib/tools/stringutils.h @@ -97,34 +97,6 @@ static inline std::string trimmed(const std::string &s) return trim(copy); } -static inline bool startsWith(const std::string &subject, const std::string &s) -{ - if (s.size() <= subject.size()) - return std::equal(s.begin(), s.end(), subject.begin()); - return false; -} - -static inline bool startsWith(const std::string &subject, char c) -{ - std::string s; - s.push_back(c); - return startsWith(subject, s); -} - -static inline bool endsWith(const std::string &subject, const std::string &s) -{ - if (s.size() <= subject.size()) - return std::equal(s.rbegin(), s.rend(), subject.rbegin()); - return false; -} - -static inline bool endsWith(const std::string &subject, char c) -{ - std::string s; - s.push_back(c); - return endsWith(subject, s); -} - } // namespace Internal } // namespace qbs diff --git a/src/lib/pkgconfig/CMakeLists.txt b/src/lib/pkgconfig/CMakeLists.txt index c82edac16..8db179354 100644 --- a/src/lib/pkgconfig/CMakeLists.txt +++ b/src/lib/pkgconfig/CMakeLists.txt @@ -8,12 +8,6 @@ set(SOURCES ) list_transform_prepend(SOLUTION_SOURCES solution/) -if(APPLE OR MINGW) - set(HAS_STD_FILESYSTEM "0") -else() - set(HAS_STD_FILESYSTEM "1") -endif() - set(QBS_PKGCONFIG_PUBLIC_DEPENDS "") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) set(QBS_PKGCONFIG_PUBLIC_DEPENDS "stdc++fs") @@ -24,9 +18,8 @@ add_qbs_library(qbspkgconfig DEFINES "PKG_CONFIG_PC_PATH=\"${CMAKE_INSTALL_PREFIX}/${QBS_LIBDIR_NAME}/pkgconfig:${CMAKE_INSTALL_PREFIX}/share/pkgconfig:/usr/${QBS_LIBDIR_NAME}/pkgconfig/:/usr/share/pkgconfig/\"" "PKG_CONFIG_SYSTEM_LIBRARY_PATH=\"/usr/${QBS_LIBDIR_NAME}\"" - "HAS_STD_FILESYSTEM=${HAS_STD_FILESYSTEM}" PUBLIC_DEFINES "QBS_PC_WITH_QT_SUPPORT=1" - PUBLIC_DEPENDS Qt${QT_VERSION_MAJOR}::Core ${QBS_PKGCONFIG_PUBLIC_DEPENDS} qbsvariant + PUBLIC_DEPENDS ${QBS_PKGCONFIG_PUBLIC_DEPENDS} SOURCES ${SOURCES} ) diff --git a/src/lib/pkgconfig/pcpackage.h b/src/lib/pkgconfig/pcpackage.h index 794e2fc40..7e40b7c09 100644 --- a/src/lib/pkgconfig/pcpackage.h +++ b/src/lib/pkgconfig/pcpackage.h @@ -40,8 +40,6 @@ #ifndef PC_PACKAGE_H #define PC_PACKAGE_H -#include <variant.h> - #include <map> #include <optional> #include <stdexcept> @@ -49,6 +47,7 @@ #include <unordered_map> #include <unordered_set> #include <utility> +#include <variant> #include <vector> namespace qbs { @@ -132,31 +131,31 @@ public: std::string errorText; }; -class PcPackageVariant: public Variant::variant<PcPackage, PcBrokenPackage> +class PcPackageVariant : public std::variant<PcPackage, PcBrokenPackage> { public: - using Base = Variant::variant<PcPackage, PcBrokenPackage>; + using Base = std::variant<PcPackage, PcBrokenPackage>; using Base::Base; bool isValid() const noexcept { return index() == 0; } bool isBroken() const noexcept { return index() == 1; } - const PcPackage &asPackage() const { return Variant::get<PcPackage>(*this); } - PcPackage &asPackage() { return Variant::get<PcPackage>(*this); } + const PcPackage &asPackage() const { return std::get<PcPackage>(*this); } + PcPackage &asPackage() { return std::get<PcPackage>(*this); } - const PcBrokenPackage &asBrokenPackage() const { return Variant::get<PcBrokenPackage>(*this); } - PcBrokenPackage &asBrokenPackage() { return Variant::get<PcBrokenPackage>(*this); } + const PcBrokenPackage &asBrokenPackage() const { return std::get<PcBrokenPackage>(*this); } + PcBrokenPackage &asBrokenPackage() { return std::get<PcBrokenPackage>(*this); } template<typename F> decltype(auto) visit(F &&f) const { - return Variant::visit(std::forward<F>(f), static_cast<const Base &>(*this)); + return std::visit(std::forward<F>(f), static_cast<const Base &>(*this)); } template<typename F> decltype(auto) visit(F &&f) { - return Variant::visit(std::forward<F>(f), static_cast<Base &>(*this)); + return std::visit(std::forward<F>(f), static_cast<Base &>(*this)); } const std::string &getBaseFileName() const diff --git a/src/lib/pkgconfig/pcparser.cpp b/src/lib/pkgconfig/pcparser.cpp index eb07aa5ef..1649ead9d 100644 --- a/src/lib/pkgconfig/pcparser.cpp +++ b/src/lib/pkgconfig/pcparser.cpp @@ -41,22 +41,9 @@ #include "pkgconfig.h" -#if HAS_STD_FILESYSTEM -# if __has_include(<filesystem>) -# include <filesystem> -# else -# include <experimental/filesystem> -// We need the alias from std::experimental::filesystem to std::filesystem -namespace std { - namespace filesystem = experimental::filesystem; -} -# endif -#else -#include <QFileInfo> -#endif - #include <algorithm> #include <cctype> +#include <filesystem> #include <fstream> #include <stdexcept> @@ -413,15 +400,9 @@ try throw PcException(std::string("Can't open file ") + path); package.baseFileName = std::string{completeBaseName(path)}; -#if HAS_STD_FILESYSTEM const auto fsPath = std::filesystem::path(path); package.filePath = fsPath.generic_string(); package.variables["pcfiledir"] = fsPath.parent_path().generic_string(); -#else - QFileInfo fileInfo(QString::fromStdString(path)); - package.filePath = fileInfo.absoluteFilePath().toStdString(); - package.variables["pcfiledir"] = fileInfo.absolutePath().toStdString(); -#endif std::string line; while (readOneLine(file, line)) diff --git a/src/lib/pkgconfig/pkgconfig.cpp b/src/lib/pkgconfig/pkgconfig.cpp index 6b3bde46c..672aa56f1 100644 --- a/src/lib/pkgconfig/pkgconfig.cpp +++ b/src/lib/pkgconfig/pkgconfig.cpp @@ -40,22 +40,8 @@ #include "pkgconfig.h" #include "pcparser.h" -#if HAS_STD_FILESYSTEM -# if __has_include(<filesystem>) -# include <filesystem> -# else -# include <experimental/filesystem> -// We need the alias from std::experimental::filesystem to std::filesystem -namespace std { - namespace filesystem = experimental::filesystem; -} -# endif -#else -# include <QtCore/QDir> -# include <QtCore/QFileInfo> -#endif - #include <algorithm> +#include <filesystem> #include <iostream> namespace qbs { @@ -202,7 +188,6 @@ std::optional<std::string_view> PkgConfig::packageGetVariable( return result; } -#if HAS_STD_FILESYSTEM std::vector<std::string> getPcFilePaths(const std::vector<std::string> &searchPaths) { std::vector<std::filesystem::path> paths; @@ -227,23 +212,6 @@ std::vector<std::string> getPcFilePaths(const std::vector<std::string> &searchPa ); return result; } -#else -std::vector<std::string> getPcFilePaths(const std::vector<std::string> &searchPaths) -{ - std::vector<std::string> result; - for (const auto &path : searchPaths) { - QDir dir(QString::fromStdString(path)); - const auto paths = dir.entryList({QStringLiteral("*.pc")}); - std::transform( - std::begin(paths), - std::end(paths), - std::back_inserter(result), - [&dir](const auto &path) { return dir.filePath(path).toStdString(); } - ); - } - return result; -} -#endif PcBrokenPackage makeMissingDependency( const PcPackage &package, const PcPackage::RequiredVersion &depVersion) diff --git a/src/lib/pkgconfig/pkgconfig.qbs b/src/lib/pkgconfig/pkgconfig.qbs index a32eb775b..1434953f7 100644 --- a/src/lib/pkgconfig/pkgconfig.qbs +++ b/src/lib/pkgconfig/pkgconfig.qbs @@ -1,10 +1,10 @@ import qbs.FileInfo import qbs.Utilities -QbsStaticLibrary { +QbsProduct { + type: "staticlibrary" Depends { name: "cpp" } Depends { name: "qbsbuildconfig" } - Depends { name: "qbsvariant" } property stringList pcPaths: { var result = []; @@ -45,20 +45,14 @@ QbsStaticLibrary { "PKG_CONFIG_PC_PATH=\"" + pcPathsString + "\"", "PKG_CONFIG_SYSTEM_LIBRARY_PATH=\"/usr/" + qbsbuildconfig.libDirName + "\"", ] - if ((qbs.targetOS.contains("darwin") - && Utilities.versionCompare(cpp.minimumMacosVersion, "10.15") < 0) - || qbs.toolchain.contains("mingw")) - result.push("HAS_STD_FILESYSTEM=0") - else - result.push("HAS_STD_FILESYSTEM=1") result = result.concat(publicDefines); return result } Export { Depends { name: "cpp" } - Depends { name: "qbsvariant" } cpp.defines: exportingProduct.publicDefines + cpp.includePaths: [exportingProduct.sourceDirectory] cpp.staticLibraries: { if (qbs.toolchainType === "gcc" && cpp.compilerVersionMajor < 9) return ["stdc++fs"]; diff --git a/src/shared/CMakeLists.txt b/src/shared/CMakeLists.txt index 226dd77ec..f01642b15 100644 --- a/src/shared/CMakeLists.txt +++ b/src/shared/CMakeLists.txt @@ -1,4 +1,4 @@ add_subdirectory(json) add_subdirectory(lsp) add_subdirectory(quickjs) -add_subdirectory(variant) +add_subdirectory(span) diff --git a/src/shared/quickjs/.clang-format b/src/shared/quickjs/.clang-format new file mode 100644 index 000000000..47a38a93f --- /dev/null +++ b/src/shared/quickjs/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: Never diff --git a/src/shared/quickjs/CMakeLists.txt b/src/shared/quickjs/CMakeLists.txt index a0af03c79..d88739ab0 100644 --- a/src/shared/quickjs/CMakeLists.txt +++ b/src/shared/quickjs/CMakeLists.txt @@ -2,6 +2,7 @@ add_qbs_library(qbsquickjs STATIC SOURCES cutils.c cutils.h + libbf.c libbf.h libregexp-opcode.h libregexp.c libregexp.h libunicode-table.h diff --git a/src/shared/quickjs/LICENSE b/src/shared/quickjs/LICENSE index 2c8fdebaf..2cf449dd3 100644 --- a/src/shared/quickjs/LICENSE +++ b/src/shared/quickjs/LICENSE @@ -1,5 +1,5 @@ QuickJS Javascript Engine - + Copyright (c) 2017-2021 Fabrice Bellard Copyright (c) 2017-2021 Charlie Gordon diff --git a/src/shared/quickjs/cutils.c b/src/shared/quickjs/cutils.c index 1f66fff3f..95ae5688d 100644 --- a/src/shared/quickjs/cutils.c +++ b/src/shared/quickjs/cutils.c @@ -1,6 +1,6 @@ /* * C utilities - * + * * Copyright (c) 2017 Fabrice Bellard * Copyright (c) 2018 Charlie Gordon * @@ -140,7 +140,7 @@ int dbuf_put(DynBuf *s, const uint8_t *data, size_t len) if (dbuf_realloc(s, s->size + len)) return -1; } - memcpy(s->buf + s->size, data, len); + memcpy_no_ub(s->buf + s->size, data, len); s->size += len; return 0; } @@ -171,7 +171,7 @@ 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); diff --git a/src/shared/quickjs/cutils.h b/src/shared/quickjs/cutils.h index ee0ce4a2e..265fd0aee 100644 --- a/src/shared/quickjs/cutils.h +++ b/src/shared/quickjs/cutils.h @@ -26,6 +26,7 @@ #define CUTILS_H #include <stdlib.h> +#include <string.h> #include <inttypes.h> #if defined(_MSC_VER) @@ -35,21 +36,18 @@ typedef SSIZE_T ssize_t; #include <sys/types.h> #endif -/* set if CPU is big endian */ -#undef WORDS_BIGENDIAN - #ifdef __GNUC__ #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #define force_inline inline __attribute__((always_inline)) #define no_inline __attribute__((noinline)) -#define maybe_unused __attribute__((unused)) +#define __maybe_unused __attribute__((unused)) #else #define likely(x) (x) #define unlikely(x) (x) #define force_inline #define no_inline -#define maybe_unused +#define __maybe_unused #endif #ifdef _MSC_VER @@ -67,6 +65,16 @@ typedef SSIZE_T ssize_t; #ifndef countof #define countof(x) (sizeof(x) / sizeof((x)[0])) #endif +#ifndef container_of +/* return the pointer of type 'type *' containing 'ptr' as field 'member' */ +#define container_of(ptr, type, member) ((type *)((uint8_t *)(ptr) - offsetof(type, member))) +#endif + +#if !defined(_MSC_VER) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define minimum_length(n) static n +#else +#define minimum_length(n) n +#endif typedef int BOOL; @@ -82,6 +90,12 @@ 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); +/* Prevent UB when n == 0 and (src == NULL or dest == NULL) */ +static inline void memcpy_no_ub(void *dest, const void *src, size_t n) { + if (n) + memcpy(dest, src, n); +} + static inline int max_int(int a, int b) { if (a > b) @@ -140,6 +154,36 @@ static inline int clz32(unsigned int a) #endif } +/* WARNING: undefined if a = 0 */ +static inline int clz64(uint64_t a) +{ +#ifdef _MSC_VER + return (int) __lzcnt64(a); +#else + return __builtin_clzll(a); +#endif +} + +/* WARNING: undefined if a = 0 */ +static inline int ctz32(unsigned int a) +{ +#ifdef _MSC_VER + return (int) _tzcnt_u32(a); +#else + return __builtin_ctz(a); +#endif +} + +/* WARNING: undefined if a = 0 */ +static inline int ctz64(uint64_t a) +{ +#ifdef _MSC_VER + return (int) _tzcnt_u64(a); +#else + return __builtin_ctzll(a); +#endif +} + #pragma pack(push, 1) struct packed_u64 { uint64_t v; @@ -212,17 +256,22 @@ static inline void put_u8(uint8_t *tab, uint8_t val) *tab = val; } +#ifndef bswap16 static inline uint16_t bswap16(uint16_t x) { return (x >> 8) | (x << 8); } +#endif +#ifndef bswap32 static inline uint32_t bswap32(uint32_t v) { return ((v & 0xff000000) >> 24) | ((v & 0x00ff0000) >> 8) | ((v & 0x0000ff00) << 8) | ((v & 0x000000ff) << 24); } +#endif +#ifndef bswap64 static inline uint64_t bswap64(uint64_t v) { return ((v & ((uint64_t)0xff << (7 * 8))) >> (7 * 8)) | @@ -234,6 +283,7 @@ static inline uint64_t bswap64(uint64_t v) ((v & ((uint64_t)0xff << (1 * 8))) << (5 * 8)) | ((v & ((uint64_t)0xff << (0 * 8))) << (7 * 8)); } +#endif /* XXX: should take an extra argument to pass slack information to the caller */ typedef void *DynBufReallocFunc(void *opaque, void *ptr, size_t size); @@ -290,6 +340,36 @@ static inline void dbuf_set_error(DynBuf *s) 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 BOOL is_surrogate(uint32_t c) +{ + return (c >> 11) == (0xD800 >> 11); // 0xD800-0xDFFF +} + +static inline BOOL is_hi_surrogate(uint32_t c) +{ + return (c >> 10) == (0xD800 >> 10); // 0xD800-0xDBFF +} + +static inline BOOL is_lo_surrogate(uint32_t c) +{ + return (c >> 10) == (0xDC00 >> 10); // 0xDC00-0xDFFF +} + +static inline uint32_t get_hi_surrogate(uint32_t c) +{ + return (c >> 10) - (0x10000 >> 10) + 0xD800; +} + +static inline uint32_t get_lo_surrogate(uint32_t c) +{ + return (c & 0x3FF) | 0xDC00; +} + +static inline uint32_t from_surrogate(uint32_t hi, uint32_t lo) +{ + return 0x10000 + 0x400 * (hi - 0xD800) + (lo - 0xDC00); +} + static inline int from_hex(int c) { if (c >= '0' && c <= '9') diff --git a/src/shared/quickjs/libbf.c b/src/shared/quickjs/libbf.c new file mode 100644 index 000000000..56b2be861 --- /dev/null +++ b/src/shared/quickjs/libbf.c @@ -0,0 +1,8475 @@ +/* + * Tiny arbitrary precision floating point library + * + * Copyright (c) 2017-2021 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include <stdlib.h> +#include <stdio.h> +#include <inttypes.h> +#include <math.h> +#include <string.h> +#include <assert.h> + +#ifdef __AVX2__ +#include <immintrin.h> +#endif + +#include "cutils.h" +#include "libbf.h" + +/* enable it to check the multiplication result */ +//#define USE_MUL_CHECK +/* enable it to use FFT/NTT multiplication */ +#define USE_FFT_MUL +/* enable decimal floating point support */ +#define USE_BF_DEC + +//#define inline __attribute__((always_inline)) + +#ifdef __AVX2__ +#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */ +#else +#define FFT_MUL_THRESHOLD 100 /* in limbs of the smallest factor */ +#endif + +/* XXX: adjust */ +#define DIVNORM_LARGE_THRESHOLD 50 +#define UDIV1NORM_THRESHOLD 3 + +#if LIMB_BITS == 64 +#define FMT_LIMB1 "%" PRIx64 +#define FMT_LIMB "%016" PRIx64 +#define PRId_LIMB PRId64 +#define PRIu_LIMB PRIu64 + +#else + +#define FMT_LIMB1 "%x" +#define FMT_LIMB "%08x" +#define PRId_LIMB "d" +#define PRIu_LIMB "u" + +#endif + +typedef intptr_t mp_size_t; + +typedef int bf_op2_func_t(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags); + +#ifdef USE_FFT_MUL + +#define FFT_MUL_R_OVERLAP_A (1 << 0) +#define FFT_MUL_R_OVERLAP_B (1 << 1) +#define FFT_MUL_R_NORESIZE (1 << 2) + +static no_inline int fft_mul(bf_context_t *s, + bf_t *res, limb_t *a_tab, limb_t a_len, + limb_t *b_tab, limb_t b_len, int mul_flags); +static void fft_clear_cache(bf_context_t *s); +#endif +#ifdef USE_BF_DEC +static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos); +#endif + + +/* could leading zeros */ +static inline int clz(limb_t a) +{ + if (a == 0) { + return LIMB_BITS; + } else { +#if LIMB_BITS == 64 + return clz64(a); +#else + return clz32(a); +#endif + } +} + +static inline int ctz(limb_t a) +{ + if (a == 0) { + return LIMB_BITS; + } else { +#if LIMB_BITS == 64 + return ctz64(a); +#else + return ctz32(a); +#endif + } +} + +static inline int ceil_log2(limb_t a) +{ + if (a <= 1) + return 0; + else + return LIMB_BITS - clz(a - 1); +} + +/* b must be >= 1 */ +static inline slimb_t ceil_div(slimb_t a, slimb_t b) +{ + if (a >= 0) + return (a + b - 1) / b; + else + return a / b; +} + +#ifdef USE_BF_DEC +/* b must be >= 1 */ +static inline slimb_t floor_div(slimb_t a, slimb_t b) +{ + if (a >= 0) { + return a / b; + } else { + return (a - b + 1) / b; + } +} +#endif + +/* return r = a modulo b (0 <= r <= b - 1. b must be >= 1 */ +static inline limb_t smod(slimb_t a, slimb_t b) +{ + a = a % (slimb_t)b; + if (a < 0) + a += b; + return a; +} + +/* signed addition with saturation */ +static inline slimb_t sat_add(slimb_t a, slimb_t b) +{ + slimb_t r; + r = a + b; + /* overflow ? */ + if (((a ^ r) & (b ^ r)) < 0) + r = (a >> (LIMB_BITS - 1)) ^ (((limb_t)1 << (LIMB_BITS - 1)) - 1); + return r; +} + +static inline __maybe_unused limb_t shrd(limb_t low, limb_t high, long shift) +{ + if (shift != 0) + low = (low >> shift) | (high << (LIMB_BITS - shift)); + return low; +} + +static inline __maybe_unused limb_t shld(limb_t a1, limb_t a0, long shift) +{ + if (shift != 0) + return (a1 << shift) | (a0 >> (LIMB_BITS - shift)); + else + return a1; +} + +#define malloc(s) malloc_is_forbidden(s) +#define free(p) free_is_forbidden(p) +#define realloc(p, s) realloc_is_forbidden(p, s) + +void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func, + void *realloc_opaque) +{ + memset(s, 0, sizeof(*s)); + s->realloc_func = realloc_func; + s->realloc_opaque = realloc_opaque; +} + +void bf_context_end(bf_context_t *s) +{ + bf_clear_cache(s); +} + +void bf_init(bf_context_t *s, bf_t *r) +{ + r->ctx = s; + r->sign = 0; + r->expn = BF_EXP_ZERO; + r->len = 0; + r->tab = NULL; +} + +/* return 0 if OK, -1 if alloc error */ +int bf_resize(bf_t *r, limb_t len) +{ + limb_t *tab; + + if (len != r->len) { + tab = bf_realloc(r->ctx, r->tab, len * sizeof(limb_t)); + if (!tab && len != 0) + return -1; + r->tab = tab; + r->len = len; + } + return 0; +} + +/* return 0 or BF_ST_MEM_ERROR */ +int bf_set_ui(bf_t *r, uint64_t a) +{ + r->sign = 0; + if (a == 0) { + r->expn = BF_EXP_ZERO; + bf_resize(r, 0); /* cannot fail */ + } +#if LIMB_BITS == 32 + else if (a <= 0xffffffff) +#else + else +#endif + { + int shift; + if (bf_resize(r, 1)) + goto fail; + shift = clz(a); + r->tab[0] = a << shift; + r->expn = LIMB_BITS - shift; + } +#if LIMB_BITS == 32 + else { + uint32_t a1, a0; + int shift; + if (bf_resize(r, 2)) + goto fail; + a0 = a; + a1 = a >> 32; + shift = clz(a1); + r->tab[0] = a0 << shift; + r->tab[1] = shld(a1, a0, shift); + r->expn = 2 * LIMB_BITS - shift; + } +#endif + return 0; + fail: + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +/* return 0 or BF_ST_MEM_ERROR */ +int bf_set_si(bf_t *r, int64_t a) +{ + int ret; + + if (a < 0) { + ret = bf_set_ui(r, -a); + r->sign = 1; + } else { + ret = bf_set_ui(r, a); + } + return ret; +} + +void bf_set_nan(bf_t *r) +{ + bf_resize(r, 0); /* cannot fail */ + r->expn = BF_EXP_NAN; + r->sign = 0; +} + +void bf_set_zero(bf_t *r, int is_neg) +{ + bf_resize(r, 0); /* cannot fail */ + r->expn = BF_EXP_ZERO; + r->sign = is_neg; +} + +void bf_set_inf(bf_t *r, int is_neg) +{ + bf_resize(r, 0); /* cannot fail */ + r->expn = BF_EXP_INF; + r->sign = is_neg; +} + +/* return 0 or BF_ST_MEM_ERROR */ +int bf_set(bf_t *r, const bf_t *a) +{ + if (r == a) + return 0; + if (bf_resize(r, a->len)) { + bf_set_nan(r); + return BF_ST_MEM_ERROR; + } + r->sign = a->sign; + r->expn = a->expn; + memcpy_no_ub(r->tab, a->tab, a->len * sizeof(limb_t)); + return 0; +} + +/* equivalent to bf_set(r, a); bf_delete(a) */ +void bf_move(bf_t *r, bf_t *a) +{ + bf_context_t *s = r->ctx; + if (r == a) + return; + bf_free(s, r->tab); + *r = *a; +} + +static limb_t get_limbz(const bf_t *a, limb_t idx) +{ + if (idx >= a->len) + return 0; + else + return a->tab[idx]; +} + +/* get LIMB_BITS at bit position 'pos' in tab */ +static inline limb_t get_bits(const limb_t *tab, limb_t len, slimb_t pos) +{ + limb_t i, a0, a1; + int p; + + i = pos >> LIMB_LOG2_BITS; + p = pos & (LIMB_BITS - 1); + if (i < len) + a0 = tab[i]; + else + a0 = 0; + if (p == 0) { + return a0; + } else { + i++; + if (i < len) + a1 = tab[i]; + else + a1 = 0; + return (a0 >> p) | (a1 << (LIMB_BITS - p)); + } +} + +static inline limb_t get_bit(const limb_t *tab, limb_t len, slimb_t pos) +{ + slimb_t i; + i = pos >> LIMB_LOG2_BITS; + if (i < 0 || i >= len) + return 0; + return (tab[i] >> (pos & (LIMB_BITS - 1))) & 1; +} + +static inline limb_t limb_mask(int start, int last) +{ + limb_t v; + int n; + n = last - start + 1; + if (n == LIMB_BITS) + v = -1; + else + v = (((limb_t)1 << n) - 1) << start; + return v; +} + +static limb_t mp_scan_nz(const limb_t *tab, mp_size_t n) +{ + mp_size_t i; + for(i = 0; i < n; i++) { + if (tab[i] != 0) + return 1; + } + return 0; +} + +/* return != 0 if one bit between 0 and bit_pos inclusive is not zero. */ +static inline limb_t scan_bit_nz(const bf_t *r, slimb_t bit_pos) +{ + slimb_t pos; + limb_t v; + + pos = bit_pos >> LIMB_LOG2_BITS; + if (pos < 0) + return 0; + v = r->tab[pos] & limb_mask(0, bit_pos & (LIMB_BITS - 1)); + if (v != 0) + return 1; + pos--; + while (pos >= 0) { + if (r->tab[pos] != 0) + return 1; + pos--; + } + return 0; +} + +/* return the addend for rounding. Note that prec can be <= 0 (for + BF_FLAG_RADPNT_PREC) */ +static int bf_get_rnd_add(int *pret, const bf_t *r, limb_t l, + slimb_t prec, int rnd_mode) +{ + int add_one, inexact; + limb_t bit1, bit0; + + if (rnd_mode == BF_RNDF) { + bit0 = 1; /* faithful rounding does not honor the INEXACT flag */ + } else { + /* starting limb for bit 'prec + 1' */ + bit0 = scan_bit_nz(r, l * LIMB_BITS - 1 - bf_max(0, prec + 1)); + } + + /* get the bit at 'prec' */ + bit1 = get_bit(r->tab, l, l * LIMB_BITS - 1 - prec); + inexact = (bit1 | bit0) != 0; + + add_one = 0; + switch(rnd_mode) { + case BF_RNDZ: + break; + case BF_RNDN: + if (bit1) { + if (bit0) { + add_one = 1; + } else { + /* round to even */ + add_one = + get_bit(r->tab, l, l * LIMB_BITS - 1 - (prec - 1)); + } + } + break; + case BF_RNDD: + case BF_RNDU: + if (r->sign == (rnd_mode == BF_RNDD)) + add_one = inexact; + break; + case BF_RNDA: + add_one = inexact; + break; + case BF_RNDNA: + case BF_RNDF: + add_one = bit1; + break; + default: + abort(); + } + + if (inexact) + *pret |= BF_ST_INEXACT; + return add_one; +} + +static int bf_set_overflow(bf_t *r, int sign, limb_t prec, bf_flags_t flags) +{ + slimb_t i, l, e_max; + int rnd_mode; + + rnd_mode = flags & BF_RND_MASK; + if (prec == BF_PREC_INF || + rnd_mode == BF_RNDN || + rnd_mode == BF_RNDNA || + rnd_mode == BF_RNDA || + (rnd_mode == BF_RNDD && sign == 1) || + (rnd_mode == BF_RNDU && sign == 0)) { + bf_set_inf(r, sign); + } else { + /* set to maximum finite number */ + l = (prec + LIMB_BITS - 1) / LIMB_BITS; + if (bf_resize(r, l)) { + bf_set_nan(r); + return BF_ST_MEM_ERROR; + } + r->tab[0] = limb_mask((-prec) & (LIMB_BITS - 1), + LIMB_BITS - 1); + for(i = 1; i < l; i++) + r->tab[i] = (limb_t)-1; + e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1); + r->expn = e_max; + r->sign = sign; + } + return BF_ST_OVERFLOW | BF_ST_INEXACT; +} + +/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is + assumed to have length 'l' (1 <= l <= r->len). Note: 'prec1' can be + infinite (BF_PREC_INF). 'ret' is 0 or BF_ST_INEXACT if the result + is known to be inexact. Can fail with BF_ST_MEM_ERROR in case of + overflow not returning infinity. */ +static int __bf_round(bf_t *r, limb_t prec1, bf_flags_t flags, limb_t l, + int ret) +{ + limb_t v, a; + int shift, add_one, rnd_mode; + slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec; + + /* e_min and e_max are computed to match the IEEE 754 conventions */ + e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1); + e_min = -e_range + 3; + e_max = e_range; + + if (flags & BF_FLAG_RADPNT_PREC) { + /* 'prec' is the precision after the radix point */ + if (prec1 != BF_PREC_INF) + prec = r->expn + prec1; + else + prec = prec1; + } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) { + /* restrict the precision in case of potentially subnormal + result */ + assert(prec1 != BF_PREC_INF); + prec = prec1 - (e_min - r->expn); + } else { + prec = prec1; + } + + /* round to prec bits */ + rnd_mode = flags & BF_RND_MASK; + add_one = bf_get_rnd_add(&ret, r, l, prec, rnd_mode); + + if (prec <= 0) { + if (add_one) { + bf_resize(r, 1); /* cannot fail */ + r->tab[0] = (limb_t)1 << (LIMB_BITS - 1); + r->expn += 1 - prec; + ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; + return ret; + } else { + goto underflow; + } + } else if (add_one) { + limb_t carry; + + /* add one starting at digit 'prec - 1' */ + bit_pos = l * LIMB_BITS - 1 - (prec - 1); + pos = bit_pos >> LIMB_LOG2_BITS; + carry = (limb_t)1 << (bit_pos & (LIMB_BITS - 1)); + + for(i = pos; i < l; i++) { + v = r->tab[i] + carry; + carry = (v < carry); + r->tab[i] = v; + if (carry == 0) + break; + } + if (carry) { + /* shift right by one digit */ + v = 1; + for(i = l - 1; i >= pos; i--) { + a = r->tab[i]; + r->tab[i] = (a >> 1) | (v << (LIMB_BITS - 1)); + v = a; + } + r->expn++; + } + } + + /* check underflow */ + if (unlikely(r->expn < e_min)) { + if (flags & BF_FLAG_SUBNORMAL) { + /* if inexact, also set the underflow flag */ + if (ret & BF_ST_INEXACT) + ret |= BF_ST_UNDERFLOW; + } else { + underflow: + ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; + bf_set_zero(r, r->sign); + return ret; + } + } + + /* check overflow */ + if (unlikely(r->expn > e_max)) + return bf_set_overflow(r, r->sign, prec1, flags); + + /* keep the bits starting at 'prec - 1' */ + bit_pos = l * LIMB_BITS - 1 - (prec - 1); + i = bit_pos >> LIMB_LOG2_BITS; + if (i >= 0) { + shift = bit_pos & (LIMB_BITS - 1); + if (shift != 0) + r->tab[i] &= limb_mask(shift, LIMB_BITS - 1); + } else { + i = 0; + } + /* remove trailing zeros */ + while (r->tab[i] == 0) + i++; + if (i > 0) { + l -= i; + memmove(r->tab, r->tab + i, l * sizeof(limb_t)); + } + bf_resize(r, l); /* cannot fail */ + return ret; +} + +/* 'r' must be a finite number. */ +int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags) +{ + limb_t l, v, a; + int shift, ret; + slimb_t i; + + // bf_print_str("bf_renorm", r); + l = r->len; + while (l > 0 && r->tab[l - 1] == 0) + l--; + if (l == 0) { + /* zero */ + r->expn = BF_EXP_ZERO; + bf_resize(r, 0); /* cannot fail */ + ret = 0; + } else { + r->expn -= (r->len - l) * LIMB_BITS; + /* shift to have the MSB set to '1' */ + v = r->tab[l - 1]; + shift = clz(v); + if (shift != 0) { + v = 0; + for(i = 0; i < l; i++) { + a = r->tab[i]; + r->tab[i] = (a << shift) | (v >> (LIMB_BITS - shift)); + v = a; + } + r->expn -= shift; + } + ret = __bf_round(r, prec1, flags, l, 0); + } + // bf_print_str("r_final", r); + return ret; +} + +/* return true if rounding can be done at precision 'prec' assuming + the exact result r is such that |r-a| <= 2^(EXP(a)-k). */ +/* XXX: check the case where the exponent would be incremented by the + rounding */ +int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k) +{ + BOOL is_rndn; + slimb_t bit_pos, n; + limb_t bit; + + if (a->expn == BF_EXP_INF || a->expn == BF_EXP_NAN) + return FALSE; + if (rnd_mode == BF_RNDF) { + return (k >= (prec + 1)); + } + if (a->expn == BF_EXP_ZERO) + return FALSE; + is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); + if (k < (prec + 2)) + return FALSE; + bit_pos = a->len * LIMB_BITS - 1 - prec; + n = k - prec; + /* bit pattern for RNDN or RNDNA: 0111.. or 1000... + for other rounding modes: 000... or 111... + */ + bit = get_bit(a->tab, a->len, bit_pos); + bit_pos--; + n--; + bit ^= is_rndn; + /* XXX: slow, but a few iterations on average */ + while (n != 0) { + if (get_bit(a->tab, a->len, bit_pos) != bit) + return TRUE; + bit_pos--; + n--; + } + return FALSE; +} + +/* Cannot fail with BF_ST_MEM_ERROR. */ +int bf_round(bf_t *r, limb_t prec, bf_flags_t flags) +{ + if (r->len == 0) + return 0; + return __bf_round(r, prec, flags, r->len, 0); +} + +/* for debugging */ +static __maybe_unused void dump_limbs(const char *str, const limb_t *tab, limb_t n) +{ + limb_t i; + printf("%s: len=%" PRId_LIMB "\n", str, n); + for(i = 0; i < n; i++) { + printf("%" PRId_LIMB ": " FMT_LIMB "\n", + i, tab[i]); + } +} + +void mp_print_str(const char *str, const limb_t *tab, limb_t n) +{ + slimb_t i; + printf("%s= 0x", str); + for(i = n - 1; i >= 0; i--) { + if (i != (n - 1)) + printf("_"); + printf(FMT_LIMB, tab[i]); + } + printf("\n"); +} + +static __maybe_unused void mp_print_str_h(const char *str, + const limb_t *tab, limb_t n, + limb_t high) +{ + slimb_t i; + printf("%s= 0x", str); + printf(FMT_LIMB, high); + for(i = n - 1; i >= 0; i--) { + printf("_"); + printf(FMT_LIMB, tab[i]); + } + printf("\n"); +} + +/* for debugging */ +void bf_print_str(const char *str, const bf_t *a) +{ + slimb_t i; + printf("%s=", str); + + if (a->expn == BF_EXP_NAN) { + printf("NaN"); + } else { + if (a->sign) + putchar('-'); + if (a->expn == BF_EXP_ZERO) { + putchar('0'); + } else if (a->expn == BF_EXP_INF) { + printf("Inf"); + } else { + printf("0x0."); + for(i = a->len - 1; i >= 0; i--) + printf(FMT_LIMB, a->tab[i]); + printf("p%" PRId_LIMB, a->expn); + } + } + printf("\n"); +} + +/* compare the absolute value of 'a' and 'b'. Return < 0 if a < b, 0 + if a = b and > 0 otherwise. */ +int bf_cmpu(const bf_t *a, const bf_t *b) +{ + slimb_t i; + limb_t len, v1, v2; + + if (a->expn != b->expn) { + if (a->expn < b->expn) + return -1; + else + return 1; + } + len = bf_max(a->len, b->len); + for(i = len - 1; i >= 0; i--) { + v1 = get_limbz(a, a->len - len + i); + v2 = get_limbz(b, b->len - len + i); + if (v1 != v2) { + if (v1 < v2) + return -1; + else + return 1; + } + } + return 0; +} + +/* Full order: -0 < 0, NaN == NaN and NaN is larger than all other numbers */ +int bf_cmp_full(const bf_t *a, const bf_t *b) +{ + int res; + + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + if (a->expn == b->expn) + res = 0; + else if (a->expn == BF_EXP_NAN) + res = 1; + else + res = -1; + } else if (a->sign != b->sign) { + res = 1 - 2 * a->sign; + } else { + res = bf_cmpu(a, b); + if (a->sign) + res = -res; + } + return res; +} + +/* Standard floating point comparison: return 2 if one of the operands + is NaN (unordered) or -1, 0, 1 depending on the ordering assuming + -0 == +0 */ +int bf_cmp(const bf_t *a, const bf_t *b) +{ + int res; + + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + res = 2; + } else if (a->sign != b->sign) { + if (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_ZERO) + res = 0; + else + res = 1 - 2 * a->sign; + } else { + res = bf_cmpu(a, b); + if (a->sign) + res = -res; + } + return res; +} + +/* Compute the number of bits 'n' matching the pattern: + a= X1000..0 + b= X0111..1 + + When computing a-b, the result will have at least n leading zero + bits. + + Precondition: a > b and a.expn - b.expn = 0 or 1 +*/ +static limb_t count_cancelled_bits(const bf_t *a, const bf_t *b) +{ + slimb_t bit_offset, b_offset, n; + int p, p1; + limb_t v1, v2, mask; + + bit_offset = a->len * LIMB_BITS - 1; + b_offset = (b->len - a->len) * LIMB_BITS - (LIMB_BITS - 1) + + a->expn - b->expn; + n = 0; + + /* first search the equals bits */ + for(;;) { + v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS); + v2 = get_bits(b->tab, b->len, bit_offset + b_offset); + // printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2); + if (v1 != v2) + break; + n += LIMB_BITS; + bit_offset -= LIMB_BITS; + } + /* find the position of the first different bit */ + p = clz(v1 ^ v2) + 1; + n += p; + /* then search for '0' in a and '1' in b */ + p = LIMB_BITS - p; + if (p > 0) { + /* search in the trailing p bits of v1 and v2 */ + mask = limb_mask(0, p - 1); + p1 = bf_min(clz(v1 & mask), clz((~v2) & mask)) - (LIMB_BITS - p); + n += p1; + if (p1 != p) + goto done; + } + bit_offset -= LIMB_BITS; + for(;;) { + v1 = get_limbz(a, bit_offset >> LIMB_LOG2_BITS); + v2 = get_bits(b->tab, b->len, bit_offset + b_offset); + // printf("v1=" FMT_LIMB " v2=" FMT_LIMB "\n", v1, v2); + if (v1 != 0 || v2 != -1) { + /* different: count the matching bits */ + p1 = bf_min(clz(v1), clz(~v2)); + n += p1; + break; + } + n += LIMB_BITS; + bit_offset -= LIMB_BITS; + } + done: + return n; +} + +static int bf_add_internal(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int b_neg) +{ + const bf_t *tmp; + int is_sub, ret, cmp_res, a_sign, b_sign; + + a_sign = a->sign; + b_sign = b->sign ^ b_neg; + is_sub = a_sign ^ b_sign; + cmp_res = bf_cmpu(a, b); + if (cmp_res < 0) { + tmp = a; + a = b; + b = tmp; + a_sign = b_sign; /* b_sign is never used later */ + } + /* abs(a) >= abs(b) */ + if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) { + /* zero result */ + bf_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD); + ret = 0; + } else if (a->len == 0 || b->len == 0) { + ret = 0; + if (a->expn >= BF_EXP_INF) { + if (a->expn == BF_EXP_NAN) { + /* at least one operand is NaN */ + bf_set_nan(r); + } else if (b->expn == BF_EXP_INF && is_sub) { + /* infinities with different signs */ + bf_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + bf_set_inf(r, a_sign); + } + } else { + /* at least one zero and not subtract */ + bf_set(r, a); + r->sign = a_sign; + goto renorm; + } + } else { + slimb_t d, a_offset, b_bit_offset, i, cancelled_bits; + limb_t carry, v1, v2, u, r_len, carry1, precl, tot_len, z, sub_mask; + + r->sign = a_sign; + r->expn = a->expn; + d = a->expn - b->expn; + /* must add more precision for the leading cancelled bits in + subtraction */ + if (is_sub) { + if (d <= 1) + cancelled_bits = count_cancelled_bits(a, b); + else + cancelled_bits = 1; + } else { + cancelled_bits = 0; + } + + /* add two extra bits for rounding */ + precl = (cancelled_bits + prec + 2 + LIMB_BITS - 1) / LIMB_BITS; + tot_len = bf_max(a->len, b->len + (d + LIMB_BITS - 1) / LIMB_BITS); + r_len = bf_min(precl, tot_len); + if (bf_resize(r, r_len)) + goto fail; + a_offset = a->len - r_len; + b_bit_offset = (b->len - r_len) * LIMB_BITS + d; + + /* compute the bits before for the rounding */ + carry = is_sub; + z = 0; + sub_mask = -is_sub; + i = r_len - tot_len; + while (i < 0) { + slimb_t ap, bp; + BOOL inflag; + + ap = a_offset + i; + bp = b_bit_offset + i * LIMB_BITS; + inflag = FALSE; + if (ap >= 0 && ap < a->len) { + v1 = a->tab[ap]; + inflag = TRUE; + } else { + v1 = 0; + } + if (bp + LIMB_BITS > 0 && bp < (slimb_t)(b->len * LIMB_BITS)) { + v2 = get_bits(b->tab, b->len, bp); + inflag = TRUE; + } else { + v2 = 0; + } + if (!inflag) { + /* outside 'a' and 'b': go directly to the next value + inside a or b so that the running time does not + depend on the exponent difference */ + i = 0; + if (ap < 0) + i = bf_min(i, -a_offset); + /* b_bit_offset + i * LIMB_BITS + LIMB_BITS >= 1 + equivalent to + i >= ceil(-b_bit_offset + 1 - LIMB_BITS) / LIMB_BITS) + */ + if (bp + LIMB_BITS <= 0) + i = bf_min(i, (-b_bit_offset) >> LIMB_LOG2_BITS); + } else { + i++; + } + v2 ^= sub_mask; + u = v1 + v2; + carry1 = u < v1; + u += carry; + carry = (u < carry) | carry1; + z |= u; + } + /* and the result */ + for(i = 0; i < r_len; i++) { + v1 = get_limbz(a, a_offset + i); + v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS); + v2 ^= sub_mask; + u = v1 + v2; + carry1 = u < v1; + u += carry; + carry = (u < carry) | carry1; + r->tab[i] = u; + } + /* set the extra bits for the rounding */ + r->tab[0] |= (z != 0); + + /* carry is only possible in add case */ + if (!is_sub && carry) { + if (bf_resize(r, r_len + 1)) + goto fail; + r->tab[r_len] = 1; + r->expn += LIMB_BITS; + } + renorm: + ret = bf_normalize_and_round(r, prec, flags); + } + return ret; + fail: + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +static int __bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_add_internal(r, a, b, prec, flags, 0); +} + +static int __bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_add_internal(r, a, b, prec, flags, 1); +} + +limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2, + limb_t n, limb_t carry) +{ + slimb_t i; + limb_t k, a, v, k1; + + k = carry; + for(i=0;i<n;i++) { + v = op1[i]; + a = v + op2[i]; + k1 = a < v; + a = a + k; + k = (a < k) | k1; + res[i] = a; + } + return k; +} + +limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n) +{ + size_t i; + limb_t k, a; + + k=b; + for(i=0;i<n;i++) { + if (k == 0) + break; + a = tab[i] + k; + k = (a < k); + tab[i] = a; + } + return k; +} + +limb_t mp_sub(limb_t *res, const limb_t *op1, const limb_t *op2, + mp_size_t n, limb_t carry) +{ + int i; + limb_t k, a, v, k1; + + k = carry; + for(i=0;i<n;i++) { + v = op1[i]; + a = v - op2[i]; + k1 = a > v; + v = a - k; + k = (v > a) | k1; + res[i] = v; + } + return k; +} + +/* compute 0 - op2 */ +static limb_t mp_neg(limb_t *res, const limb_t *op2, mp_size_t n, limb_t carry) +{ + int i; + limb_t k, a, v, k1; + + k = carry; + for(i=0;i<n;i++) { + v = 0; + a = v - op2[i]; + k1 = a > v; + v = a - k; + k = (v > a) | k1; + res[i] = v; + } + return k; +} + +limb_t mp_sub_ui(limb_t *tab, limb_t b, mp_size_t n) +{ + mp_size_t i; + limb_t k, a, v; + + k=b; + for(i=0;i<n;i++) { + v = tab[i]; + a = v - k; + k = a > v; + tab[i] = a; + if (k == 0) + break; + } + return k; +} + +/* r = (a + high*B^n) >> shift. Return the remainder r (0 <= r < 2^shift). + 1 <= shift <= LIMB_BITS - 1 */ +static limb_t mp_shr(limb_t *tab_r, const limb_t *tab, mp_size_t n, + int shift, limb_t high) +{ + mp_size_t i; + limb_t l, a; + + assert(shift >= 1 && shift < LIMB_BITS); + l = high; + for(i = n - 1; i >= 0; i--) { + a = tab[i]; + tab_r[i] = (a >> shift) | (l << (LIMB_BITS - shift)); + l = a; + } + return l & (((limb_t)1 << shift) - 1); +} + +/* tabr[] = taba[] * b + l. Return the high carry */ +static limb_t mp_mul1(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b, limb_t l) +{ + limb_t i; + dlimb_t t; + + for(i = 0; i < n; i++) { + t = (dlimb_t)taba[i] * (dlimb_t)b + l; + tabr[i] = t; + l = t >> LIMB_BITS; + } + return l; +} + +/* tabr[] += taba[] * b, return the high word. */ +static limb_t mp_add_mul1(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b) +{ + limb_t i, l; + dlimb_t t; + + l = 0; + for(i = 0; i < n; i++) { + t = (dlimb_t)taba[i] * (dlimb_t)b + l + tabr[i]; + tabr[i] = t; + l = t >> LIMB_BITS; + } + return l; +} + +/* size of the result : op1_size + op2_size. */ +static void mp_mul_basecase(limb_t *result, + const limb_t *op1, limb_t op1_size, + const limb_t *op2, limb_t op2_size) +{ + limb_t i, r; + + result[op1_size] = mp_mul1(result, op1, op1_size, op2[0], 0); + for(i=1;i<op2_size;i++) { + r = mp_add_mul1(result + i, op1, op1_size, op2[i]); + result[i + op1_size] = r; + } +} + +/* return 0 if OK, -1 if memory error */ +/* XXX: change API so that result can be allocated */ +int mp_mul(bf_context_t *s, limb_t *result, + const limb_t *op1, limb_t op1_size, + const limb_t *op2, limb_t op2_size) +{ +#ifdef USE_FFT_MUL + if (unlikely(bf_min(op1_size, op2_size) >= FFT_MUL_THRESHOLD)) { + bf_t r_s, *r = &r_s; + r->tab = result; + /* XXX: optimize memory usage in API */ + if (fft_mul(s, r, (limb_t *)op1, op1_size, + (limb_t *)op2, op2_size, FFT_MUL_R_NORESIZE)) + return -1; + } else +#endif + { + mp_mul_basecase(result, op1, op1_size, op2, op2_size); + } + return 0; +} + +/* tabr[] -= taba[] * b. Return the value to substract to the high + word. */ +static limb_t mp_sub_mul1(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b) +{ + limb_t i, l; + dlimb_t t; + + l = 0; + for(i = 0; i < n; i++) { + t = tabr[i] - (dlimb_t)taba[i] * (dlimb_t)b - l; + tabr[i] = t; + l = -(t >> LIMB_BITS); + } + return l; +} + +/* WARNING: d must be >= 2^(LIMB_BITS-1) */ +static inline limb_t udiv1norm_init(limb_t d) +{ + limb_t a0, a1; + a1 = -d - 1; + a0 = -1; + return (((dlimb_t)a1 << LIMB_BITS) | a0) / d; +} + +/* return the quotient and the remainder in '*pr'of 'a1*2^LIMB_BITS+a0 + / d' with 0 <= a1 < d. */ +static inline limb_t udiv1norm(limb_t *pr, limb_t a1, limb_t a0, + limb_t d, limb_t d_inv) +{ + limb_t n1m, n_adj, q, r, ah; + dlimb_t a; + n1m = ((slimb_t)a0 >> (LIMB_BITS - 1)); + n_adj = a0 + (n1m & d); + a = (dlimb_t)d_inv * (a1 - n1m) + n_adj; + q = (a >> LIMB_BITS) + a1; + /* compute a - q * r and update q so that the remainder is\ + between 0 and d - 1 */ + a = ((dlimb_t)a1 << LIMB_BITS) | a0; + a = a - (dlimb_t)q * d - d; + ah = a >> LIMB_BITS; + q += 1 + ah; + r = (limb_t)a + (ah & d); + *pr = r; + return q; +} + +/* b must be >= 1 << (LIMB_BITS - 1) */ +static limb_t mp_div1norm(limb_t *tabr, const limb_t *taba, limb_t n, + limb_t b, limb_t r) +{ + slimb_t i; + + if (n >= UDIV1NORM_THRESHOLD) { + limb_t b_inv; + b_inv = udiv1norm_init(b); + for(i = n - 1; i >= 0; i--) { + tabr[i] = udiv1norm(&r, r, taba[i], b, b_inv); + } + } else { + dlimb_t a1; + for(i = n - 1; i >= 0; i--) { + a1 = ((dlimb_t)r << LIMB_BITS) | taba[i]; + tabr[i] = a1 / b; + r = a1 % b; + } + } + return r; +} + +static int mp_divnorm_large(bf_context_t *s, + limb_t *tabq, limb_t *taba, limb_t na, + const limb_t *tabb, limb_t nb); + +/* base case division: divides taba[0..na-1] by tabb[0..nb-1]. tabb[nb + - 1] must be >= 1 << (LIMB_BITS - 1). na - nb must be >= 0. 'taba' + is modified and contains the remainder (nb limbs). tabq[0..na-nb] + contains the quotient with tabq[na - nb] <= 1. */ +static int mp_divnorm(bf_context_t *s, limb_t *tabq, limb_t *taba, limb_t na, + const limb_t *tabb, limb_t nb) +{ + limb_t r, a, c, q, v, b1, b1_inv, n, dummy_r; + slimb_t i, j; + + b1 = tabb[nb - 1]; + if (nb == 1) { + taba[0] = mp_div1norm(tabq, taba, na, b1, 0); + return 0; + } + n = na - nb; + if (bf_min(n, nb) >= DIVNORM_LARGE_THRESHOLD) { + return mp_divnorm_large(s, tabq, taba, na, tabb, nb); + } + + if (n >= UDIV1NORM_THRESHOLD) + b1_inv = udiv1norm_init(b1); + else + b1_inv = 0; + + /* first iteration: the quotient is only 0 or 1 */ + q = 1; + for(j = nb - 1; j >= 0; j--) { + if (taba[n + j] != tabb[j]) { + if (taba[n + j] < tabb[j]) + q = 0; + break; + } + } + tabq[n] = q; + if (q) { + mp_sub(taba + n, taba + n, tabb, nb, 0); + } + + for(i = n - 1; i >= 0; i--) { + if (unlikely(taba[i + nb] >= b1)) { + q = -1; + } else if (b1_inv) { + q = udiv1norm(&dummy_r, taba[i + nb], taba[i + nb - 1], b1, b1_inv); + } else { + dlimb_t al; + al = ((dlimb_t)taba[i + nb] << LIMB_BITS) | taba[i + nb - 1]; + q = al / b1; + r = al % b1; + } + r = mp_sub_mul1(taba + i, tabb, nb, q); + + v = taba[i + nb]; + a = v - r; + c = (a > v); + taba[i + nb] = a; + + if (c != 0) { + /* negative result */ + for(;;) { + q--; + c = mp_add(taba + i, taba + i, tabb, nb, 0); + /* propagate carry and test if positive result */ + if (c != 0) { + if (++taba[i + nb] == 0) { + break; + } + } + } + } + tabq[i] = q; + } + return 0; +} + +/* compute r=B^(2*n)/a such as a*r < B^(2*n) < a*r + 2 with n >= 1. 'a' + has n limbs with a[n-1] >= B/2 and 'r' has n+1 limbs with r[n] = 1. + + See Modern Computer Arithmetic by Richard P. Brent and Paul + Zimmermann, algorithm 3.5 */ +int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n) +{ + mp_size_t l, h, k, i; + limb_t *tabxh, *tabt, c, *tabu; + + if (n <= 2) { + /* return ceil(B^(2*n)/a) - 1 */ + /* XXX: could avoid allocation */ + tabu = bf_malloc(s, sizeof(limb_t) * (2 * n + 1)); + tabt = bf_malloc(s, sizeof(limb_t) * (n + 2)); + if (!tabt || !tabu) + goto fail; + for(i = 0; i < 2 * n; i++) + tabu[i] = 0; + tabu[2 * n] = 1; + if (mp_divnorm(s, tabt, tabu, 2 * n + 1, taba, n)) + goto fail; + for(i = 0; i < n + 1; i++) + tabr[i] = tabt[i]; + if (mp_scan_nz(tabu, n) == 0) { + /* only happens for a=B^n/2 */ + mp_sub_ui(tabr, 1, n + 1); + } + } else { + l = (n - 1) / 2; + h = n - l; + /* n=2p -> l=p-1, h = p + 1, k = p + 3 + n=2p+1-> l=p, h = p + 1; k = p + 2 + */ + tabt = bf_malloc(s, sizeof(limb_t) * (n + h + 1)); + tabu = bf_malloc(s, sizeof(limb_t) * (n + 2 * h - l + 2)); + if (!tabt || !tabu) + goto fail; + tabxh = tabr + l; + if (mp_recip(s, tabxh, taba + l, h)) + goto fail; + if (mp_mul(s, tabt, taba, n, tabxh, h + 1)) /* n + h + 1 limbs */ + goto fail; + while (tabt[n + h] != 0) { + mp_sub_ui(tabxh, 1, h + 1); + c = mp_sub(tabt, tabt, taba, n, 0); + mp_sub_ui(tabt + n, c, h + 1); + } + /* T = B^(n+h) - T */ + mp_neg(tabt, tabt, n + h + 1, 0); + tabt[n + h]++; + if (mp_mul(s, tabu, tabt + l, n + h + 1 - l, tabxh, h + 1)) + goto fail; + /* n + 2*h - l + 2 limbs */ + k = 2 * h - l; + for(i = 0; i < l; i++) + tabr[i] = tabu[i + k]; + mp_add(tabr + l, tabr + l, tabu + 2 * h, h, 0); + } + bf_free(s, tabt); + bf_free(s, tabu); + return 0; + fail: + bf_free(s, tabt); + bf_free(s, tabu); + return -1; +} + +/* return -1, 0 or 1 */ +static int mp_cmp(const limb_t *taba, const limb_t *tabb, mp_size_t n) +{ + mp_size_t i; + for(i = n - 1; i >= 0; i--) { + if (taba[i] != tabb[i]) { + if (taba[i] < tabb[i]) + return -1; + else + return 1; + } + } + return 0; +} + +//#define DEBUG_DIVNORM_LARGE +//#define DEBUG_DIVNORM_LARGE2 + +/* subquadratic divnorm */ +static int mp_divnorm_large(bf_context_t *s, + limb_t *tabq, limb_t *taba, limb_t na, + const limb_t *tabb, limb_t nb) +{ + limb_t *tabb_inv, nq, *tabt, i, n; + nq = na - nb; +#ifdef DEBUG_DIVNORM_LARGE + printf("na=%d nb=%d nq=%d\n", (int)na, (int)nb, (int)nq); + mp_print_str("a", taba, na); + mp_print_str("b", tabb, nb); +#endif + assert(nq >= 1); + n = nq; + if (nq < nb) + n++; + tabb_inv = bf_malloc(s, sizeof(limb_t) * (n + 1)); + tabt = bf_malloc(s, sizeof(limb_t) * 2 * (n + 1)); + if (!tabb_inv || !tabt) + goto fail; + + if (n >= nb) { + for(i = 0; i < n - nb; i++) + tabt[i] = 0; + for(i = 0; i < nb; i++) + tabt[i + n - nb] = tabb[i]; + } else { + /* truncate B: need to increment it so that the approximate + inverse is smaller that the exact inverse */ + for(i = 0; i < n; i++) + tabt[i] = tabb[i + nb - n]; + if (mp_add_ui(tabt, 1, n)) { + /* tabt = B^n : tabb_inv = B^n */ + memset(tabb_inv, 0, n * sizeof(limb_t)); + tabb_inv[n] = 1; + goto recip_done; + } + } + if (mp_recip(s, tabb_inv, tabt, n)) + goto fail; + recip_done: + /* Q=A*B^-1 */ + if (mp_mul(s, tabt, tabb_inv, n + 1, taba + na - (n + 1), n + 1)) + goto fail; + + for(i = 0; i < nq + 1; i++) + tabq[i] = tabt[i + 2 * (n + 1) - (nq + 1)]; +#ifdef DEBUG_DIVNORM_LARGE + mp_print_str("q", tabq, nq + 1); +#endif + + bf_free(s, tabt); + bf_free(s, tabb_inv); + tabb_inv = NULL; + + /* R=A-B*Q */ + tabt = bf_malloc(s, sizeof(limb_t) * (na + 1)); + if (!tabt) + goto fail; + if (mp_mul(s, tabt, tabq, nq + 1, tabb, nb)) + goto fail; + /* we add one more limb for the result */ + mp_sub(taba, taba, tabt, nb + 1, 0); + bf_free(s, tabt); + /* the approximated quotient is smaller than than the exact one, + hence we may have to increment it */ +#ifdef DEBUG_DIVNORM_LARGE2 + int cnt = 0; + static int cnt_max; +#endif + for(;;) { + if (taba[nb] == 0 && mp_cmp(taba, tabb, nb) < 0) + break; + taba[nb] -= mp_sub(taba, taba, tabb, nb, 0); + mp_add_ui(tabq, 1, nq + 1); +#ifdef DEBUG_DIVNORM_LARGE2 + cnt++; +#endif + } +#ifdef DEBUG_DIVNORM_LARGE2 + if (cnt > cnt_max) { + cnt_max = cnt; + printf("\ncnt=%d nq=%d nb=%d\n", cnt_max, (int)nq, (int)nb); + } +#endif + return 0; + fail: + bf_free(s, tabb_inv); + bf_free(s, tabt); + return -1; +} + +int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + int ret, r_sign; + + if (a->len < b->len) { + const bf_t *tmp = a; + a = b; + b = tmp; + } + r_sign = a->sign ^ b->sign; + /* here b->len <= a->len */ + if (b->len == 0) { + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bf_set_nan(r); + ret = 0; + } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) { + if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) || + (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) { + bf_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + bf_set_inf(r, r_sign); + ret = 0; + } + } else { + bf_set_zero(r, r_sign); + ret = 0; + } + } else { + bf_t tmp, *r1 = NULL; + limb_t a_len, b_len, precl; + limb_t *a_tab, *b_tab; + + a_len = a->len; + b_len = b->len; + + if ((flags & BF_RND_MASK) == BF_RNDF) { + /* faithful rounding does not require using the full inputs */ + precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS; + a_len = bf_min(a_len, precl); + b_len = bf_min(b_len, precl); + } + a_tab = a->tab + a->len - a_len; + b_tab = b->tab + b->len - b_len; + +#ifdef USE_FFT_MUL + if (b_len >= FFT_MUL_THRESHOLD) { + int mul_flags = 0; + if (r == a) + mul_flags |= FFT_MUL_R_OVERLAP_A; + if (r == b) + mul_flags |= FFT_MUL_R_OVERLAP_B; + if (fft_mul(r->ctx, r, a_tab, a_len, b_tab, b_len, mul_flags)) + goto fail; + } else +#endif + { + if (r == a || r == b) { + bf_init(r->ctx, &tmp); + r1 = r; + r = &tmp; + } + if (bf_resize(r, a_len + b_len)) { +#ifdef USE_FFT_MUL + fail: +#endif + bf_set_nan(r); + ret = BF_ST_MEM_ERROR; + goto done; + } + mp_mul_basecase(r->tab, a_tab, a_len, b_tab, b_len); + } + r->sign = r_sign; + r->expn = a->expn + b->expn; + ret = bf_normalize_and_round(r, prec, flags); + done: + if (r == &tmp) + bf_move(r1, &tmp); + } + return ret; +} + +/* multiply 'r' by 2^e */ +int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags) +{ + slimb_t e_max; + if (r->len == 0) + return 0; + e_max = ((limb_t)1 << BF_EXT_EXP_BITS_MAX) - 1; + e = bf_max(e, -e_max); + e = bf_min(e, e_max); + r->expn += e; + return __bf_round(r, prec, flags, r->len, 0); +} + +/* Return e such as a=m*2^e with m odd integer. return 0 if a is zero, + Infinite or Nan. */ +slimb_t bf_get_exp_min(const bf_t *a) +{ + slimb_t i; + limb_t v; + int k; + + for(i = 0; i < a->len; i++) { + v = a->tab[i]; + if (v != 0) { + k = ctz(v); + return a->expn - (a->len - i) * LIMB_BITS + k; + } + } + return 0; +} + +/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the + integer defined as floor(a/b) and r = a - q * b. */ +static void bf_tdivremu(bf_t *q, bf_t *r, + const bf_t *a, const bf_t *b) +{ + if (bf_cmpu(a, b) < 0) { + bf_set_ui(q, 0); + bf_set(r, a); + } else { + bf_div(q, a, b, bf_max(a->expn - b->expn + 1, 2), BF_RNDZ); + bf_rint(q, BF_RNDZ); + bf_mul(r, q, b, BF_PREC_INF, BF_RNDZ); + bf_sub(r, a, r, BF_PREC_INF, BF_RNDZ); + } +} + +static int __bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + int ret, r_sign; + limb_t n, nb, precl; + + r_sign = a->sign ^ b->sign; + if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) { + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else if (a->expn == BF_EXP_INF) { + bf_set_inf(r, r_sign); + return 0; + } else { + bf_set_zero(r, r_sign); + return 0; + } + } else if (a->expn == BF_EXP_ZERO) { + if (b->expn == BF_EXP_ZERO) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_zero(r, r_sign); + return 0; + } + } else if (b->expn == BF_EXP_ZERO) { + bf_set_inf(r, r_sign); + return BF_ST_DIVIDE_ZERO; + } + + /* number of limbs of the quotient (2 extra bits for rounding) */ + precl = (prec + 2 + LIMB_BITS - 1) / LIMB_BITS; + nb = b->len; + n = bf_max(a->len, precl); + + { + limb_t *taba, na; + slimb_t d; + + na = n + nb; + taba = bf_malloc(s, (na + 1) * sizeof(limb_t)); + if (!taba) + goto fail; + d = na - a->len; + memset(taba, 0, d * sizeof(limb_t)); + memcpy(taba + d, a->tab, a->len * sizeof(limb_t)); + if (bf_resize(r, n + 1)) + goto fail1; + if (mp_divnorm(s, r->tab, taba, na, b->tab, nb)) { + fail1: + bf_free(s, taba); + goto fail; + } + /* see if non zero remainder */ + if (mp_scan_nz(taba, nb)) + r->tab[0] |= 1; + bf_free(r->ctx, taba); + r->expn = a->expn - b->expn + LIMB_BITS; + r->sign = r_sign; + ret = bf_normalize_and_round(r, prec, flags); + } + return ret; + fail: + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +/* division and remainder. + + rnd_mode is the rounding mode for the quotient. The additional + rounding mode BF_RND_EUCLIDIAN is supported. + + 'q' is an integer. 'r' is rounded with prec and flags (prec can be + BF_PREC_INF). +*/ +int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, + limb_t prec, bf_flags_t flags, int rnd_mode) +{ + bf_t a1_s, *a1 = &a1_s; + bf_t b1_s, *b1 = &b1_s; + int q_sign, ret; + BOOL is_ceil, is_rndn; + + assert(q != a && q != b); + assert(r != a && r != b); + assert(q != r); + + if (a->len == 0 || b->len == 0) { + bf_set_zero(q, 0); + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set(r, a); + return bf_round(r, prec, flags); + } + } + + q_sign = a->sign ^ b->sign; + is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); + switch(rnd_mode) { + default: + case BF_RNDZ: + case BF_RNDN: + case BF_RNDNA: + is_ceil = FALSE; + break; + case BF_RNDD: + is_ceil = q_sign; + break; + case BF_RNDU: + is_ceil = q_sign ^ 1; + break; + case BF_RNDA: + is_ceil = TRUE; + break; + case BF_DIVREM_EUCLIDIAN: + is_ceil = a->sign; + break; + } + + a1->expn = a->expn; + a1->tab = a->tab; + a1->len = a->len; + a1->sign = 0; + + b1->expn = b->expn; + b1->tab = b->tab; + b1->len = b->len; + b1->sign = 0; + + /* XXX: could improve to avoid having a large 'q' */ + bf_tdivremu(q, r, a1, b1); + if (bf_is_nan(q) || bf_is_nan(r)) + goto fail; + + if (r->len != 0) { + if (is_rndn) { + int res; + b1->expn--; + res = bf_cmpu(r, b1); + b1->expn++; + if (res > 0 || + (res == 0 && + (rnd_mode == BF_RNDNA || + get_bit(q->tab, q->len, q->len * LIMB_BITS - q->expn)))) { + goto do_sub_r; + } + } else if (is_ceil) { + do_sub_r: + ret = bf_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ); + ret |= bf_sub(r, r, b1, BF_PREC_INF, BF_RNDZ); + if (ret & BF_ST_MEM_ERROR) + goto fail; + } + } + + r->sign ^= a->sign; + q->sign = q_sign; + return bf_round(r, prec, flags); + fail: + bf_set_nan(q); + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode) +{ + bf_t q_s, *q = &q_s; + int ret; + + bf_init(r->ctx, q); + ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode); + bf_delete(q); + return ret; +} + +static inline int bf_get_limb(slimb_t *pres, const bf_t *a, int flags) +{ +#if LIMB_BITS == 32 + return bf_get_int32(pres, a, flags); +#else + return bf_get_int64(pres, a, flags); +#endif +} + +int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode) +{ + bf_t q_s, *q = &q_s; + int ret; + + bf_init(r->ctx, q); + ret = bf_divrem(q, r, a, b, prec, flags, rnd_mode); + bf_get_limb(pq, q, BF_GET_INT_MOD); + bf_delete(q); + return ret; +} + +static __maybe_unused inline limb_t mul_mod(limb_t a, limb_t b, limb_t m) +{ + dlimb_t t; + t = (dlimb_t)a * (dlimb_t)b; + return t % m; +} + +#if defined(USE_MUL_CHECK) +static limb_t mp_mod1(const limb_t *tab, limb_t n, limb_t m, limb_t r) +{ + slimb_t i; + dlimb_t t; + + for(i = n - 1; i >= 0; i--) { + t = ((dlimb_t)r << LIMB_BITS) | tab[i]; + r = t % m; + } + return r; +} +#endif + +static const uint16_t sqrt_table[192] = { +128,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,144,145,146,147,148,149,150,150,151,152,153,154,155,155,156,157,158,159,160,160,161,162,163,163,164,165,166,167,167,168,169,170,170,171,172,173,173,174,175,176,176,177,178,178,179,180,181,181,182,183,183,184,185,185,186,187,187,188,189,189,190,191,192,192,193,193,194,195,195,196,197,197,198,199,199,200,201,201,202,203,203,204,204,205,206,206,207,208,208,209,209,210,211,211,212,212,213,214,214,215,215,216,217,217,218,218,219,219,220,221,221,222,222,223,224,224,225,225,226,226,227,227,228,229,229,230,230,231,231,232,232,233,234,234,235,235,236,236,237,237,238,238,239,240,240,241,241,242,242,243,243,244,244,245,245,246,246,247,247,248,248,249,249,250,250,251,251,252,252,253,253,254,254,255, +}; + +/* a >= 2^(LIMB_BITS - 2). Return (s, r) with s=floor(sqrt(a)) and + r=a-s^2. 0 <= r <= 2 * s */ +static limb_t mp_sqrtrem1(limb_t *pr, limb_t a) +{ + limb_t s1, r1, s, r, q, u, num; + + /* use a table for the 16 -> 8 bit sqrt */ + s1 = sqrt_table[(a >> (LIMB_BITS - 8)) - 64]; + r1 = (a >> (LIMB_BITS - 16)) - s1 * s1; + if (r1 > 2 * s1) { + r1 -= 2 * s1 + 1; + s1++; + } + + /* one iteration to get a 32 -> 16 bit sqrt */ + num = (r1 << 8) | ((a >> (LIMB_BITS - 32 + 8)) & 0xff); + q = num / (2 * s1); /* q <= 2^8 */ + u = num % (2 * s1); + s = (s1 << 8) + q; + r = (u << 8) | ((a >> (LIMB_BITS - 32)) & 0xff); + r -= q * q; + if ((slimb_t)r < 0) { + s--; + r += 2 * s + 1; + } + +#if LIMB_BITS == 64 + s1 = s; + r1 = r; + /* one more iteration for 64 -> 32 bit sqrt */ + num = (r1 << 16) | ((a >> (LIMB_BITS - 64 + 16)) & 0xffff); + q = num / (2 * s1); /* q <= 2^16 */ + u = num % (2 * s1); + s = (s1 << 16) + q; + r = (u << 16) | ((a >> (LIMB_BITS - 64)) & 0xffff); + r -= q * q; + if ((slimb_t)r < 0) { + s--; + r += 2 * s + 1; + } +#endif + *pr = r; + return s; +} + +/* return floor(sqrt(a)) */ +limb_t bf_isqrt(limb_t a) +{ + limb_t s, r; + int k; + + if (a == 0) + return 0; + k = clz(a) & ~1; + s = mp_sqrtrem1(&r, a << k); + s >>= (k >> 1); + return s; +} + +static limb_t mp_sqrtrem2(limb_t *tabs, limb_t *taba) +{ + limb_t s1, r1, s, q, u, a0, a1; + dlimb_t r, num; + int l; + + a0 = taba[0]; + a1 = taba[1]; + s1 = mp_sqrtrem1(&r1, a1); + l = LIMB_BITS / 2; + num = ((dlimb_t)r1 << l) | (a0 >> l); + q = num / (2 * s1); + u = num % (2 * s1); + s = (s1 << l) + q; + r = ((dlimb_t)u << l) | (a0 & (((limb_t)1 << l) - 1)); + if (unlikely((q >> l) != 0)) + r -= (dlimb_t)1 << LIMB_BITS; /* special case when q=2^l */ + else + r -= q * q; + if ((slimb_t)(r >> LIMB_BITS) < 0) { + s--; + r += 2 * (dlimb_t)s + 1; + } + tabs[0] = s; + taba[0] = r; + return r >> LIMB_BITS; +} + +//#define DEBUG_SQRTREM + +/* tmp_buf must contain (n / 2 + 1 limbs). *prh contains the highest + limb of the remainder. */ +static int mp_sqrtrem_rec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n, + limb_t *tmp_buf, limb_t *prh) +{ + limb_t l, h, rh, ql, qh, c, i; + + if (n == 1) { + *prh = mp_sqrtrem2(tabs, taba); + return 0; + } +#ifdef DEBUG_SQRTREM + mp_print_str("a", taba, 2 * n); +#endif + l = n / 2; + h = n - l; + if (mp_sqrtrem_rec(s, tabs + l, taba + 2 * l, h, tmp_buf, &qh)) + return -1; +#ifdef DEBUG_SQRTREM + mp_print_str("s1", tabs + l, h); + mp_print_str_h("r1", taba + 2 * l, h, qh); + mp_print_str_h("r2", taba + l, n, qh); +#endif + + /* the remainder is in taba + 2 * l. Its high bit is in qh */ + if (qh) { + mp_sub(taba + 2 * l, taba + 2 * l, tabs + l, h, 0); + } + /* instead of dividing by 2*s, divide by s (which is normalized) + and update q and r */ + if (mp_divnorm(s, tmp_buf, taba + l, n, tabs + l, h)) + return -1; + qh += tmp_buf[l]; + for(i = 0; i < l; i++) + tabs[i] = tmp_buf[i]; + ql = mp_shr(tabs, tabs, l, 1, qh & 1); + qh = qh >> 1; /* 0 or 1 */ + if (ql) + rh = mp_add(taba + l, taba + l, tabs + l, h, 0); + else + rh = 0; +#ifdef DEBUG_SQRTREM + mp_print_str_h("q", tabs, l, qh); + mp_print_str_h("u", taba + l, h, rh); +#endif + + mp_add_ui(tabs + l, qh, h); +#ifdef DEBUG_SQRTREM + mp_print_str_h("s2", tabs, n, sh); +#endif + + /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */ + /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */ + if (qh) { + c = qh; + } else { + if (mp_mul(s, taba + n, tabs, l, tabs, l)) + return -1; + c = mp_sub(taba, taba, taba + n, 2 * l, 0); + } + rh -= mp_sub_ui(taba + 2 * l, c, n - 2 * l); + if ((slimb_t)rh < 0) { + mp_sub_ui(tabs, 1, n); + rh += mp_add_mul1(taba, tabs, n, 2); + rh += mp_add_ui(taba, 1, n); + } + *prh = rh; + return 0; +} + +/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= 2 ^ (LIMB_BITS + - 2). Return (s, r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2 + * s. tabs has n limbs. r is returned in the lower n limbs of + taba. Its r[n] is the returned value of the function. */ +/* Algorithm from the article "Karatsuba Square Root" by Paul Zimmermann and + inspirated from its GMP implementation */ +int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n) +{ + limb_t tmp_buf1[8]; + limb_t *tmp_buf; + mp_size_t n2; + int ret; + n2 = n / 2 + 1; + if (n2 <= countof(tmp_buf1)) { + tmp_buf = tmp_buf1; + } else { + tmp_buf = bf_malloc(s, sizeof(limb_t) * n2); + if (!tmp_buf) + return -1; + } + ret = mp_sqrtrem_rec(s, tabs, taba, n, tmp_buf, taba + n); + if (tmp_buf != tmp_buf1) + bf_free(s, tmp_buf); + return ret; +} + +/* Integer square root with remainder. 'a' must be an integer. r = + floor(sqrt(a)) and rem = a - r^2. BF_ST_INEXACT is set if the result + is inexact. 'rem' can be NULL if the remainder is not needed. */ +int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a) +{ + int ret; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + } else if (a->expn == BF_EXP_INF && a->sign) { + goto invalid_op; + } else { + bf_set(r, a); + } + if (rem1) + bf_set_ui(rem1, 0); + ret = 0; + } else if (a->sign) { + invalid_op: + bf_set_nan(r); + if (rem1) + bf_set_ui(rem1, 0); + ret = BF_ST_INVALID_OP; + } else { + bf_t rem_s, *rem; + + bf_sqrt(r, a, (a->expn + 1) / 2, BF_RNDZ); + bf_rint(r, BF_RNDZ); + /* see if the result is exact by computing the remainder */ + if (rem1) { + rem = rem1; + } else { + rem = &rem_s; + bf_init(r->ctx, rem); + } + /* XXX: could avoid recomputing the remainder */ + bf_mul(rem, r, r, BF_PREC_INF, BF_RNDZ); + bf_neg(rem); + bf_add(rem, rem, a, BF_PREC_INF, BF_RNDZ); + if (bf_is_nan(rem)) { + ret = BF_ST_MEM_ERROR; + goto done; + } + if (rem->len != 0) { + ret = BF_ST_INEXACT; + } else { + ret = 0; + } + done: + if (!rem1) + bf_delete(rem); + } + return ret; +} + +int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = a->ctx; + int ret; + + assert(r != a); + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + } else if (a->expn == BF_EXP_INF && a->sign) { + goto invalid_op; + } else { + bf_set(r, a); + } + ret = 0; + } else if (a->sign) { + invalid_op: + bf_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + limb_t *a1; + slimb_t n, n1; + limb_t res; + + /* convert the mantissa to an integer with at least 2 * + prec + 4 bits */ + n = (2 * (prec + 2) + 2 * LIMB_BITS - 1) / (2 * LIMB_BITS); + if (bf_resize(r, n)) + goto fail; + a1 = bf_malloc(s, sizeof(limb_t) * 2 * n); + if (!a1) + goto fail; + n1 = bf_min(2 * n, a->len); + memset(a1, 0, (2 * n - n1) * sizeof(limb_t)); + memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t)); + if (a->expn & 1) { + res = mp_shr(a1, a1, 2 * n, 1, 0); + } else { + res = 0; + } + if (mp_sqrtrem(s, r->tab, a1, n)) { + bf_free(s, a1); + goto fail; + } + if (!res) { + res = mp_scan_nz(a1, n + 1); + } + bf_free(s, a1); + if (!res) { + res = mp_scan_nz(a->tab, a->len - n1); + } + if (res != 0) + r->tab[0] |= 1; + r->sign = 0; + r->expn = (a->expn + 1) >> 1; + ret = bf_round(r, prec, flags); + } + return ret; + fail: + bf_set_nan(r); + return BF_ST_MEM_ERROR; +} + +static no_inline int bf_op2(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, bf_op2_func_t *func) +{ + bf_t tmp; + int ret; + + if (r == a || r == b) { + bf_init(r->ctx, &tmp); + ret = func(&tmp, a, b, prec, flags); + bf_move(r, &tmp); + } else { + ret = func(r, a, b, prec, flags); + } + return ret; +} + +int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2(r, a, b, prec, flags, __bf_add); +} + +int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2(r, a, b, prec, flags, __bf_sub); +} + +int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2(r, a, b, prec, flags, __bf_div); +} + +int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, + bf_flags_t flags) +{ + bf_t b; + int ret; + bf_init(r->ctx, &b); + ret = bf_set_ui(&b, b1); + ret |= bf_mul(r, a, &b, prec, flags); + bf_delete(&b); + return ret; +} + +int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, + bf_flags_t flags) +{ + bf_t b; + int ret; + bf_init(r->ctx, &b); + ret = bf_set_si(&b, b1); + ret |= bf_mul(r, a, &b, prec, flags); + bf_delete(&b); + return ret; +} + +int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, + bf_flags_t flags) +{ + bf_t b; + int ret; + + bf_init(r->ctx, &b); + ret = bf_set_si(&b, b1); + ret |= bf_add(r, a, &b, prec, flags); + bf_delete(&b); + return ret; +} + +static int bf_pow_ui(bf_t *r, const bf_t *a, limb_t b, limb_t prec, + bf_flags_t flags) +{ + int ret, n_bits, i; + + assert(r != a); + if (b == 0) + return bf_set_ui(r, 1); + ret = bf_set(r, a); + n_bits = LIMB_BITS - clz(b); + for(i = n_bits - 2; i >= 0; i--) { + ret |= bf_mul(r, r, r, prec, flags); + if ((b >> i) & 1) + ret |= bf_mul(r, r, a, prec, flags); + } + return ret; +} + +static int bf_pow_ui_ui(bf_t *r, limb_t a1, limb_t b, + limb_t prec, bf_flags_t flags) +{ + bf_t a; + int ret; + +#ifdef USE_BF_DEC + if (a1 == 10 && b <= LIMB_DIGITS) { + /* use precomputed powers. We do not round at this point + because we expect the caller to do it */ + ret = bf_set_ui(r, mp_pow_dec[b]); + } else +#endif + { + bf_init(r->ctx, &a); + ret = bf_set_ui(&a, a1); + ret |= bf_pow_ui(r, &a, b, prec, flags); + bf_delete(&a); + } + return ret; +} + +/* convert to integer (infinite precision) */ +int bf_rint(bf_t *r, int rnd_mode) +{ + return bf_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC); +} + +/* logical operations */ +#define BF_LOGIC_OR 0 +#define BF_LOGIC_XOR 1 +#define BF_LOGIC_AND 2 + +static inline limb_t bf_logic_op1(limb_t a, limb_t b, int op) +{ + switch(op) { + case BF_LOGIC_OR: + return a | b; + case BF_LOGIC_XOR: + return a ^ b; + default: + case BF_LOGIC_AND: + return a & b; + } +} + +static int bf_logic_op(bf_t *r, const bf_t *a1, const bf_t *b1, int op) +{ + bf_t b1_s, a1_s, *a, *b; + limb_t a_sign, b_sign, r_sign; + slimb_t l, i, a_bit_offset, b_bit_offset; + limb_t v1, v2, v1_mask, v2_mask, r_mask; + int ret; + + assert(r != a1 && r != b1); + + if (a1->expn <= 0) + a_sign = 0; /* minus zero is considered as positive */ + else + a_sign = a1->sign; + + if (b1->expn <= 0) + b_sign = 0; /* minus zero is considered as positive */ + else + b_sign = b1->sign; + + if (a_sign) { + a = &a1_s; + bf_init(r->ctx, a); + if (bf_add_si(a, a1, 1, BF_PREC_INF, BF_RNDZ)) { + b = NULL; + goto fail; + } + } else { + a = (bf_t *)a1; + } + + if (b_sign) { + b = &b1_s; + bf_init(r->ctx, b); + if (bf_add_si(b, b1, 1, BF_PREC_INF, BF_RNDZ)) + goto fail; + } else { + b = (bf_t *)b1; + } + + r_sign = bf_logic_op1(a_sign, b_sign, op); + if (op == BF_LOGIC_AND && r_sign == 0) { + /* no need to compute extra zeros for and */ + if (a_sign == 0 && b_sign == 0) + l = bf_min(a->expn, b->expn); + else if (a_sign == 0) + l = a->expn; + else + l = b->expn; + } else { + l = bf_max(a->expn, b->expn); + } + /* Note: a or b can be zero */ + l = (bf_max(l, 1) + LIMB_BITS - 1) / LIMB_BITS; + if (bf_resize(r, l)) + goto fail; + a_bit_offset = a->len * LIMB_BITS - a->expn; + b_bit_offset = b->len * LIMB_BITS - b->expn; + v1_mask = -a_sign; + v2_mask = -b_sign; + r_mask = -r_sign; + for(i = 0; i < l; i++) { + v1 = get_bits(a->tab, a->len, a_bit_offset + i * LIMB_BITS) ^ v1_mask; + v2 = get_bits(b->tab, b->len, b_bit_offset + i * LIMB_BITS) ^ v2_mask; + r->tab[i] = bf_logic_op1(v1, v2, op) ^ r_mask; + } + r->expn = l * LIMB_BITS; + r->sign = r_sign; + bf_normalize_and_round(r, BF_PREC_INF, BF_RNDZ); /* cannot fail */ + if (r_sign) { + if (bf_add_si(r, r, -1, BF_PREC_INF, BF_RNDZ)) + goto fail; + } + ret = 0; + done: + if (a == &a1_s) + bf_delete(a); + if (b == &b1_s) + bf_delete(b); + return ret; + fail: + bf_set_nan(r); + ret = BF_ST_MEM_ERROR; + goto done; +} + +/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ +int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b) +{ + return bf_logic_op(r, a, b, BF_LOGIC_OR); +} + +/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ +int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b) +{ + return bf_logic_op(r, a, b, BF_LOGIC_XOR); +} + +/* 'a' and 'b' must be integers. Return 0 or BF_ST_MEM_ERROR. */ +int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b) +{ + return bf_logic_op(r, a, b, BF_LOGIC_AND); +} + +/* conversion between fixed size types */ + +typedef union { + double d; + uint64_t u; +} Float64Union; + +int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode) +{ + Float64Union u; + int e, ret; + uint64_t m; + + ret = 0; + if (a->expn == BF_EXP_NAN) { + u.u = 0x7ff8000000000000; /* quiet nan */ + } else { + bf_t b_s, *b = &b_s; + + bf_init(a->ctx, b); + bf_set(b, a); + if (bf_is_finite(b)) { + ret = bf_round(b, 53, rnd_mode | BF_FLAG_SUBNORMAL | bf_set_exp_bits(11)); + } + if (b->expn == BF_EXP_INF) { + e = (1 << 11) - 1; + m = 0; + } else if (b->expn == BF_EXP_ZERO) { + e = 0; + m = 0; + } else { + e = b->expn + 1023 - 1; +#if LIMB_BITS == 32 + if (b->len == 2) { + m = ((uint64_t)b->tab[1] << 32) | b->tab[0]; + } else { + m = ((uint64_t)b->tab[0] << 32); + } +#else + m = b->tab[0]; +#endif + if (e <= 0) { + /* subnormal */ + m = m >> (12 - e); + e = 0; + } else { + m = (m << 1) >> 12; + } + } + u.u = m | ((uint64_t)e << 52) | ((uint64_t)b->sign << 63); + bf_delete(b); + } + *pres = u.d; + return ret; +} + +int bf_set_float64(bf_t *a, double d) +{ + Float64Union u; + uint64_t m; + int shift, e, sgn; + + u.d = d; + sgn = u.u >> 63; + e = (u.u >> 52) & ((1 << 11) - 1); + m = u.u & (((uint64_t)1 << 52) - 1); + if (e == ((1 << 11) - 1)) { + if (m != 0) { + bf_set_nan(a); + } else { + bf_set_inf(a, sgn); + } + } else if (e == 0) { + if (m == 0) { + bf_set_zero(a, sgn); + } else { + /* subnormal number */ + m <<= 12; + shift = clz64(m); + m <<= shift; + e = -shift; + goto norm; + } + } else { + m = (m << 11) | ((uint64_t)1 << 63); + norm: + a->expn = e - 1023 + 1; +#if LIMB_BITS == 32 + if (bf_resize(a, 2)) + goto fail; + a->tab[0] = m; + a->tab[1] = m >> 32; +#else + if (bf_resize(a, 1)) + goto fail; + a->tab[0] = m; +#endif + a->sign = sgn; + } + return 0; +fail: + bf_set_nan(a); + return BF_ST_MEM_ERROR; +} + +/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there + is an overflow and 0 otherwise. */ +int bf_get_int32(int *pres, const bf_t *a, int flags) +{ + uint32_t v; + int ret; + if (a->expn >= BF_EXP_INF) { + ret = BF_ST_INVALID_OP; + if (flags & BF_GET_INT_MOD) { + v = 0; + } else if (a->expn == BF_EXP_INF) { + v = (uint32_t)INT32_MAX + a->sign; + } else { + v = INT32_MAX; + } + } else if (a->expn <= 0) { + v = 0; + ret = 0; + } else if (a->expn <= 31) { + v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); + if (a->sign) + v = -v; + ret = 0; + } else if (!(flags & BF_GET_INT_MOD)) { + ret = BF_ST_INVALID_OP; + if (a->sign) { + v = (uint32_t)INT32_MAX + 1; + if (a->expn == 32 && + (a->tab[a->len - 1] >> (LIMB_BITS - 32)) == v) { + ret = 0; + } + } else { + v = INT32_MAX; + } + } else { + v = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn); + if (a->sign) + v = -v; + ret = 0; + } + *pres = v; + return ret; +} + +/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there + is an overflow and 0 otherwise. */ +int bf_get_int64(int64_t *pres, const bf_t *a, int flags) +{ + uint64_t v; + int ret; + if (a->expn >= BF_EXP_INF) { + ret = BF_ST_INVALID_OP; + if (flags & BF_GET_INT_MOD) { + v = 0; + } else if (a->expn == BF_EXP_INF) { + v = (uint64_t)INT64_MAX + a->sign; + } else { + v = INT64_MAX; + } + } else if (a->expn <= 0) { + v = 0; + ret = 0; + } else if (a->expn <= 63) { +#if LIMB_BITS == 32 + if (a->expn <= 32) + v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); + else + v = (((uint64_t)a->tab[a->len - 1] << 32) | + get_limbz(a, a->len - 2)) >> (64 - a->expn); +#else + v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); +#endif + if (a->sign) + v = -v; + ret = 0; + } else if (!(flags & BF_GET_INT_MOD)) { + ret = BF_ST_INVALID_OP; + if (a->sign) { + uint64_t v1; + v = (uint64_t)INT64_MAX + 1; + if (a->expn == 64) { + v1 = a->tab[a->len - 1]; +#if LIMB_BITS == 32 + v1 = (v1 << 32) | get_limbz(a, a->len - 2); +#endif + if (v1 == v) + ret = 0; + } + } else { + v = INT64_MAX; + } + } else { + slimb_t bit_pos = a->len * LIMB_BITS - a->expn; + v = get_bits(a->tab, a->len, bit_pos); +#if LIMB_BITS == 32 + v |= (uint64_t)get_bits(a->tab, a->len, bit_pos + 32) << 32; +#endif + if (a->sign) + v = -v; + ret = 0; + } + *pres = v; + return ret; +} + +/* The rounding mode is always BF_RNDZ. Return BF_ST_INVALID_OP if there + is an overflow and 0 otherwise. */ +int bf_get_uint64(uint64_t *pres, const bf_t *a) +{ + uint64_t v; + int ret; + if (a->expn == BF_EXP_NAN) { + goto overflow; + } else if (a->expn <= 0) { + v = 0; + ret = 0; + } else if (a->sign) { + v = 0; + ret = BF_ST_INVALID_OP; + } else if (a->expn <= 64) { +#if LIMB_BITS == 32 + if (a->expn <= 32) + v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); + else + v = (((uint64_t)a->tab[a->len - 1] << 32) | + get_limbz(a, a->len - 2)) >> (64 - a->expn); +#else + v = a->tab[a->len - 1] >> (LIMB_BITS - a->expn); +#endif + ret = 0; + } else { + overflow: + v = UINT64_MAX; + ret = BF_ST_INVALID_OP; + } + *pres = v; + return ret; +} + +/* base conversion from radix */ + +static const uint8_t digits_per_limb_table[BF_RADIX_MAX - 1] = { +#if LIMB_BITS == 32 +32,20,16,13,12,11,10,10, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, +#else +64,40,32,27,24,22,21,20,19,18,17,17,16,16,16,15,15,15,14,14,14,14,13,13,13,13,13,13,13,12,12,12,12,12,12, +#endif +}; + +static limb_t get_limb_radix(int radix) +{ + int i, k; + limb_t radixl; + + k = digits_per_limb_table[radix - 2]; + radixl = radix; + for(i = 1; i < k; i++) + radixl *= radix; + return radixl; +} + +/* return != 0 if error */ +static int bf_integer_from_radix_rec(bf_t *r, const limb_t *tab, + limb_t n, int level, limb_t n0, + limb_t radix, bf_t *pow_tab) +{ + int ret; + if (n == 1) { + ret = bf_set_ui(r, tab[0]); + } else { + bf_t T_s, *T = &T_s, *B; + limb_t n1, n2; + + n2 = (((n0 * 2) >> (level + 1)) + 1) / 2; + n1 = n - n2; + // printf("level=%d n0=%ld n1=%ld n2=%ld\n", level, n0, n1, n2); + B = &pow_tab[level]; + if (B->len == 0) { + ret = bf_pow_ui_ui(B, radix, n2, BF_PREC_INF, BF_RNDZ); + if (ret) + return ret; + } + ret = bf_integer_from_radix_rec(r, tab + n2, n1, level + 1, n0, + radix, pow_tab); + if (ret) + return ret; + ret = bf_mul(r, r, B, BF_PREC_INF, BF_RNDZ); + if (ret) + return ret; + bf_init(r->ctx, T); + ret = bf_integer_from_radix_rec(T, tab, n2, level + 1, n0, + radix, pow_tab); + if (!ret) + ret = bf_add(r, r, T, BF_PREC_INF, BF_RNDZ); + bf_delete(T); + } + return ret; + // bf_print_str(" r=", r); +} + +/* return 0 if OK != 0 if memory error */ +static int bf_integer_from_radix(bf_t *r, const limb_t *tab, + limb_t n, limb_t radix) +{ + bf_context_t *s = r->ctx; + int pow_tab_len, i, ret; + limb_t radixl; + bf_t *pow_tab; + + radixl = get_limb_radix(radix); + pow_tab_len = ceil_log2(n) + 2; /* XXX: check */ + pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len); + if (!pow_tab) + return -1; + for(i = 0; i < pow_tab_len; i++) + bf_init(r->ctx, &pow_tab[i]); + ret = bf_integer_from_radix_rec(r, tab, n, 0, n, radixl, pow_tab); + for(i = 0; i < pow_tab_len; i++) { + bf_delete(&pow_tab[i]); + } + bf_free(s, pow_tab); + return ret; +} + +/* compute and round T * radix^expn. */ +int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, + slimb_t expn, limb_t prec, bf_flags_t flags) +{ + int ret, expn_sign, overflow; + slimb_t e, extra_bits, prec1, ziv_extra_bits; + bf_t B_s, *B = &B_s; + + if (T->len == 0) { + return bf_set(r, T); + } else if (expn == 0) { + ret = bf_set(r, T); + ret |= bf_round(r, prec, flags); + return ret; + } + + e = expn; + expn_sign = 0; + if (e < 0) { + e = -e; + expn_sign = 1; + } + bf_init(r->ctx, B); + if (prec == BF_PREC_INF) { + /* infinite precision: only used if the result is known to be exact */ + ret = bf_pow_ui_ui(B, radix, e, BF_PREC_INF, BF_RNDN); + if (expn_sign) { + ret |= bf_div(r, T, B, T->len * LIMB_BITS, BF_RNDN); + } else { + ret |= bf_mul(r, T, B, BF_PREC_INF, BF_RNDN); + } + } else { + ziv_extra_bits = 16; + for(;;) { + prec1 = prec + ziv_extra_bits; + /* XXX: correct overflow/underflow handling */ + /* XXX: rigorous error analysis needed */ + extra_bits = ceil_log2(e) * 2 + 1; + ret = bf_pow_ui_ui(B, radix, e, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); + overflow = !bf_is_finite(B); + /* XXX: if bf_pow_ui_ui returns an exact result, can stop + after the next operation */ + if (expn_sign) + ret |= bf_div(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); + else + ret |= bf_mul(r, T, B, prec1 + extra_bits, BF_RNDN | BF_FLAG_EXT_EXP); + if (ret & BF_ST_MEM_ERROR) + break; + if ((ret & BF_ST_INEXACT) && + !bf_can_round(r, prec, flags & BF_RND_MASK, prec1) && + !overflow) { + /* and more precision and retry */ + ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); + } else { + /* XXX: need to use __bf_round() to pass the inexact + flag for the subnormal case */ + ret = bf_round(r, prec, flags) | (ret & BF_ST_INEXACT); + break; + } + } + } + bf_delete(B); + return ret; +} + +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; +} + +/* add a limb at 'pos' and decrement pos. new space is created if + needed. Return 0 if OK, -1 if memory error */ +static int bf_add_limb(bf_t *a, slimb_t *ppos, limb_t v) +{ + slimb_t pos; + pos = *ppos; + if (unlikely(pos < 0)) { + limb_t new_size, d, *new_tab; + new_size = bf_max(a->len + 1, a->len * 3 / 2); + new_tab = bf_realloc(a->ctx, a->tab, sizeof(limb_t) * new_size); + if (!new_tab) + return -1; + a->tab = new_tab; + d = new_size - a->len; + memmove(a->tab + d, a->tab, a->len * sizeof(limb_t)); + a->len = new_size; + pos += d; + } + a->tab[pos--] = v; + *ppos = pos; + return 0; +} + +static int bf_tolower(int c) +{ + if (c >= 'A' && c <= 'Z') + c = c - 'A' + 'a'; + return c; +} + +static int strcasestart(const char *str, const char *val, const char **ptr) +{ + const char *p, *q; + p = str; + q = val; + while (*q != '\0') { + if (bf_tolower(*p) != *q) + return 0; + p++; + q++; + } + if (ptr) + *ptr = p; + return 1; +} + +static int bf_atof_internal(bf_t *r, slimb_t *pexponent, + const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags, BOOL is_dec) +{ + const char *p, *p_start; + int is_neg, radix_bits, exp_is_neg, ret, digits_per_limb, shift; + limb_t cur_limb; + slimb_t pos, expn, int_len, digit_count; + BOOL has_decpt, is_bin_exp; + bf_t a_s, *a; + + *pexponent = 0; + p = str; + if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 && + strcasestart(p, "nan", &p)) { + bf_set_nan(r); + ret = 0; + goto done; + } + is_neg = 0; + + if (p[0] == '+') { + p++; + p_start = p; + } else if (p[0] == '-') { + is_neg = 1; + p++; + p_start = p; + } else { + p_start = p; + } + if (p[0] == '0') { + if ((p[1] == 'x' || p[1] == 'X') && + (radix == 0 || radix == 16) && + !(flags & BF_ATOF_NO_HEX)) { + radix = 16; + p += 2; + } else if ((p[1] == 'o' || p[1] == 'O') && + radix == 0 && (flags & BF_ATOF_BIN_OCT)) { + p += 2; + radix = 8; + } else if ((p[1] == 'b' || p[1] == 'B') && + radix == 0 && (flags & BF_ATOF_BIN_OCT)) { + p += 2; + radix = 2; + } else { + goto no_prefix; + } + /* there must be a digit after the prefix */ + if (to_digit((uint8_t)*p) >= radix) { + bf_set_nan(r); + ret = 0; + goto done; + } + no_prefix: ; + } else { + if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 && + strcasestart(p, "inf", &p)) { + bf_set_inf(r, is_neg); + ret = 0; + goto done; + } + } + + if (radix == 0) + radix = 10; + if (is_dec) { + assert(radix == 10); + radix_bits = 0; + a = r; + } else if ((radix & (radix - 1)) != 0) { + radix_bits = 0; /* base is not a power of two */ + a = &a_s; + bf_init(r->ctx, a); + } else { + radix_bits = ceil_log2(radix); + a = r; + } + + /* skip leading zeros */ + /* XXX: could also skip zeros after the decimal point */ + while (*p == '0') + p++; + + if (radix_bits) { + shift = digits_per_limb = LIMB_BITS; + } else { + radix_bits = 0; + shift = digits_per_limb = digits_per_limb_table[radix - 2]; + } + cur_limb = 0; + bf_resize(a, 1); + pos = 0; + has_decpt = FALSE; + int_len = digit_count = 0; + for(;;) { + limb_t c; + if (*p == '.' && (p > p_start || to_digit(p[1]) < radix)) { + if (has_decpt) + break; + has_decpt = TRUE; + int_len = digit_count; + p++; + } + c = to_digit(*p); + if (c >= radix) + break; + digit_count++; + p++; + if (radix_bits) { + shift -= radix_bits; + if (shift <= 0) { + cur_limb |= c >> (-shift); + if (bf_add_limb(a, &pos, cur_limb)) + goto mem_error; + if (shift < 0) + cur_limb = c << (LIMB_BITS + shift); + else + cur_limb = 0; + shift += LIMB_BITS; + } else { + cur_limb |= c << shift; + } + } else { + cur_limb = cur_limb * radix + c; + shift--; + if (shift == 0) { + if (bf_add_limb(a, &pos, cur_limb)) + goto mem_error; + shift = digits_per_limb; + cur_limb = 0; + } + } + } + if (!has_decpt) + int_len = digit_count; + + /* add the last limb and pad with zeros */ + if (shift != digits_per_limb) { + if (radix_bits == 0) { + while (shift != 0) { + cur_limb *= radix; + shift--; + } + } + if (bf_add_limb(a, &pos, cur_limb)) { + mem_error: + ret = BF_ST_MEM_ERROR; + if (!radix_bits) + bf_delete(a); + bf_set_nan(r); + goto done; + } + } + + /* reset the next limbs to zero (we prefer to reallocate in the + renormalization) */ + memset(a->tab, 0, (pos + 1) * sizeof(limb_t)); + + if (p == p_start) { + ret = 0; + if (!radix_bits) + bf_delete(a); + bf_set_nan(r); + goto done; + } + + /* parse the exponent, if any */ + expn = 0; + is_bin_exp = FALSE; + if (((radix == 10 && (*p == 'e' || *p == 'E')) || + (radix != 10 && (*p == '@' || + (radix_bits && (*p == 'p' || *p == 'P'))))) && + p > p_start) { + is_bin_exp = (*p == 'p' || *p == 'P'); + p++; + exp_is_neg = 0; + if (*p == '+') { + p++; + } else if (*p == '-') { + exp_is_neg = 1; + p++; + } + for(;;) { + int c; + c = to_digit(*p); + if (c >= 10) + break; + if (unlikely(expn > ((BF_RAW_EXP_MAX - 2 - 9) / 10))) { + /* exponent overflow */ + if (exp_is_neg) { + bf_set_zero(r, is_neg); + ret = BF_ST_UNDERFLOW | BF_ST_INEXACT; + } else { + bf_set_inf(r, is_neg); + ret = BF_ST_OVERFLOW | BF_ST_INEXACT; + } + goto done; + } + p++; + expn = expn * 10 + c; + } + if (exp_is_neg) + expn = -expn; + } + if (is_dec) { + a->expn = expn + int_len; + a->sign = is_neg; + ret = bfdec_normalize_and_round((bfdec_t *)a, prec, flags); + } else if (radix_bits) { + /* XXX: may overflow */ + if (!is_bin_exp) + expn *= radix_bits; + a->expn = expn + (int_len * radix_bits); + a->sign = is_neg; + ret = bf_normalize_and_round(a, prec, flags); + } else { + limb_t l; + pos++; + l = a->len - pos; /* number of limbs */ + if (l == 0) { + bf_set_zero(r, is_neg); + ret = 0; + } else { + bf_t T_s, *T = &T_s; + + expn -= l * digits_per_limb - int_len; + bf_init(r->ctx, T); + if (bf_integer_from_radix(T, a->tab + pos, l, radix)) { + bf_set_nan(r); + ret = BF_ST_MEM_ERROR; + } else { + T->sign = is_neg; + if (flags & BF_ATOF_EXPONENT) { + /* return the exponent */ + *pexponent = expn; + ret = bf_set(r, T); + } else { + ret = bf_mul_pow_radix(r, T, radix, expn, prec, flags); + } + } + bf_delete(T); + } + bf_delete(a); + } + done: + if (pnext) + *pnext = p; + return ret; +} + +/* + Return (status, n, exp). 'status' is the floating point status. 'n' + is the parsed number. + + If (flags & BF_ATOF_EXPONENT) and if the radix is not a power of + two, the parsed number is equal to r * + (*pexponent)^radix. Otherwise *pexponent = 0. +*/ +int bf_atof2(bf_t *r, slimb_t *pexponent, + const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags) +{ + return bf_atof_internal(r, pexponent, str, pnext, radix, prec, flags, + FALSE); +} + +int bf_atof(bf_t *r, const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags) +{ + slimb_t dummy_exp; + return bf_atof_internal(r, &dummy_exp, str, pnext, radix, prec, flags, FALSE); +} + +/* base conversion to radix */ + +#if LIMB_BITS == 64 +#define RADIXL_10 UINT64_C(10000000000000000000) +#else +#define RADIXL_10 UINT64_C(1000000000) +#endif + +static const uint32_t inv_log2_radix[BF_RADIX_MAX - 1][LIMB_BITS / 32 + 1] = { +#if LIMB_BITS == 32 +{ 0x80000000, 0x00000000,}, +{ 0x50c24e60, 0xd4d4f4a7,}, +{ 0x40000000, 0x00000000,}, +{ 0x372068d2, 0x0a1ee5ca,}, +{ 0x3184648d, 0xb8153e7a,}, +{ 0x2d983275, 0x9d5369c4,}, +{ 0x2aaaaaaa, 0xaaaaaaab,}, +{ 0x28612730, 0x6a6a7a54,}, +{ 0x268826a1, 0x3ef3fde6,}, +{ 0x25001383, 0xbac8a744,}, +{ 0x23b46706, 0x82c0c709,}, +{ 0x229729f1, 0xb2c83ded,}, +{ 0x219e7ffd, 0xa5ad572b,}, +{ 0x20c33b88, 0xda7c29ab,}, +{ 0x20000000, 0x00000000,}, +{ 0x1f50b57e, 0xac5884b3,}, +{ 0x1eb22cc6, 0x8aa6e26f,}, +{ 0x1e21e118, 0x0c5daab2,}, +{ 0x1d9dcd21, 0x439834e4,}, +{ 0x1d244c78, 0x367a0d65,}, +{ 0x1cb40589, 0xac173e0c,}, +{ 0x1c4bd95b, 0xa8d72b0d,}, +{ 0x1bead768, 0x98f8ce4c,}, +{ 0x1b903469, 0x050f72e5,}, +{ 0x1b3b433f, 0x2eb06f15,}, +{ 0x1aeb6f75, 0x9c46fc38,}, +{ 0x1aa038eb, 0x0e3bfd17,}, +{ 0x1a593062, 0xb38d8c56,}, +{ 0x1a15f4c3, 0x2b95a2e6,}, +{ 0x19d630dc, 0xcc7ddef9,}, +{ 0x19999999, 0x9999999a,}, +{ 0x195fec80, 0x8a609431,}, +{ 0x1928ee7b, 0x0b4f22f9,}, +{ 0x18f46acf, 0x8c06e318,}, +{ 0x18c23246, 0xdc0a9f3d,}, +#else +{ 0x80000000, 0x00000000, 0x00000000,}, +{ 0x50c24e60, 0xd4d4f4a7, 0x021f57bc,}, +{ 0x40000000, 0x00000000, 0x00000000,}, +{ 0x372068d2, 0x0a1ee5ca, 0x19ea911b,}, +{ 0x3184648d, 0xb8153e7a, 0x7fc2d2e1,}, +{ 0x2d983275, 0x9d5369c4, 0x4dec1661,}, +{ 0x2aaaaaaa, 0xaaaaaaaa, 0xaaaaaaab,}, +{ 0x28612730, 0x6a6a7a53, 0x810fabde,}, +{ 0x268826a1, 0x3ef3fde6, 0x23e2566b,}, +{ 0x25001383, 0xbac8a744, 0x385a3349,}, +{ 0x23b46706, 0x82c0c709, 0x3f891718,}, +{ 0x229729f1, 0xb2c83ded, 0x15fba800,}, +{ 0x219e7ffd, 0xa5ad572a, 0xe169744b,}, +{ 0x20c33b88, 0xda7c29aa, 0x9bddee52,}, +{ 0x20000000, 0x00000000, 0x00000000,}, +{ 0x1f50b57e, 0xac5884b3, 0x70e28eee,}, +{ 0x1eb22cc6, 0x8aa6e26f, 0x06d1a2a2,}, +{ 0x1e21e118, 0x0c5daab1, 0x81b4f4bf,}, +{ 0x1d9dcd21, 0x439834e3, 0x81667575,}, +{ 0x1d244c78, 0x367a0d64, 0xc8204d6d,}, +{ 0x1cb40589, 0xac173e0c, 0x3b7b16ba,}, +{ 0x1c4bd95b, 0xa8d72b0d, 0x5879f25a,}, +{ 0x1bead768, 0x98f8ce4c, 0x66cc2858,}, +{ 0x1b903469, 0x050f72e5, 0x0cf5488e,}, +{ 0x1b3b433f, 0x2eb06f14, 0x8c89719c,}, +{ 0x1aeb6f75, 0x9c46fc37, 0xab5fc7e9,}, +{ 0x1aa038eb, 0x0e3bfd17, 0x1bd62080,}, +{ 0x1a593062, 0xb38d8c56, 0x7998ab45,}, +{ 0x1a15f4c3, 0x2b95a2e6, 0x46aed6a0,}, +{ 0x19d630dc, 0xcc7ddef9, 0x5aadd61b,}, +{ 0x19999999, 0x99999999, 0x9999999a,}, +{ 0x195fec80, 0x8a609430, 0xe1106014,}, +{ 0x1928ee7b, 0x0b4f22f9, 0x5f69791d,}, +{ 0x18f46acf, 0x8c06e318, 0x4d2aeb2c,}, +{ 0x18c23246, 0xdc0a9f3d, 0x3fe16970,}, +#endif +}; + +static const limb_t log2_radix[BF_RADIX_MAX - 1] = { +#if LIMB_BITS == 32 +0x20000000, +0x32b80347, +0x40000000, +0x4a4d3c26, +0x52b80347, +0x59d5d9fd, +0x60000000, +0x6570068e, +0x6a4d3c26, +0x6eb3a9f0, +0x72b80347, +0x766a008e, +0x79d5d9fd, +0x7d053f6d, +0x80000000, +0x82cc7edf, +0x8570068e, +0x87ef05ae, +0x8a4d3c26, +0x8c8ddd45, +0x8eb3a9f0, +0x90c10501, +0x92b80347, +0x949a784c, +0x966a008e, +0x982809d6, +0x99d5d9fd, +0x9b74948f, +0x9d053f6d, +0x9e88c6b3, +0xa0000000, +0xa16bad37, +0xa2cc7edf, +0xa4231623, +0xa570068e, +#else +0x2000000000000000, +0x32b803473f7ad0f4, +0x4000000000000000, +0x4a4d3c25e68dc57f, +0x52b803473f7ad0f4, +0x59d5d9fd5010b366, +0x6000000000000000, +0x6570068e7ef5a1e8, +0x6a4d3c25e68dc57f, +0x6eb3a9f01975077f, +0x72b803473f7ad0f4, +0x766a008e4788cbcd, +0x79d5d9fd5010b366, +0x7d053f6d26089673, +0x8000000000000000, +0x82cc7edf592262d0, +0x8570068e7ef5a1e8, +0x87ef05ae409a0289, +0x8a4d3c25e68dc57f, +0x8c8ddd448f8b845a, +0x8eb3a9f01975077f, +0x90c10500d63aa659, +0x92b803473f7ad0f4, +0x949a784bcd1b8afe, +0x966a008e4788cbcd, +0x982809d5be7072dc, +0x99d5d9fd5010b366, +0x9b74948f5532da4b, +0x9d053f6d26089673, +0x9e88c6b3626a72aa, +0xa000000000000000, +0xa16bad3758efd873, +0xa2cc7edf592262d0, +0xa4231623369e78e6, +0xa570068e7ef5a1e8, +#endif +}; + +/* compute floor(a*b) or ceil(a*b) with b = log2(radix) or + b=1/log2(radix). For is_inv = 0, strict accuracy is not guaranteed + when radix is not a power of two. */ +slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv, + int is_ceil1) +{ + int is_neg; + limb_t a; + BOOL is_ceil; + + is_ceil = is_ceil1; + a = a1; + if (a1 < 0) { + a = -a; + is_neg = 1; + } else { + is_neg = 0; + } + is_ceil ^= is_neg; + if ((radix & (radix - 1)) == 0) { + int radix_bits; + /* radix is a power of two */ + radix_bits = ceil_log2(radix); + if (is_inv) { + if (is_ceil) + a += radix_bits - 1; + a = a / radix_bits; + } else { + a = a * radix_bits; + } + } else { + const uint32_t *tab; + limb_t b0, b1; + dlimb_t t; + + if (is_inv) { + tab = inv_log2_radix[radix - 2]; +#if LIMB_BITS == 32 + b1 = tab[0]; + b0 = tab[1]; +#else + b1 = ((limb_t)tab[0] << 32) | tab[1]; + b0 = (limb_t)tab[2] << 32; +#endif + t = (dlimb_t)b0 * (dlimb_t)a; + t = (dlimb_t)b1 * (dlimb_t)a + (t >> LIMB_BITS); + a = t >> (LIMB_BITS - 1); + } else { + b0 = log2_radix[radix - 2]; + t = (dlimb_t)b0 * (dlimb_t)a; + a = t >> (LIMB_BITS - 3); + } + /* a = floor(result) and 'result' cannot be an integer */ + a += is_ceil; + } + if (is_neg) + a = -a; + return a; +} + +/* 'n' is the number of output limbs */ +static int bf_integer_to_radix_rec(bf_t *pow_tab, + limb_t *out, const bf_t *a, limb_t n, + int level, limb_t n0, limb_t radixl, + unsigned int radixl_bits) +{ + limb_t n1, n2, q_prec; + int ret; + + assert(n >= 1); + if (n == 1) { + out[0] = get_bits(a->tab, a->len, a->len * LIMB_BITS - a->expn); + } else if (n == 2) { + dlimb_t t; + slimb_t pos; + pos = a->len * LIMB_BITS - a->expn; + t = ((dlimb_t)get_bits(a->tab, a->len, pos + LIMB_BITS) << LIMB_BITS) | + get_bits(a->tab, a->len, pos); + if (likely(radixl == RADIXL_10)) { + /* use division by a constant when possible */ + out[0] = t % RADIXL_10; + out[1] = t / RADIXL_10; + } else { + out[0] = t % radixl; + out[1] = t / radixl; + } + } else { + bf_t Q, R, *B, *B_inv; + int q_add; + bf_init(a->ctx, &Q); + bf_init(a->ctx, &R); + n2 = (((n0 * 2) >> (level + 1)) + 1) / 2; + n1 = n - n2; + B = &pow_tab[2 * level]; + B_inv = &pow_tab[2 * level + 1]; + ret = 0; + if (B->len == 0) { + /* compute BASE^n2 */ + ret |= bf_pow_ui_ui(B, radixl, n2, BF_PREC_INF, BF_RNDZ); + /* we use enough bits for the maximum possible 'n1' value, + i.e. n2 + 1 */ + ret |= bf_set_ui(&R, 1); + ret |= bf_div(B_inv, &R, B, (n2 + 1) * radixl_bits + 2, BF_RNDN); + } + // printf("%d: n1=% " PRId64 " n2=%" PRId64 "\n", level, n1, n2); + q_prec = n1 * radixl_bits; + ret |= bf_mul(&Q, a, B_inv, q_prec, BF_RNDN); + ret |= bf_rint(&Q, BF_RNDZ); + + ret |= bf_mul(&R, &Q, B, BF_PREC_INF, BF_RNDZ); + ret |= bf_sub(&R, a, &R, BF_PREC_INF, BF_RNDZ); + + if (ret & BF_ST_MEM_ERROR) + goto fail; + /* adjust if necessary */ + q_add = 0; + while (R.sign && R.len != 0) { + if (bf_add(&R, &R, B, BF_PREC_INF, BF_RNDZ)) + goto fail; + q_add--; + } + while (bf_cmpu(&R, B) >= 0) { + if (bf_sub(&R, &R, B, BF_PREC_INF, BF_RNDZ)) + goto fail; + q_add++; + } + if (q_add != 0) { + if (bf_add_si(&Q, &Q, q_add, BF_PREC_INF, BF_RNDZ)) + goto fail; + } + if (bf_integer_to_radix_rec(pow_tab, out + n2, &Q, n1, level + 1, n0, + radixl, radixl_bits)) + goto fail; + if (bf_integer_to_radix_rec(pow_tab, out, &R, n2, level + 1, n0, + radixl, radixl_bits)) { + fail: + bf_delete(&Q); + bf_delete(&R); + return -1; + } + bf_delete(&Q); + bf_delete(&R); + } + return 0; +} + +/* return 0 if OK != 0 if memory error */ +static int bf_integer_to_radix(bf_t *r, const bf_t *a, limb_t radixl) +{ + bf_context_t *s = r->ctx; + limb_t r_len; + bf_t *pow_tab; + int i, pow_tab_len, ret; + + r_len = r->len; + pow_tab_len = (ceil_log2(r_len) + 2) * 2; /* XXX: check */ + pow_tab = bf_malloc(s, sizeof(pow_tab[0]) * pow_tab_len); + if (!pow_tab) + return -1; + for(i = 0; i < pow_tab_len; i++) + bf_init(r->ctx, &pow_tab[i]); + + ret = bf_integer_to_radix_rec(pow_tab, r->tab, a, r_len, 0, r_len, radixl, + ceil_log2(radixl)); + + for(i = 0; i < pow_tab_len; i++) { + bf_delete(&pow_tab[i]); + } + bf_free(s, pow_tab); + return ret; +} + +/* a must be >= 0. 'P' is the wanted number of digits in radix + 'radix'. 'r' is the mantissa represented as an integer. *pE + contains the exponent. Return != 0 if memory error. */ +static int bf_convert_to_radix(bf_t *r, slimb_t *pE, + const bf_t *a, int radix, + limb_t P, bf_rnd_t rnd_mode, + BOOL is_fixed_exponent) +{ + slimb_t E, e, prec, extra_bits, ziv_extra_bits, prec0; + bf_t B_s, *B = &B_s; + int e_sign, ret, res; + + if (a->len == 0) { + /* zero case */ + *pE = 0; + return bf_set(r, a); + } + + if (is_fixed_exponent) { + E = *pE; + } else { + /* compute the new exponent */ + E = 1 + bf_mul_log2_radix(a->expn - 1, radix, TRUE, FALSE); + } + // bf_print_str("a", a); + // printf("E=%ld P=%ld radix=%d\n", E, P, radix); + + for(;;) { + e = P - E; + e_sign = 0; + if (e < 0) { + e = -e; + e_sign = 1; + } + /* Note: precision for log2(radix) is not critical here */ + prec0 = bf_mul_log2_radix(P, radix, FALSE, TRUE); + ziv_extra_bits = 16; + for(;;) { + prec = prec0 + ziv_extra_bits; + /* XXX: rigorous error analysis needed */ + extra_bits = ceil_log2(e) * 2 + 1; + ret = bf_pow_ui_ui(r, radix, e, prec + extra_bits, + BF_RNDN | BF_FLAG_EXT_EXP); + if (!e_sign) + ret |= bf_mul(r, r, a, prec + extra_bits, + BF_RNDN | BF_FLAG_EXT_EXP); + else + ret |= bf_div(r, a, r, prec + extra_bits, + BF_RNDN | BF_FLAG_EXT_EXP); + if (ret & BF_ST_MEM_ERROR) + return BF_ST_MEM_ERROR; + /* if the result is not exact, check that it can be safely + rounded to an integer */ + if ((ret & BF_ST_INEXACT) && + !bf_can_round(r, r->expn, rnd_mode, prec)) { + /* and more precision and retry */ + ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); + continue; + } else { + ret = bf_rint(r, rnd_mode); + if (ret & BF_ST_MEM_ERROR) + return BF_ST_MEM_ERROR; + break; + } + } + if (is_fixed_exponent) + break; + /* check that the result is < B^P */ + /* XXX: do a fast approximate test first ? */ + bf_init(r->ctx, B); + ret = bf_pow_ui_ui(B, radix, P, BF_PREC_INF, BF_RNDZ); + if (ret) { + bf_delete(B); + return ret; + } + res = bf_cmpu(r, B); + bf_delete(B); + if (res < 0) + break; + /* try a larger exponent */ + E++; + } + *pE = E; + return 0; +} + +static void limb_to_a(char *buf, limb_t n, unsigned int radix, int len) +{ + int digit, i; + + if (radix == 10) { + /* specific case with constant divisor */ + for(i = len - 1; i >= 0; i--) { + digit = (limb_t)n % 10; + n = (limb_t)n / 10; + buf[i] = digit + '0'; + } + } else { + for(i = len - 1; i >= 0; i--) { + digit = (limb_t)n % radix; + n = (limb_t)n / radix; + if (digit < 10) + digit += '0'; + else + digit += 'a' - 10; + buf[i] = digit; + } + } +} + +/* for power of 2 radixes */ +static void limb_to_a2(char *buf, limb_t n, unsigned int radix_bits, int len) +{ + int digit, i; + unsigned int mask; + + mask = (1 << radix_bits) - 1; + for(i = len - 1; i >= 0; i--) { + digit = n & mask; + n >>= radix_bits; + if (digit < 10) + digit += '0'; + else + digit += 'a' - 10; + buf[i] = digit; + } +} + +/* 'a' must be an integer if the is_dec = FALSE or if the radix is not + a power of two. A dot is added before the 'dot_pos' digit. dot_pos + = n_digits does not display the dot. 0 <= dot_pos <= + n_digits. n_digits >= 1. */ +static void output_digits(DynBuf *s, const bf_t *a1, int radix, limb_t n_digits, + limb_t dot_pos, BOOL is_dec) +{ + limb_t i, v, l; + slimb_t pos, pos_incr; + int digits_per_limb, buf_pos, radix_bits, first_buf_pos; + char buf[65]; + bf_t a_s, *a; + + if (is_dec) { + digits_per_limb = LIMB_DIGITS; + a = (bf_t *)a1; + radix_bits = 0; + pos = a->len; + pos_incr = 1; + first_buf_pos = 0; + } else if ((radix & (radix - 1)) == 0) { + a = (bf_t *)a1; + radix_bits = ceil_log2(radix); + digits_per_limb = LIMB_BITS / radix_bits; + pos_incr = digits_per_limb * radix_bits; + /* digits are aligned relative to the radix point */ + pos = a->len * LIMB_BITS + smod(-a->expn, radix_bits); + first_buf_pos = 0; + } else { + limb_t n, radixl; + + digits_per_limb = digits_per_limb_table[radix - 2]; + radixl = get_limb_radix(radix); + a = &a_s; + bf_init(a1->ctx, a); + n = (n_digits + digits_per_limb - 1) / digits_per_limb; + if (bf_resize(a, n)) { + dbuf_set_error(s); + goto done; + } + if (bf_integer_to_radix(a, a1, radixl)) { + dbuf_set_error(s); + goto done; + } + radix_bits = 0; + pos = n; + pos_incr = 1; + first_buf_pos = pos * digits_per_limb - n_digits; + } + buf_pos = digits_per_limb; + i = 0; + while (i < n_digits) { + if (buf_pos == digits_per_limb) { + pos -= pos_incr; + if (radix_bits == 0) { + v = get_limbz(a, pos); + limb_to_a(buf, v, radix, digits_per_limb); + } else { + v = get_bits(a->tab, a->len, pos); + limb_to_a2(buf, v, radix_bits, digits_per_limb); + } + buf_pos = first_buf_pos; + first_buf_pos = 0; + } + if (i < dot_pos) { + l = dot_pos; + } else { + if (i == dot_pos) + dbuf_putc(s, '.'); + l = n_digits; + } + l = bf_min(digits_per_limb - buf_pos, l - i); + dbuf_put(s, (uint8_t *)(buf + buf_pos), l); + buf_pos += l; + i += l; + } + done: + if (a != a1) + bf_delete(a); +} + +static void *bf_dbuf_realloc(void *opaque, void *ptr, size_t size) +{ + bf_context_t *s = opaque; + return bf_realloc(s, ptr, size); +} + +/* return the length in bytes. A trailing '\0' is added */ +static char *bf_ftoa_internal(size_t *plen, const bf_t *a2, int radix, + limb_t prec, bf_flags_t flags, BOOL is_dec) +{ + bf_context_t *ctx = a2->ctx; + DynBuf s_s, *s = &s_s; + int radix_bits; + + // bf_print_str("ftoa", a2); + // printf("radix=%d\n", radix); + dbuf_init2(s, ctx, bf_dbuf_realloc); + if (a2->expn == BF_EXP_NAN) { + dbuf_putstr(s, "NaN"); + } else { + if (a2->sign) + dbuf_putc(s, '-'); + if (a2->expn == BF_EXP_INF) { + if (flags & BF_FTOA_JS_QUIRKS) + dbuf_putstr(s, "Infinity"); + else + dbuf_putstr(s, "Inf"); + } else { + int fmt, ret; + slimb_t n_digits, n, i, n_max, n1; + bf_t a1_s, *a1 = &a1_s; + + if ((radix & (radix - 1)) != 0) + radix_bits = 0; + else + radix_bits = ceil_log2(radix); + + fmt = flags & BF_FTOA_FORMAT_MASK; + bf_init(ctx, a1); + if (fmt == BF_FTOA_FORMAT_FRAC) { + if (is_dec || radix_bits != 0) { + if (bf_set(a1, a2)) + goto fail1; +#ifdef USE_BF_DEC + if (is_dec) { + if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR) + goto fail1; + n = a1->expn; + } else +#endif + { + if (bf_round(a1, prec * radix_bits, (flags & BF_RND_MASK) | BF_FLAG_RADPNT_PREC) & BF_ST_MEM_ERROR) + goto fail1; + n = ceil_div(a1->expn, radix_bits); + } + if (flags & BF_FTOA_ADD_PREFIX) { + if (radix == 16) + dbuf_putstr(s, "0x"); + else if (radix == 8) + dbuf_putstr(s, "0o"); + else if (radix == 2) + dbuf_putstr(s, "0b"); + } + if (a1->expn == BF_EXP_ZERO) { + dbuf_putstr(s, "0"); + if (prec > 0) { + dbuf_putstr(s, "."); + for(i = 0; i < prec; i++) { + dbuf_putc(s, '0'); + } + } + } else { + n_digits = prec + n; + if (n <= 0) { + /* 0.x */ + dbuf_putstr(s, "0."); + for(i = 0; i < -n; i++) { + dbuf_putc(s, '0'); + } + if (n_digits > 0) { + output_digits(s, a1, radix, n_digits, n_digits, is_dec); + } + } else { + output_digits(s, a1, radix, n_digits, n, is_dec); + } + } + } else { + size_t pos, start; + bf_t a_s, *a = &a_s; + + /* make a positive number */ + a->tab = a2->tab; + a->len = a2->len; + a->expn = a2->expn; + a->sign = 0; + + /* one more digit for the rounding */ + n = 1 + bf_mul_log2_radix(bf_max(a->expn, 0), radix, TRUE, TRUE); + n_digits = n + prec; + n1 = n; + if (bf_convert_to_radix(a1, &n1, a, radix, n_digits, + flags & BF_RND_MASK, TRUE)) + goto fail1; + start = s->size; + output_digits(s, a1, radix, n_digits, n, is_dec); + /* remove leading zeros because we allocated one more digit */ + pos = start; + while ((pos + 1) < s->size && s->buf[pos] == '0' && + s->buf[pos + 1] != '.') + pos++; + if (pos > start) { + memmove(s->buf + start, s->buf + pos, s->size - pos); + s->size -= (pos - start); + } + } + } else { +#ifdef USE_BF_DEC + if (is_dec) { + if (bf_set(a1, a2)) + goto fail1; + if (fmt == BF_FTOA_FORMAT_FIXED) { + n_digits = prec; + n_max = n_digits; + if (bfdec_round((bfdec_t *)a1, prec, (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR) + goto fail1; + } else { + /* prec is ignored */ + prec = n_digits = a1->len * LIMB_DIGITS; + /* remove the trailing zero digits */ + while (n_digits > 1 && + get_digit(a1->tab, a1->len, prec - n_digits) == 0) { + n_digits--; + } + n_max = n_digits + 4; + } + n = a1->expn; + } else +#endif + if (radix_bits != 0) { + if (bf_set(a1, a2)) + goto fail1; + if (fmt == BF_FTOA_FORMAT_FIXED) { + slimb_t prec_bits; + n_digits = prec; + n_max = n_digits; + /* align to the radix point */ + prec_bits = prec * radix_bits - + smod(-a1->expn, radix_bits); + if (bf_round(a1, prec_bits, + (flags & BF_RND_MASK)) & BF_ST_MEM_ERROR) + goto fail1; + } else { + limb_t digit_mask; + slimb_t pos; + /* position of the digit before the most + significant digit in bits */ + pos = a1->len * LIMB_BITS + + smod(-a1->expn, radix_bits); + n_digits = ceil_div(pos, radix_bits); + /* remove the trailing zero digits */ + digit_mask = ((limb_t)1 << radix_bits) - 1; + while (n_digits > 1 && + (get_bits(a1->tab, a1->len, pos - n_digits * radix_bits) & digit_mask) == 0) { + n_digits--; + } + n_max = n_digits + 4; + } + n = ceil_div(a1->expn, radix_bits); + } else { + bf_t a_s, *a = &a_s; + + /* make a positive number */ + a->tab = a2->tab; + a->len = a2->len; + a->expn = a2->expn; + a->sign = 0; + + if (fmt == BF_FTOA_FORMAT_FIXED) { + n_digits = prec; + n_max = n_digits; + } else { + slimb_t n_digits_max, n_digits_min; + + assert(prec != BF_PREC_INF); + n_digits = 1 + bf_mul_log2_radix(prec, radix, TRUE, TRUE); + /* max number of digits for non exponential + notation. The rational is to have the same rule + as JS i.e. n_max = 21 for 64 bit float in base 10. */ + n_max = n_digits + 4; + if (fmt == BF_FTOA_FORMAT_FREE_MIN) { + bf_t b_s, *b = &b_s; + + /* find the minimum number of digits by + dichotomy. */ + /* XXX: inefficient */ + n_digits_max = n_digits; + n_digits_min = 1; + bf_init(ctx, b); + while (n_digits_min < n_digits_max) { + n_digits = (n_digits_min + n_digits_max) / 2; + if (bf_convert_to_radix(a1, &n, a, radix, n_digits, + flags & BF_RND_MASK, FALSE)) { + bf_delete(b); + goto fail1; + } + /* convert back to a number and compare */ + ret = bf_mul_pow_radix(b, a1, radix, n - n_digits, + prec, + (flags & ~BF_RND_MASK) | + BF_RNDN); + if (ret & BF_ST_MEM_ERROR) { + bf_delete(b); + goto fail1; + } + if (bf_cmpu(b, a) == 0) { + n_digits_max = n_digits; + } else { + n_digits_min = n_digits + 1; + } + } + bf_delete(b); + n_digits = n_digits_max; + } + } + if (bf_convert_to_radix(a1, &n, a, radix, n_digits, + flags & BF_RND_MASK, FALSE)) { + fail1: + bf_delete(a1); + goto fail; + } + } + if (a1->expn == BF_EXP_ZERO && + fmt != BF_FTOA_FORMAT_FIXED && + !(flags & BF_FTOA_FORCE_EXP)) { + /* just output zero */ + dbuf_putstr(s, "0"); + } else { + if (flags & BF_FTOA_ADD_PREFIX) { + if (radix == 16) + dbuf_putstr(s, "0x"); + else if (radix == 8) + dbuf_putstr(s, "0o"); + else if (radix == 2) + dbuf_putstr(s, "0b"); + } + if (a1->expn == BF_EXP_ZERO) + n = 1; + if ((flags & BF_FTOA_FORCE_EXP) || + n <= -6 || n > n_max) { + const char *fmt; + /* exponential notation */ + output_digits(s, a1, radix, n_digits, 1, is_dec); + if (radix_bits != 0 && radix <= 16) { + if (flags & BF_FTOA_JS_QUIRKS) + fmt = "p%+" PRId_LIMB; + else + fmt = "p%" PRId_LIMB; + dbuf_printf(s, fmt, (n - 1) * radix_bits); + } else { + if (flags & BF_FTOA_JS_QUIRKS) + fmt = "%c%+" PRId_LIMB; + else + fmt = "%c%" PRId_LIMB; + dbuf_printf(s, fmt, + radix <= 10 ? 'e' : '@', n - 1); + } + } else if (n <= 0) { + /* 0.x */ + dbuf_putstr(s, "0."); + for(i = 0; i < -n; i++) { + dbuf_putc(s, '0'); + } + output_digits(s, a1, radix, n_digits, n_digits, is_dec); + } else { + if (n_digits <= n) { + /* no dot */ + output_digits(s, a1, radix, n_digits, n_digits, is_dec); + for(i = 0; i < (n - n_digits); i++) + dbuf_putc(s, '0'); + } else { + output_digits(s, a1, radix, n_digits, n, is_dec); + } + } + } + } + bf_delete(a1); + } + } + dbuf_putc(s, '\0'); + if (dbuf_error(s)) + goto fail; + if (plen) + *plen = s->size - 1; + return (char *)s->buf; + fail: + bf_free(ctx, s->buf); + if (plen) + *plen = 0; + return NULL; +} + +char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec, + bf_flags_t flags) +{ + return bf_ftoa_internal(plen, a, radix, prec, flags, FALSE); +} + +/***************************************************************/ +/* transcendental functions */ + +/* Note: the algorithm is from MPFR */ +static void bf_const_log2_rec(bf_t *T, bf_t *P, bf_t *Q, limb_t n1, + limb_t n2, BOOL need_P) +{ + bf_context_t *s = T->ctx; + if ((n2 - n1) == 1) { + if (n1 == 0) { + bf_set_ui(P, 3); + } else { + bf_set_ui(P, n1); + P->sign = 1; + } + bf_set_ui(Q, 2 * n1 + 1); + Q->expn += 2; + bf_set(T, P); + } else { + limb_t m; + bf_t T1_s, *T1 = &T1_s; + bf_t P1_s, *P1 = &P1_s; + bf_t Q1_s, *Q1 = &Q1_s; + + m = n1 + ((n2 - n1) >> 1); + bf_const_log2_rec(T, P, Q, n1, m, TRUE); + bf_init(s, T1); + bf_init(s, P1); + bf_init(s, Q1); + bf_const_log2_rec(T1, P1, Q1, m, n2, need_P); + bf_mul(T, T, Q1, BF_PREC_INF, BF_RNDZ); + bf_mul(T1, T1, P, BF_PREC_INF, BF_RNDZ); + bf_add(T, T, T1, BF_PREC_INF, BF_RNDZ); + if (need_P) + bf_mul(P, P, P1, BF_PREC_INF, BF_RNDZ); + bf_mul(Q, Q, Q1, BF_PREC_INF, BF_RNDZ); + bf_delete(T1); + bf_delete(P1); + bf_delete(Q1); + } +} + +/* compute log(2) with faithful rounding at precision 'prec' */ +static void bf_const_log2_internal(bf_t *T, limb_t prec) +{ + limb_t w, N; + bf_t P_s, *P = &P_s; + bf_t Q_s, *Q = &Q_s; + + w = prec + 15; + N = w / 3 + 1; + bf_init(T->ctx, P); + bf_init(T->ctx, Q); + bf_const_log2_rec(T, P, Q, 0, N, FALSE); + bf_div(T, T, Q, prec, BF_RNDN); + bf_delete(P); + bf_delete(Q); +} + +/* PI constant */ + +#define CHUD_A 13591409 +#define CHUD_B 545140134 +#define CHUD_C 640320 +#define CHUD_BITS_PER_TERM 47 + +static void chud_bs(bf_t *P, bf_t *Q, bf_t *G, int64_t a, int64_t b, int need_g, + limb_t prec) +{ + bf_context_t *s = P->ctx; + int64_t c; + + if (a == (b - 1)) { + bf_t T0, T1; + + bf_init(s, &T0); + bf_init(s, &T1); + bf_set_ui(G, 2 * b - 1); + bf_mul_ui(G, G, 6 * b - 1, prec, BF_RNDN); + bf_mul_ui(G, G, 6 * b - 5, prec, BF_RNDN); + bf_set_ui(&T0, CHUD_B); + bf_mul_ui(&T0, &T0, b, prec, BF_RNDN); + bf_set_ui(&T1, CHUD_A); + bf_add(&T0, &T0, &T1, prec, BF_RNDN); + bf_mul(P, G, &T0, prec, BF_RNDN); + P->sign = b & 1; + + bf_set_ui(Q, b); + bf_mul_ui(Q, Q, b, prec, BF_RNDN); + bf_mul_ui(Q, Q, b, prec, BF_RNDN); + bf_mul_ui(Q, Q, (uint64_t)CHUD_C * CHUD_C * CHUD_C / 24, prec, BF_RNDN); + bf_delete(&T0); + bf_delete(&T1); + } else { + bf_t P2, Q2, G2; + + bf_init(s, &P2); + bf_init(s, &Q2); + bf_init(s, &G2); + + c = (a + b) / 2; + chud_bs(P, Q, G, a, c, 1, prec); + chud_bs(&P2, &Q2, &G2, c, b, need_g, prec); + + /* Q = Q1 * Q2 */ + /* G = G1 * G2 */ + /* P = P1 * Q2 + P2 * G1 */ + bf_mul(&P2, &P2, G, prec, BF_RNDN); + if (!need_g) + bf_set_ui(G, 0); + bf_mul(P, P, &Q2, prec, BF_RNDN); + bf_add(P, P, &P2, prec, BF_RNDN); + bf_delete(&P2); + + bf_mul(Q, Q, &Q2, prec, BF_RNDN); + bf_delete(&Q2); + if (need_g) + bf_mul(G, G, &G2, prec, BF_RNDN); + bf_delete(&G2); + } +} + +/* compute Pi with faithful rounding at precision 'prec' using the + Chudnovsky formula */ +static void bf_const_pi_internal(bf_t *Q, limb_t prec) +{ + bf_context_t *s = Q->ctx; + int64_t n, prec1; + bf_t P, G; + + /* number of serie terms */ + n = prec / CHUD_BITS_PER_TERM + 1; + /* XXX: precision analysis */ + prec1 = prec + 32; + + bf_init(s, &P); + bf_init(s, &G); + + chud_bs(&P, Q, &G, 0, n, 0, BF_PREC_INF); + + bf_mul_ui(&G, Q, CHUD_A, prec1, BF_RNDN); + bf_add(&P, &G, &P, prec1, BF_RNDN); + bf_div(Q, Q, &P, prec1, BF_RNDF); + + bf_set_ui(&P, CHUD_C); + bf_sqrt(&G, &P, prec1, BF_RNDF); + bf_mul_ui(&G, &G, (uint64_t)CHUD_C / 12, prec1, BF_RNDF); + bf_mul(Q, Q, &G, prec, BF_RNDN); + bf_delete(&P); + bf_delete(&G); +} + +static int bf_const_get(bf_t *T, limb_t prec, bf_flags_t flags, + BFConstCache *c, + void (*func)(bf_t *res, limb_t prec), int sign) +{ + limb_t ziv_extra_bits, prec1; + + ziv_extra_bits = 32; + for(;;) { + prec1 = prec + ziv_extra_bits; + if (c->prec < prec1) { + if (c->val.len == 0) + bf_init(T->ctx, &c->val); + func(&c->val, prec1); + c->prec = prec1; + } else { + prec1 = c->prec; + } + bf_set(T, &c->val); + T->sign = sign; + if (!bf_can_round(T, prec, flags & BF_RND_MASK, prec1)) { + /* and more precision and retry */ + ziv_extra_bits = ziv_extra_bits + (ziv_extra_bits / 2); + } else { + break; + } + } + return bf_round(T, prec, flags); +} + +static void bf_const_free(BFConstCache *c) +{ + bf_delete(&c->val); + memset(c, 0, sizeof(*c)); +} + +int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = T->ctx; + return bf_const_get(T, prec, flags, &s->log2_cache, bf_const_log2_internal, 0); +} + +/* return rounded pi * (1 - 2 * sign) */ +static int bf_const_pi_signed(bf_t *T, int sign, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = T->ctx; + return bf_const_get(T, prec, flags, &s->pi_cache, bf_const_pi_internal, + sign); +} + +int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags) +{ + return bf_const_pi_signed(T, 0, prec, flags); +} + +void bf_clear_cache(bf_context_t *s) +{ +#ifdef USE_FFT_MUL + fft_clear_cache(s); +#endif + bf_const_free(&s->log2_cache); + bf_const_free(&s->pi_cache); +} + +/* ZivFunc should compute the result 'r' with faithful rounding at + precision 'prec'. For efficiency purposes, the final bf_round() + does not need to be done in the function. */ +typedef int ZivFunc(bf_t *r, const bf_t *a, limb_t prec, void *opaque); + +static int bf_ziv_rounding(bf_t *r, const bf_t *a, + limb_t prec, bf_flags_t flags, + ZivFunc *f, void *opaque) +{ + int rnd_mode, ret; + slimb_t prec1, ziv_extra_bits; + + rnd_mode = flags & BF_RND_MASK; + if (rnd_mode == BF_RNDF) { + /* no need to iterate */ + f(r, a, prec, opaque); + ret = 0; + } else { + ziv_extra_bits = 32; + for(;;) { + prec1 = prec + ziv_extra_bits; + ret = f(r, a, prec1, opaque); + if (ret & (BF_ST_OVERFLOW | BF_ST_UNDERFLOW | BF_ST_MEM_ERROR)) { + /* overflow or underflow should never happen because + it indicates the rounding cannot be done correctly, + but we do not catch all the cases */ + return ret; + } + /* if the result is exact, we can stop */ + if (!(ret & BF_ST_INEXACT)) { + ret = 0; + break; + } + if (bf_can_round(r, prec, rnd_mode, prec1)) { + ret = BF_ST_INEXACT; + break; + } + ziv_extra_bits = ziv_extra_bits * 2; + // printf("ziv_extra_bits=%" PRId64 "\n", (int64_t)ziv_extra_bits); + } + } + if (r->len == 0) + return ret; + else + return __bf_round(r, prec, flags, r->len, ret); +} + +/* add (1 - 2*e_sign) * 2^e */ +static int bf_add_epsilon(bf_t *r, const bf_t *a, slimb_t e, int e_sign, + limb_t prec, int flags) +{ + bf_t T_s, *T = &T_s; + int ret; + /* small argument case: result = 1 + epsilon * sign(x) */ + bf_init(a->ctx, T); + bf_set_ui(T, 1); + T->sign = e_sign; + T->expn += e; + ret = bf_add(r, r, T, prec, flags); + bf_delete(T); + return ret; +} + +/* Compute the exponential using faithful rounding at precision 'prec'. + Note: the algorithm is from MPFR */ +static int bf_exp_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + slimb_t n, K, l, i, prec1; + + assert(r != a); + + /* argument reduction: + T = a - n*log(2) with 0 <= T < log(2) and n integer. + */ + bf_init(s, T); + if (a->expn <= -1) { + /* 0 <= abs(a) <= 0.5 */ + if (a->sign) + n = -1; + else + n = 0; + } else { + bf_const_log2(T, LIMB_BITS, BF_RNDZ); + bf_div(T, a, T, LIMB_BITS, BF_RNDD); + bf_get_limb(&n, T, 0); + } + + K = bf_isqrt((prec + 1) / 2); + l = (prec - 1) / K + 1; + /* XXX: precision analysis ? */ + prec1 = prec + (K + 2 * l + 18) + K + 8; + if (a->expn > 0) + prec1 += a->expn; + // printf("n=%ld K=%ld prec1=%ld\n", n, K, prec1); + + bf_const_log2(T, prec1, BF_RNDF); + bf_mul_si(T, T, n, prec1, BF_RNDN); + bf_sub(T, a, T, prec1, BF_RNDN); + + /* reduce the range of T */ + bf_mul_2exp(T, -K, BF_PREC_INF, BF_RNDZ); + + /* Taylor expansion around zero : + 1 + x + x^2/2 + ... + x^n/n! + = (1 + x * (1 + x/2 * (1 + ... (x/n)))) + */ + { + bf_t U_s, *U = &U_s; + + bf_init(s, U); + bf_set_ui(r, 1); + for(i = l ; i >= 1; i--) { + bf_set_ui(U, i); + bf_div(U, T, U, prec1, BF_RNDN); + bf_mul(r, r, U, prec1, BF_RNDN); + bf_add_si(r, r, 1, prec1, BF_RNDN); + } + bf_delete(U); + } + bf_delete(T); + + /* undo the range reduction */ + for(i = 0; i < K; i++) { + bf_mul(r, r, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP); + } + + /* undo the argument reduction */ + bf_mul_2exp(r, n, BF_PREC_INF, BF_RNDZ | BF_FLAG_EXT_EXP); + + return BF_ST_INEXACT; +} + +/* crude overflow and underflow tests for exp(a). a_low <= a <= a_high */ +static int check_exp_underflow_overflow(bf_context_t *s, bf_t *r, + const bf_t *a_low, const bf_t *a_high, + limb_t prec, bf_flags_t flags) +{ + bf_t T_s, *T = &T_s; + bf_t log2_s, *log2 = &log2_s; + slimb_t e_min, e_max; + + if (a_high->expn <= 0) + return 0; + + e_max = (limb_t)1 << (bf_get_exp_bits(flags) - 1); + e_min = -e_max + 3; + if (flags & BF_FLAG_SUBNORMAL) + e_min -= (prec - 1); + + bf_init(s, T); + bf_init(s, log2); + bf_const_log2(log2, LIMB_BITS, BF_RNDU); + bf_mul_ui(T, log2, e_max, LIMB_BITS, BF_RNDU); + /* a_low > e_max * log(2) implies exp(a) > e_max */ + if (bf_cmp_lt(T, a_low) > 0) { + /* overflow */ + bf_delete(T); + bf_delete(log2); + return bf_set_overflow(r, 0, prec, flags); + } + /* a_high < (e_min - 2) * log(2) implies exp(a) < (e_min - 2) */ + bf_const_log2(log2, LIMB_BITS, BF_RNDD); + bf_mul_si(T, log2, e_min - 2, LIMB_BITS, BF_RNDD); + if (bf_cmp_lt(a_high, T)) { + int rnd_mode = flags & BF_RND_MASK; + + /* underflow */ + bf_delete(T); + bf_delete(log2); + if (rnd_mode == BF_RNDU) { + /* set the smallest value */ + bf_set_ui(r, 1); + r->expn = e_min; + } else { + bf_set_zero(r, 0); + } + return BF_ST_UNDERFLOW | BF_ST_INEXACT; + } + bf_delete(log2); + bf_delete(T); + return 0; +} + +int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + int ret; + assert(r != a); + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + } else if (a->expn == BF_EXP_INF) { + if (a->sign) + bf_set_zero(r, 0); + else + bf_set_inf(r, 0); + } else { + bf_set_ui(r, 1); + } + return 0; + } + + ret = check_exp_underflow_overflow(s, r, a, a, prec, flags); + if (ret) + return ret; + if (a->expn < 0 && (-a->expn) >= (prec + 2)) { + /* small argument case: result = 1 + epsilon * sign(x) */ + bf_set_ui(r, 1); + return bf_add_epsilon(r, r, -(prec + 2), a->sign, prec, flags); + } + + return bf_ziv_rounding(r, a, prec, flags, bf_exp_internal, NULL); +} + +static int bf_log_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + bf_t U_s, *U = &U_s; + bf_t V_s, *V = &V_s; + slimb_t n, prec1, l, i, K; + + assert(r != a); + + bf_init(s, T); + /* argument reduction 1 */ + /* T=a*2^n with 2/3 <= T <= 4/3 */ + { + bf_t U_s, *U = &U_s; + bf_set(T, a); + n = T->expn; + T->expn = 0; + /* U= ~ 2/3 */ + bf_init(s, U); + bf_set_ui(U, 0xaaaaaaaa); + U->expn = 0; + if (bf_cmp_lt(T, U)) { + T->expn++; + n--; + } + bf_delete(U); + } + // printf("n=%ld\n", n); + // bf_print_str("T", T); + + /* XXX: precision analysis */ + /* number of iterations for argument reduction 2 */ + K = bf_isqrt((prec + 1) / 2); + /* order of Taylor expansion */ + l = prec / (2 * K) + 1; + /* precision of the intermediate computations */ + prec1 = prec + K + 2 * l + 32; + + bf_init(s, U); + bf_init(s, V); + + /* Note: cancellation occurs here, so we use more precision (XXX: + reduce the precision by computing the exact cancellation) */ + bf_add_si(T, T, -1, BF_PREC_INF, BF_RNDN); + + /* argument reduction 2 */ + for(i = 0; i < K; i++) { + /* T = T / (1 + sqrt(1 + T)) */ + bf_add_si(U, T, 1, prec1, BF_RNDN); + bf_sqrt(V, U, prec1, BF_RNDF); + bf_add_si(U, V, 1, prec1, BF_RNDN); + bf_div(T, T, U, prec1, BF_RNDN); + } + + { + bf_t Y_s, *Y = &Y_s; + bf_t Y2_s, *Y2 = &Y2_s; + bf_init(s, Y); + bf_init(s, Y2); + + /* compute ln(1+x) = ln((1+y)/(1-y)) with y=x/(2+x) + = y + y^3/3 + ... + y^(2*l + 1) / (2*l+1) + with Y=Y^2 + = y*(1+Y/3+Y^2/5+...) = y*(1+Y*(1/3+Y*(1/5 + ...))) + */ + bf_add_si(Y, T, 2, prec1, BF_RNDN); + bf_div(Y, T, Y, prec1, BF_RNDN); + + bf_mul(Y2, Y, Y, prec1, BF_RNDN); + bf_set_ui(r, 0); + for(i = l; i >= 1; i--) { + bf_set_ui(U, 1); + bf_set_ui(V, 2 * i + 1); + bf_div(U, U, V, prec1, BF_RNDN); + bf_add(r, r, U, prec1, BF_RNDN); + bf_mul(r, r, Y2, prec1, BF_RNDN); + } + bf_add_si(r, r, 1, prec1, BF_RNDN); + bf_mul(r, r, Y, prec1, BF_RNDN); + bf_delete(Y); + bf_delete(Y2); + } + bf_delete(V); + bf_delete(U); + + /* multiplication by 2 for the Taylor expansion and undo the + argument reduction 2*/ + bf_mul_2exp(r, K + 1, BF_PREC_INF, BF_RNDZ); + + /* undo the argument reduction 1 */ + bf_const_log2(T, prec1, BF_RNDF); + bf_mul_si(T, T, n, prec1, BF_RNDN); + bf_add(r, r, T, prec1, BF_RNDN); + + bf_delete(T); + return BF_ST_INEXACT; +} + +int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + + assert(r != a); + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + if (a->sign) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_inf(r, 0); + return 0; + } + } else { + bf_set_inf(r, 1); + return 0; + } + } + if (a->sign) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } + bf_init(s, T); + bf_set_ui(T, 1); + if (bf_cmp_eq(a, T)) { + bf_set_zero(r, 0); + bf_delete(T); + return 0; + } + bf_delete(T); + + return bf_ziv_rounding(r, a, prec, flags, bf_log_internal, NULL); +} + +/* x and y finite and x > 0 */ +static int bf_pow_generic(bf_t *r, const bf_t *x, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + const bf_t *y = opaque; + bf_t T_s, *T = &T_s; + limb_t prec1; + + bf_init(s, T); + /* XXX: proof for the added precision */ + prec1 = prec + 32; + bf_log(T, x, prec1, BF_RNDF | BF_FLAG_EXT_EXP); + bf_mul(T, T, y, prec1, BF_RNDF | BF_FLAG_EXT_EXP); + if (bf_is_nan(T)) + bf_set_nan(r); + else + bf_exp_internal(r, T, prec1, NULL); /* no overflow/underlow test needed */ + bf_delete(T); + return BF_ST_INEXACT; +} + +/* x and y finite, x > 0, y integer and y fits on one limb */ +static int bf_pow_int(bf_t *r, const bf_t *x, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + const bf_t *y = opaque; + bf_t T_s, *T = &T_s; + limb_t prec1; + int ret; + slimb_t y1; + + bf_get_limb(&y1, y, 0); + if (y1 < 0) + y1 = -y1; + /* XXX: proof for the added precision */ + prec1 = prec + ceil_log2(y1) * 2 + 8; + ret = bf_pow_ui(r, x, y1 < 0 ? -y1 : y1, prec1, BF_RNDN | BF_FLAG_EXT_EXP); + if (y->sign) { + bf_init(s, T); + bf_set_ui(T, 1); + ret |= bf_div(r, T, r, prec1, BF_RNDN | BF_FLAG_EXT_EXP); + bf_delete(T); + } + return ret; +} + +/* x must be a finite non zero float. Return TRUE if there is a + floating point number r such as x=r^(2^n) and return this floating + point number 'r'. Otherwise return FALSE and r is undefined. */ +static BOOL check_exact_power2n(bf_t *r, const bf_t *x, slimb_t n) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + slimb_t e, i, er; + limb_t v; + + /* x = m*2^e with m odd integer */ + e = bf_get_exp_min(x); + /* fast check on the exponent */ + if (n > (LIMB_BITS - 1)) { + if (e != 0) + return FALSE; + er = 0; + } else { + if ((e & (((limb_t)1 << n) - 1)) != 0) + return FALSE; + er = e >> n; + } + /* every perfect odd square = 1 modulo 8 */ + v = get_bits(x->tab, x->len, x->len * LIMB_BITS - x->expn + e); + if ((v & 7) != 1) + return FALSE; + + bf_init(s, T); + bf_set(T, x); + T->expn -= e; + for(i = 0; i < n; i++) { + if (i != 0) + bf_set(T, r); + if (bf_sqrtrem(r, NULL, T) != 0) + return FALSE; + } + r->expn += er; + return TRUE; +} + +/* prec = BF_PREC_INF is accepted for x and y integers and y >= 0 */ +int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + bf_t ytmp_s; + BOOL y_is_int, y_is_odd; + int r_sign, ret, rnd_mode; + slimb_t y_emin; + + if (x->len == 0 || y->len == 0) { + if (y->expn == BF_EXP_ZERO) { + /* pow(x, 0) = 1 */ + bf_set_ui(r, 1); + } else if (x->expn == BF_EXP_NAN) { + bf_set_nan(r); + } else { + int cmp_x_abs_1; + bf_set_ui(r, 1); + cmp_x_abs_1 = bf_cmpu(x, r); + if (cmp_x_abs_1 == 0 && (flags & BF_POW_JS_QUIRKS) && + (y->expn >= BF_EXP_INF)) { + bf_set_nan(r); + } else if (cmp_x_abs_1 == 0 && + (!x->sign || y->expn != BF_EXP_NAN)) { + /* pow(1, y) = 1 even if y = NaN */ + /* pow(-1, +/-inf) = 1 */ + } else if (y->expn == BF_EXP_NAN) { + bf_set_nan(r); + } else if (y->expn == BF_EXP_INF) { + if (y->sign == (cmp_x_abs_1 > 0)) { + bf_set_zero(r, 0); + } else { + bf_set_inf(r, 0); + } + } else { + y_emin = bf_get_exp_min(y); + y_is_odd = (y_emin == 0); + if (y->sign == (x->expn == BF_EXP_ZERO)) { + bf_set_inf(r, y_is_odd & x->sign); + if (y->sign) { + /* pow(0, y) with y < 0 */ + return BF_ST_DIVIDE_ZERO; + } + } else { + bf_set_zero(r, y_is_odd & x->sign); + } + } + } + return 0; + } + bf_init(s, T); + bf_set(T, x); + y_emin = bf_get_exp_min(y); + y_is_int = (y_emin >= 0); + rnd_mode = flags & BF_RND_MASK; + if (x->sign) { + if (!y_is_int) { + bf_set_nan(r); + bf_delete(T); + return BF_ST_INVALID_OP; + } + y_is_odd = (y_emin == 0); + r_sign = y_is_odd; + /* change the directed rounding mode if the sign of the result + is changed */ + if (r_sign && (rnd_mode == BF_RNDD || rnd_mode == BF_RNDU)) + flags ^= 1; + bf_neg(T); + } else { + r_sign = 0; + } + + bf_set_ui(r, 1); + if (bf_cmp_eq(T, r)) { + /* abs(x) = 1: nothing more to do */ + ret = 0; + } else { + /* check the overflow/underflow cases */ + { + bf_t al_s, *al = &al_s; + bf_t ah_s, *ah = &ah_s; + limb_t precl = LIMB_BITS; + + bf_init(s, al); + bf_init(s, ah); + /* compute bounds of log(abs(x)) * y with a low precision */ + /* XXX: compute bf_log() once */ + /* XXX: add a fast test before this slow test */ + bf_log(al, T, precl, BF_RNDD); + bf_log(ah, T, precl, BF_RNDU); + bf_mul(al, al, y, precl, BF_RNDD ^ y->sign); + bf_mul(ah, ah, y, precl, BF_RNDU ^ y->sign); + ret = check_exp_underflow_overflow(s, r, al, ah, prec, flags); + bf_delete(al); + bf_delete(ah); + if (ret) + goto done; + } + + if (y_is_int) { + slimb_t T_bits, e; + int_pow: + T_bits = T->expn - bf_get_exp_min(T); + if (T_bits == 1) { + /* pow(2^b, y) = 2^(b*y) */ + bf_mul_si(T, y, T->expn - 1, LIMB_BITS, BF_RNDZ); + bf_get_limb(&e, T, 0); + bf_set_ui(r, 1); + ret = bf_mul_2exp(r, e, prec, flags); + } else if (prec == BF_PREC_INF) { + slimb_t y1; + /* specific case for infinite precision (integer case) */ + bf_get_limb(&y1, y, 0); + assert(!y->sign); + /* x must be an integer, so abs(x) >= 2 */ + if (y1 >= ((slimb_t)1 << BF_EXP_BITS_MAX)) { + bf_delete(T); + return bf_set_overflow(r, 0, BF_PREC_INF, flags); + } + ret = bf_pow_ui(r, T, y1, BF_PREC_INF, BF_RNDZ); + } else { + if (y->expn <= 31) { + /* small enough power: use exponentiation in all cases */ + } else if (y->sign) { + /* cannot be exact */ + goto general_case; + } else { + if (rnd_mode == BF_RNDF) + goto general_case; /* no need to track exact results */ + /* see if the result has a chance to be exact: + if x=a*2^b (a odd), x^y=a^y*2^(b*y) + x^y needs a precision of at least floor_log2(a)*y bits + */ + bf_mul_si(r, y, T_bits - 1, LIMB_BITS, BF_RNDZ); + bf_get_limb(&e, r, 0); + if (prec < e) + goto general_case; + } + ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_int, (void *)y); + } + } else { + if (rnd_mode != BF_RNDF) { + bf_t *y1; + if (y_emin < 0 && check_exact_power2n(r, T, -y_emin)) { + /* the problem is reduced to a power to an integer */ +#if 0 + printf("\nn=%" PRId64 "\n", -(int64_t)y_emin); + bf_print_str("T", T); + bf_print_str("r", r); +#endif + bf_set(T, r); + y1 = &ytmp_s; + y1->tab = y->tab; + y1->len = y->len; + y1->sign = y->sign; + y1->expn = y->expn - y_emin; + y = y1; + goto int_pow; + } + } + general_case: + ret = bf_ziv_rounding(r, T, prec, flags, bf_pow_generic, (void *)y); + } + } + done: + bf_delete(T); + r->sign = r_sign; + return ret; +} + +/* compute sqrt(-2*x-x^2) to get |sin(x)| from cos(x) - 1. */ +static void bf_sqrt_sin(bf_t *r, const bf_t *x, limb_t prec1) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + bf_init(s, T); + bf_set(T, x); + bf_mul(r, T, T, prec1, BF_RNDN); + bf_mul_2exp(T, 1, BF_PREC_INF, BF_RNDZ); + bf_add(T, T, r, prec1, BF_RNDN); + bf_neg(T); + bf_sqrt(r, T, prec1, BF_RNDF); + bf_delete(T); +} + +static int bf_sincos(bf_t *s, bf_t *c, const bf_t *a, limb_t prec) +{ + bf_context_t *s1 = a->ctx; + bf_t T_s, *T = &T_s; + bf_t U_s, *U = &U_s; + bf_t r_s, *r = &r_s; + slimb_t K, prec1, i, l, mod, prec2; + int is_neg; + + assert(c != a && s != a); + + bf_init(s1, T); + bf_init(s1, U); + bf_init(s1, r); + + /* XXX: precision analysis */ + K = bf_isqrt(prec / 2); + l = prec / (2 * K) + 1; + prec1 = prec + 2 * K + l + 8; + + /* after the modulo reduction, -pi/4 <= T <= pi/4 */ + if (a->expn <= -1) { + /* abs(a) <= 0.25: no modulo reduction needed */ + bf_set(T, a); + mod = 0; + } else { + slimb_t cancel; + cancel = 0; + for(;;) { + prec2 = prec1 + a->expn + cancel; + bf_const_pi(U, prec2, BF_RNDF); + bf_mul_2exp(U, -1, BF_PREC_INF, BF_RNDZ); + bf_remquo(&mod, T, a, U, prec2, BF_RNDN, BF_RNDN); + // printf("T.expn=%ld prec2=%ld\n", T->expn, prec2); + if (mod == 0 || (T->expn != BF_EXP_ZERO && + (T->expn + prec2) >= (prec1 - 1))) + break; + /* increase the number of bits until the precision is good enough */ + cancel = bf_max(-T->expn, (cancel + 1) * 3 / 2); + } + mod &= 3; + } + + is_neg = T->sign; + + /* compute cosm1(x) = cos(x) - 1 */ + bf_mul(T, T, T, prec1, BF_RNDN); + bf_mul_2exp(T, -2 * K, BF_PREC_INF, BF_RNDZ); + + /* Taylor expansion: + -x^2/2 + x^4/4! - x^6/6! + ... + */ + bf_set_ui(r, 1); + for(i = l ; i >= 1; i--) { + bf_set_ui(U, 2 * i - 1); + bf_mul_ui(U, U, 2 * i, BF_PREC_INF, BF_RNDZ); + bf_div(U, T, U, prec1, BF_RNDN); + bf_mul(r, r, U, prec1, BF_RNDN); + bf_neg(r); + if (i != 1) + bf_add_si(r, r, 1, prec1, BF_RNDN); + } + bf_delete(U); + + /* undo argument reduction: + cosm1(2*x)= 2*(2*cosm1(x)+cosm1(x)^2) + */ + for(i = 0; i < K; i++) { + bf_mul(T, r, r, prec1, BF_RNDN); + bf_mul_2exp(r, 1, BF_PREC_INF, BF_RNDZ); + bf_add(r, r, T, prec1, BF_RNDN); + bf_mul_2exp(r, 1, BF_PREC_INF, BF_RNDZ); + } + bf_delete(T); + + if (c) { + if ((mod & 1) == 0) { + bf_add_si(c, r, 1, prec1, BF_RNDN); + } else { + bf_sqrt_sin(c, r, prec1); + c->sign = is_neg ^ 1; + } + c->sign ^= mod >> 1; + } + if (s) { + if ((mod & 1) == 0) { + bf_sqrt_sin(s, r, prec1); + s->sign = is_neg; + } else { + bf_add_si(s, r, 1, prec1, BF_RNDN); + } + s->sign ^= mod >> 1; + } + bf_delete(r); + return BF_ST_INEXACT; +} + +static int bf_cos_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + return bf_sincos(NULL, r, a, prec); +} + +int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_ui(r, 1); + return 0; + } + } + + /* small argument case: result = 1+r(x) with r(x) = -x^2/2 + + O(X^4). We assume r(x) < 2^(2*EXP(x) - 1). */ + if (a->expn < 0) { + slimb_t e; + e = 2 * a->expn - 1; + if (e < -(prec + 2)) { + bf_set_ui(r, 1); + return bf_add_epsilon(r, r, e, 1, prec, flags); + } + } + + return bf_ziv_rounding(r, a, prec, flags, bf_cos_internal, NULL); +} + +static int bf_sin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + return bf_sincos(r, NULL, a, prec); +} + +int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + + /* small argument case: result = x+r(x) with r(x) = -x^3/6 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 2); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags); + } + } + + return bf_ziv_rounding(r, a, prec, flags, bf_sin_internal, NULL); +} + +static int bf_tan_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + limb_t prec1; + + /* XXX: precision analysis */ + prec1 = prec + 8; + bf_init(s, T); + bf_sincos(r, T, a, prec1); + bf_div(r, r, T, prec1, BF_RNDF); + bf_delete(T); + return BF_ST_INEXACT; +} + +int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + assert(r != a); + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + + /* small argument case: result = x+r(x) with r(x) = x^3/3 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 1); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, a->sign, prec, flags); + } + } + + return bf_ziv_rounding(r, a, prec, flags, bf_tan_internal, NULL); +} + +/* if add_pi2 is true, add pi/2 to the result (used for acos(x) to + avoid cancellation) */ +static int bf_atan_internal(bf_t *r, const bf_t *a, limb_t prec, + void *opaque) +{ + bf_context_t *s = r->ctx; + BOOL add_pi2 = (BOOL)(intptr_t)opaque; + bf_t T_s, *T = &T_s; + bf_t U_s, *U = &U_s; + bf_t V_s, *V = &V_s; + bf_t X2_s, *X2 = &X2_s; + int cmp_1; + slimb_t prec1, i, K, l; + + /* XXX: precision analysis */ + K = bf_isqrt((prec + 1) / 2); + l = prec / (2 * K) + 1; + prec1 = prec + K + 2 * l + 32; + // printf("prec=%d K=%d l=%d prec1=%d\n", (int)prec, (int)K, (int)l, (int)prec1); + + bf_init(s, T); + cmp_1 = (a->expn >= 1); /* a >= 1 */ + if (cmp_1) { + bf_set_ui(T, 1); + bf_div(T, T, a, prec1, BF_RNDN); + } else { + bf_set(T, a); + } + + /* abs(T) <= 1 */ + + /* argument reduction */ + + bf_init(s, U); + bf_init(s, V); + bf_init(s, X2); + for(i = 0; i < K; i++) { + /* T = T / (1 + sqrt(1 + T^2)) */ + bf_mul(U, T, T, prec1, BF_RNDN); + bf_add_si(U, U, 1, prec1, BF_RNDN); + bf_sqrt(V, U, prec1, BF_RNDN); + bf_add_si(V, V, 1, prec1, BF_RNDN); + bf_div(T, T, V, prec1, BF_RNDN); + } + + /* Taylor series: + x - x^3/3 + ... + (-1)^ l * y^(2*l + 1) / (2*l+1) + */ + bf_mul(X2, T, T, prec1, BF_RNDN); + bf_set_ui(r, 0); + for(i = l; i >= 1; i--) { + bf_set_si(U, 1); + bf_set_ui(V, 2 * i + 1); + bf_div(U, U, V, prec1, BF_RNDN); + bf_neg(r); + bf_add(r, r, U, prec1, BF_RNDN); + bf_mul(r, r, X2, prec1, BF_RNDN); + } + bf_neg(r); + bf_add_si(r, r, 1, prec1, BF_RNDN); + bf_mul(r, r, T, prec1, BF_RNDN); + + /* undo the argument reduction */ + bf_mul_2exp(r, K, BF_PREC_INF, BF_RNDZ); + + bf_delete(U); + bf_delete(V); + bf_delete(X2); + + i = add_pi2; + if (cmp_1 > 0) { + /* undo the inversion : r = sign(a)*PI/2 - r */ + bf_neg(r); + i += 1 - 2 * a->sign; + } + /* add i*(pi/2) with -1 <= i <= 2 */ + if (i != 0) { + bf_const_pi(T, prec1, BF_RNDF); + if (i != 2) + bf_mul_2exp(T, -1, BF_PREC_INF, BF_RNDZ); + T->sign = (i < 0); + bf_add(r, T, r, prec1, BF_RNDN); + } + + bf_delete(T); + return BF_ST_INEXACT; +} + +int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + int res; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + /* -PI/2 or PI/2 */ + bf_const_pi_signed(r, a->sign, prec, flags); + bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ); + return BF_ST_INEXACT; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + + bf_init(s, T); + bf_set_ui(T, 1); + res = bf_cmpu(a, T); + bf_delete(T); + if (res == 0) { + /* short cut: abs(a) == 1 -> +/-pi/4 */ + bf_const_pi_signed(r, a->sign, prec, flags); + bf_mul_2exp(r, -2, BF_PREC_INF, BF_RNDZ); + return BF_ST_INEXACT; + } + + /* small argument case: result = x+r(x) with r(x) = -x^3/3 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 1). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 1); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, 1 - a->sign, prec, flags); + } + } + + return bf_ziv_rounding(r, a, prec, flags, bf_atan_internal, (void *)FALSE); +} + +static int bf_atan2_internal(bf_t *r, const bf_t *y, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + const bf_t *x = opaque; + bf_t T_s, *T = &T_s; + limb_t prec1; + int ret; + + if (y->expn == BF_EXP_NAN || x->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } + + /* compute atan(y/x) assumming inf/inf = 1 and 0/0 = 0 */ + bf_init(s, T); + prec1 = prec + 32; + if (y->expn == BF_EXP_INF && x->expn == BF_EXP_INF) { + bf_set_ui(T, 1); + T->sign = y->sign ^ x->sign; + } else if (y->expn == BF_EXP_ZERO && x->expn == BF_EXP_ZERO) { + bf_set_zero(T, y->sign ^ x->sign); + } else { + bf_div(T, y, x, prec1, BF_RNDF); + } + ret = bf_atan(r, T, prec1, BF_RNDF); + + if (x->sign) { + /* if x < 0 (it includes -0), return sign(y)*pi + atan(y/x) */ + bf_const_pi(T, prec1, BF_RNDF); + T->sign = y->sign; + bf_add(r, r, T, prec1, BF_RNDN); + ret |= BF_ST_INEXACT; + } + + bf_delete(T); + return ret; +} + +int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x, + limb_t prec, bf_flags_t flags) +{ + return bf_ziv_rounding(r, y, prec, flags, bf_atan2_internal, (void *)x); +} + +static int bf_asin_internal(bf_t *r, const bf_t *a, limb_t prec, void *opaque) +{ + bf_context_t *s = r->ctx; + BOOL is_acos = (BOOL)(intptr_t)opaque; + bf_t T_s, *T = &T_s; + limb_t prec1, prec2; + + /* asin(x) = atan(x/sqrt(1-x^2)) + acos(x) = pi/2 - asin(x) */ + prec1 = prec + 8; + /* increase the precision in x^2 to compensate the cancellation in + (1-x^2) if x is close to 1 */ + /* XXX: use less precision when possible */ + if (a->expn >= 0) + prec2 = BF_PREC_INF; + else + prec2 = prec1; + bf_init(s, T); + bf_mul(T, a, a, prec2, BF_RNDN); + bf_neg(T); + bf_add_si(T, T, 1, prec2, BF_RNDN); + + bf_sqrt(r, T, prec1, BF_RNDN); + bf_div(T, a, r, prec1, BF_RNDN); + if (is_acos) + bf_neg(T); + bf_atan_internal(r, T, prec1, (void *)(intptr_t)is_acos); + bf_delete(T); + return BF_ST_INEXACT; +} + +int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + int res; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_set_zero(r, a->sign); + return 0; + } + } + bf_init(s, T); + bf_set_ui(T, 1); + res = bf_cmpu(a, T); + bf_delete(T); + if (res > 0) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } + + /* small argument case: result = x+r(x) with r(x) = x^3/6 + + O(X^5). We assume r(x) < 2^(3*EXP(x) - 2). */ + if (a->expn < 0) { + slimb_t e; + e = sat_add(2 * a->expn, a->expn - 2); + if (e < a->expn - bf_max(prec + 2, a->len * LIMB_BITS + 2)) { + bf_set(r, a); + return bf_add_epsilon(r, r, e, a->sign, prec, flags); + } + } + + return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)FALSE); +} + +int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = r->ctx; + bf_t T_s, *T = &T_s; + int res; + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bf_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bf_const_pi(r, prec, flags); + bf_mul_2exp(r, -1, BF_PREC_INF, BF_RNDZ); + return BF_ST_INEXACT; + } + } + bf_init(s, T); + bf_set_ui(T, 1); + res = bf_cmpu(a, T); + bf_delete(T); + if (res > 0) { + bf_set_nan(r); + return BF_ST_INVALID_OP; + } else if (res == 0 && a->sign == 0) { + bf_set_zero(r, 0); + return 0; + } + + return bf_ziv_rounding(r, a, prec, flags, bf_asin_internal, (void *)TRUE); +} + +/***************************************************************/ +/* decimal floating point numbers */ + +#ifdef USE_BF_DEC + +#define adddq(r1, r0, a1, a0) \ + do { \ + limb_t __t = r0; \ + r0 += (a0); \ + r1 += (a1) + (r0 < __t); \ + } while (0) + +#define subdq(r1, r0, a1, a0) \ + do { \ + limb_t __t = r0; \ + r0 -= (a0); \ + r1 -= (a1) + (r0 > __t); \ + } while (0) + +#if LIMB_BITS == 64 + +/* Note: we assume __int128 is available */ +#define muldq(r1, r0, a, b) \ + do { \ + unsigned __int128 __t; \ + __t = (unsigned __int128)(a) * (unsigned __int128)(b); \ + r0 = __t; \ + r1 = __t >> 64; \ + } while (0) + +#define divdq(q, r, a1, a0, b) \ + do { \ + unsigned __int128 __t; \ + limb_t __b = (b); \ + __t = ((unsigned __int128)(a1) << 64) | (a0); \ + q = __t / __b; \ + r = __t % __b; \ + } while (0) + +#else + +#define muldq(r1, r0, a, b) \ + do { \ + uint64_t __t; \ + __t = (uint64_t)(a) * (uint64_t)(b); \ + r0 = __t; \ + r1 = __t >> 32; \ + } while (0) + +#define divdq(q, r, a1, a0, b) \ + do { \ + uint64_t __t; \ + limb_t __b = (b); \ + __t = ((uint64_t)(a1) << 32) | (a0); \ + q = __t / __b; \ + r = __t % __b; \ + } while (0) + +#endif /* LIMB_BITS != 64 */ + +#if LIMB_DIGITS == 19 + +/* WARNING: hardcoded for b = 1e19. It is assumed that: + 0 <= a1 < 2^63 */ +#define divdq_base(q, r, a1, a0)\ +do {\ + uint64_t __a0, __a1, __t0, __t1, __b = BF_DEC_BASE; \ + __a0 = a0;\ + __a1 = a1;\ + __t0 = __a1;\ + __t0 = shld(__t0, __a0, 1);\ + muldq(q, __t1, __t0, UINT64_C(17014118346046923173)); \ + muldq(__t1, __t0, q, __b);\ + subdq(__a1, __a0, __t1, __t0);\ + subdq(__a1, __a0, 1, __b * 2); \ + __t0 = (slimb_t)__a1 >> 1; \ + q += 2 + __t0;\ + adddq(__a1, __a0, 0, __b & __t0);\ + q += __a1; \ + __a0 += __b & __a1; \ + r = __a0;\ +} while(0) + +#elif LIMB_DIGITS == 9 + +/* WARNING: hardcoded for b = 1e9. It is assumed that: + 0 <= a1 < 2^29 */ +#define divdq_base(q, r, a1, a0)\ +do {\ + uint32_t __t0, __t1, __b = BF_DEC_BASE; \ + __t0 = a1;\ + __t1 = a0;\ + __t0 = (__t0 << 3) | (__t1 >> (32 - 3)); \ + muldq(q, __t1, __t0, 2305843009U);\ + r = a0 - q * __b;\ + __t1 = (r >= __b);\ + q += __t1;\ + if (__t1)\ + r -= __b;\ +} while(0) + +#endif + +/* fast integer division by a fixed constant */ + +typedef struct FastDivData { + limb_t m1; /* multiplier */ + int8_t shift1; + int8_t shift2; +} FastDivData; + +/* From "Division by Invariant Integers using Multiplication" by + Torborn Granlund and Peter L. Montgomery */ +/* d must be != 0 */ +static inline __maybe_unused void fast_udiv_init(FastDivData *s, limb_t d) +{ + int l; + limb_t q, r, m1; + if (d == 1) + l = 0; + else + l = 64 - clz64(d - 1); + divdq(q, r, ((limb_t)1 << l) - d, 0, d); + (void)r; + m1 = q + 1; + // printf("d=%lu l=%d m1=0x%016lx\n", d, l, m1); + s->m1 = m1; + s->shift1 = l; + if (s->shift1 > 1) + s->shift1 = 1; + s->shift2 = l - 1; + if (s->shift2 < 0) + s->shift2 = 0; +} + +static inline limb_t fast_udiv(limb_t a, const FastDivData *s) +{ + limb_t t0, t1; + muldq(t1, t0, s->m1, a); + t0 = (a - t1) >> s->shift1; + return (t1 + t0) >> s->shift2; +} + +#endif // USE_BF_DEC +/* contains 10^i */ +const limb_t mp_pow_dec[LIMB_DIGITS + 1] = { + 1U, + 10U, + 100U, + 1000U, + 10000U, + 100000U, + 1000000U, + 10000000U, + 100000000U, + 1000000000U, +#if LIMB_BITS == 64 + 10000000000U, + 100000000000U, + 1000000000000U, + 10000000000000U, + 100000000000000U, + 1000000000000000U, + 10000000000000000U, + 100000000000000000U, + 1000000000000000000U, + 10000000000000000000U, +#endif +}; +#ifdef USE_BF_DEC + +/* precomputed from fast_udiv_init(10^i) */ +static const FastDivData mp_pow_div[LIMB_DIGITS + 1] = { +#if LIMB_BITS == 32 + { 0x00000001, 0, 0 }, + { 0x9999999a, 1, 3 }, + { 0x47ae147b, 1, 6 }, + { 0x0624dd30, 1, 9 }, + { 0xa36e2eb2, 1, 13 }, + { 0x4f8b588f, 1, 16 }, + { 0x0c6f7a0c, 1, 19 }, + { 0xad7f29ac, 1, 23 }, + { 0x5798ee24, 1, 26 }, + { 0x12e0be83, 1, 29 }, +#else + { 0x0000000000000001, 0, 0 }, + { 0x999999999999999a, 1, 3 }, + { 0x47ae147ae147ae15, 1, 6 }, + { 0x0624dd2f1a9fbe77, 1, 9 }, + { 0xa36e2eb1c432ca58, 1, 13 }, + { 0x4f8b588e368f0847, 1, 16 }, + { 0x0c6f7a0b5ed8d36c, 1, 19 }, + { 0xad7f29abcaf48579, 1, 23 }, + { 0x5798ee2308c39dfa, 1, 26 }, + { 0x12e0be826d694b2f, 1, 29 }, + { 0xb7cdfd9d7bdbab7e, 1, 33 }, + { 0x5fd7fe17964955fe, 1, 36 }, + { 0x19799812dea11198, 1, 39 }, + { 0xc25c268497681c27, 1, 43 }, + { 0x6849b86a12b9b01f, 1, 46 }, + { 0x203af9ee756159b3, 1, 49 }, + { 0xcd2b297d889bc2b7, 1, 53 }, + { 0x70ef54646d496893, 1, 56 }, + { 0x2725dd1d243aba0f, 1, 59 }, + { 0xd83c94fb6d2ac34d, 1, 63 }, +#endif +}; + +/* divide by 10^shift with 0 <= shift <= LIMB_DIGITS */ +static inline limb_t fast_shr_dec(limb_t a, int shift) +{ + return fast_udiv(a, &mp_pow_div[shift]); +} + +/* division and remainder by 10^shift */ +#define fast_shr_rem_dec(q, r, a, shift) q = fast_shr_dec(a, shift), r = a - q * mp_pow_dec[shift] + +limb_t mp_add_dec(limb_t *res, const limb_t *op1, const limb_t *op2, + mp_size_t n, limb_t carry) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t k, a, v; + + k=carry; + for(i=0;i<n;i++) { + /* XXX: reuse the trick in add_mod */ + v = op1[i]; + a = v + op2[i] + k - base; + k = a <= v; + if (!k) + a += base; + res[i]=a; + } + return k; +} + +limb_t mp_add_ui_dec(limb_t *tab, limb_t b, mp_size_t n) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t k, a, v; + + k=b; + for(i=0;i<n;i++) { + v = tab[i]; + a = v + k - base; + k = a <= v; + if (!k) + a += base; + tab[i] = a; + if (k == 0) + break; + } + return k; +} + +limb_t mp_sub_dec(limb_t *res, const limb_t *op1, const limb_t *op2, + mp_size_t n, limb_t carry) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t k, v, a; + + k=carry; + for(i=0;i<n;i++) { + v = op1[i]; + a = v - op2[i] - k; + k = a > v; + if (k) + a += base; + res[i] = a; + } + return k; +} + +limb_t mp_sub_ui_dec(limb_t *tab, limb_t b, mp_size_t n) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t k, v, a; + + k=b; + for(i=0;i<n;i++) { + v = tab[i]; + a = v - k; + k = a > v; + if (k) + a += base; + tab[i]=a; + if (k == 0) + break; + } + return k; +} + +/* taba[] = taba[] * b + l. 0 <= b, l <= base - 1. Return the high carry */ +limb_t mp_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, + limb_t b, limb_t l) +{ + mp_size_t i; + limb_t t0, t1, r; + + for(i = 0; i < n; i++) { + muldq(t1, t0, taba[i], b); + adddq(t1, t0, 0, l); + divdq_base(l, r, t1, t0); + tabr[i] = r; + } + return l; +} + +/* tabr[] += taba[] * b. 0 <= b <= base - 1. Return the value to add + to the high word */ +limb_t mp_add_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, + limb_t b) +{ + mp_size_t i; + limb_t l, t0, t1, r; + + l = 0; + for(i = 0; i < n; i++) { + muldq(t1, t0, taba[i], b); + adddq(t1, t0, 0, l); + adddq(t1, t0, 0, tabr[i]); + divdq_base(l, r, t1, t0); + tabr[i] = r; + } + return l; +} + +/* tabr[] -= taba[] * b. 0 <= b <= base - 1. Return the value to + substract to the high word. */ +limb_t mp_sub_mul1_dec(limb_t *tabr, const limb_t *taba, mp_size_t n, + limb_t b) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t l, t0, t1, r, a, v, c; + + /* XXX: optimize */ + l = 0; + for(i = 0; i < n; i++) { + muldq(t1, t0, taba[i], b); + adddq(t1, t0, 0, l); + divdq_base(l, r, t1, t0); + v = tabr[i]; + a = v - r; + c = a > v; + if (c) + a += base; + /* never bigger than base because r = 0 when l = base - 1 */ + l += c; + tabr[i] = a; + } + return l; +} + +/* size of the result : op1_size + op2_size. */ +void mp_mul_basecase_dec(limb_t *result, + const limb_t *op1, mp_size_t op1_size, + const limb_t *op2, mp_size_t op2_size) +{ + mp_size_t i; + limb_t r; + + result[op1_size] = mp_mul1_dec(result, op1, op1_size, op2[0], 0); + + for(i=1;i<op2_size;i++) { + r = mp_add_mul1_dec(result + i, op1, op1_size, op2[i]); + result[i + op1_size] = r; + } +} + +/* taba[] = (taba[] + r*base^na) / b. 0 <= b < base. 0 <= r < + b. Return the remainder. */ +limb_t mp_div1_dec(limb_t *tabr, const limb_t *taba, mp_size_t na, + limb_t b, limb_t r) +{ + limb_t base = BF_DEC_BASE; + mp_size_t i; + limb_t t0, t1, q; + int shift; + +#if (BF_DEC_BASE % 2) == 0 + if (b == 2) { + limb_t base_div2; + /* Note: only works if base is even */ + base_div2 = base >> 1; + if (r) + r = base_div2; + for(i = na - 1; i >= 0; i--) { + t0 = taba[i]; + tabr[i] = (t0 >> 1) + r; + r = 0; + if (t0 & 1) + r = base_div2; + } + if (r) + r = 1; + } else +#endif + if (na >= UDIV1NORM_THRESHOLD) { + shift = clz(b); + if (shift == 0) { + /* normalized case: b >= 2^(LIMB_BITS-1) */ + limb_t b_inv; + b_inv = udiv1norm_init(b); + for(i = na - 1; i >= 0; i--) { + muldq(t1, t0, r, base); + adddq(t1, t0, 0, taba[i]); + q = udiv1norm(&r, t1, t0, b, b_inv); + tabr[i] = q; + } + } else { + limb_t b_inv; + b <<= shift; + b_inv = udiv1norm_init(b); + for(i = na - 1; i >= 0; i--) { + muldq(t1, t0, r, base); + adddq(t1, t0, 0, taba[i]); + t1 = (t1 << shift) | (t0 >> (LIMB_BITS - shift)); + t0 <<= shift; + q = udiv1norm(&r, t1, t0, b, b_inv); + r >>= shift; + tabr[i] = q; + } + } + } else { + for(i = na - 1; i >= 0; i--) { + muldq(t1, t0, r, base); + adddq(t1, t0, 0, taba[i]); + divdq(q, r, t1, t0, b); + tabr[i] = q; + } + } + return r; +} + +static __maybe_unused void mp_print_str_dec(const char *str, + const limb_t *tab, slimb_t n) +{ + slimb_t i; + printf("%s=", str); + for(i = n - 1; i >= 0; i--) { + if (i != n - 1) + printf("_"); + printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]); + } + printf("\n"); +} + +static __maybe_unused void mp_print_str_h_dec(const char *str, + const limb_t *tab, slimb_t n, + limb_t high) +{ + slimb_t i; + printf("%s=", str); + printf("%0*" PRIu_LIMB, LIMB_DIGITS, high); + for(i = n - 1; i >= 0; i--) { + printf("_"); + printf("%0*" PRIu_LIMB, LIMB_DIGITS, tab[i]); + } + printf("\n"); +} + +//#define DEBUG_DIV_SLOW + +#define DIV_STATIC_ALLOC_LEN 16 + +/* return q = a / b and r = a % b. + + taba[na] must be allocated if tabb1[nb - 1] < B / 2. tabb1[nb - 1] + must be != zero. na must be >= nb. 's' can be NULL if tabb1[nb - 1] + >= B / 2. + + The remainder is is returned in taba and contains nb libms. tabq + contains na - nb + 1 limbs. No overlap is permitted. + + Running time of the standard method: (na - nb + 1) * nb + Return 0 if OK, -1 if memory alloc error +*/ +/* XXX: optimize */ +static int mp_div_dec(bf_context_t *s, limb_t *tabq, + limb_t *taba, mp_size_t na, + const limb_t *tabb1, mp_size_t nb) +{ + limb_t base = BF_DEC_BASE; + limb_t r, mult, t0, t1, a, c, q, v, *tabb; + mp_size_t i, j; + limb_t static_tabb[DIV_STATIC_ALLOC_LEN]; + +#ifdef DEBUG_DIV_SLOW + mp_print_str_dec("a", taba, na); + mp_print_str_dec("b", tabb1, nb); +#endif + + /* normalize tabb */ + r = tabb1[nb - 1]; + assert(r != 0); + i = na - nb; + if (r >= BF_DEC_BASE / 2) { + mult = 1; + tabb = (limb_t *)tabb1; + q = 1; + for(j = nb - 1; j >= 0; j--) { + if (taba[i + j] != tabb[j]) { + if (taba[i + j] < tabb[j]) + q = 0; + break; + } + } + tabq[i] = q; + if (q) { + mp_sub_dec(taba + i, taba + i, tabb, nb, 0); + } + i--; + } else { + mult = base / (r + 1); + if (likely(nb <= DIV_STATIC_ALLOC_LEN)) { + tabb = static_tabb; + } else { + tabb = bf_malloc(s, sizeof(limb_t) * nb); + if (!tabb) + return -1; + } + mp_mul1_dec(tabb, tabb1, nb, mult, 0); + taba[na] = mp_mul1_dec(taba, taba, na, mult, 0); + } + +#ifdef DEBUG_DIV_SLOW + printf("mult=" FMT_LIMB "\n", mult); + mp_print_str_dec("a_norm", taba, na + 1); + mp_print_str_dec("b_norm", tabb, nb); +#endif + + for(; i >= 0; i--) { + if (unlikely(taba[i + nb] >= tabb[nb - 1])) { + /* XXX: check if it is really possible */ + q = base - 1; + } else { + muldq(t1, t0, taba[i + nb], base); + adddq(t1, t0, 0, taba[i + nb - 1]); + divdq(q, r, t1, t0, tabb[nb - 1]); + } + // printf("i=%d q1=%ld\n", i, q); + + r = mp_sub_mul1_dec(taba + i, tabb, nb, q); + // mp_dump("r1", taba + i, nb, bd); + // printf("r2=%ld\n", r); + + v = taba[i + nb]; + a = v - r; + c = a > v; + if (c) + a += base; + taba[i + nb] = a; + + if (c != 0) { + /* negative result */ + for(;;) { + q--; + c = mp_add_dec(taba + i, taba + i, tabb, nb, 0); + /* propagate carry and test if positive result */ + if (c != 0) { + if (++taba[i + nb] == base) { + break; + } + } + } + } + tabq[i] = q; + } + +#ifdef DEBUG_DIV_SLOW + mp_print_str_dec("q", tabq, na - nb + 1); + mp_print_str_dec("r", taba, nb); +#endif + + /* remove the normalization */ + if (mult != 1) { + mp_div1_dec(taba, taba, nb, mult, 0); + if (unlikely(tabb != static_tabb)) + bf_free(s, tabb); + } + return 0; +} + +/* divide by 10^shift */ +static limb_t mp_shr_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, + limb_t shift, limb_t high) +{ + mp_size_t i; + limb_t l, a, q, r; + + assert(shift >= 1 && shift < LIMB_DIGITS); + l = high; + for(i = n - 1; i >= 0; i--) { + a = tab[i]; + fast_shr_rem_dec(q, r, a, shift); + tab_r[i] = q + l * mp_pow_dec[LIMB_DIGITS - shift]; + l = r; + } + return l; +} + +/* multiply by 10^shift */ +static limb_t mp_shl_dec(limb_t *tab_r, const limb_t *tab, mp_size_t n, + limb_t shift, limb_t low) +{ + mp_size_t i; + limb_t l, a, q, r; + + assert(shift >= 1 && shift < LIMB_DIGITS); + l = low; + for(i = 0; i < n; i++) { + a = tab[i]; + fast_shr_rem_dec(q, r, a, LIMB_DIGITS - shift); + tab_r[i] = r * mp_pow_dec[shift] + l; + l = q; + } + return l; +} + +static limb_t mp_sqrtrem2_dec(limb_t *tabs, limb_t *taba) +{ + int k; + dlimb_t a, b, r; + limb_t taba1[2], s, r0, r1; + + /* convert to binary and normalize */ + a = (dlimb_t)taba[1] * BF_DEC_BASE + taba[0]; + k = clz(a >> LIMB_BITS) & ~1; + b = a << k; + taba1[0] = b; + taba1[1] = b >> LIMB_BITS; + mp_sqrtrem2(&s, taba1); + s >>= (k >> 1); + /* convert the remainder back to decimal */ + r = a - (dlimb_t)s * (dlimb_t)s; + divdq_base(r1, r0, r >> LIMB_BITS, r); + taba[0] = r0; + tabs[0] = s; + return r1; +} + +//#define DEBUG_SQRTREM_DEC + +/* tmp_buf must contain (n / 2 + 1 limbs) */ +static limb_t mp_sqrtrem_rec_dec(limb_t *tabs, limb_t *taba, limb_t n, + limb_t *tmp_buf) +{ + limb_t l, h, rh, ql, qh, c, i; + + if (n == 1) + return mp_sqrtrem2_dec(tabs, taba); +#ifdef DEBUG_SQRTREM_DEC + mp_print_str_dec("a", taba, 2 * n); +#endif + l = n / 2; + h = n - l; + qh = mp_sqrtrem_rec_dec(tabs + l, taba + 2 * l, h, tmp_buf); +#ifdef DEBUG_SQRTREM_DEC + mp_print_str_dec("s1", tabs + l, h); + mp_print_str_h_dec("r1", taba + 2 * l, h, qh); + mp_print_str_h_dec("r2", taba + l, n, qh); +#endif + + /* the remainder is in taba + 2 * l. Its high bit is in qh */ + if (qh) { + mp_sub_dec(taba + 2 * l, taba + 2 * l, tabs + l, h, 0); + } + /* instead of dividing by 2*s, divide by s (which is normalized) + and update q and r */ + mp_div_dec(NULL, tmp_buf, taba + l, n, tabs + l, h); + qh += tmp_buf[l]; + for(i = 0; i < l; i++) + tabs[i] = tmp_buf[i]; + ql = mp_div1_dec(tabs, tabs, l, 2, qh & 1); + qh = qh >> 1; /* 0 or 1 */ + if (ql) + rh = mp_add_dec(taba + l, taba + l, tabs + l, h, 0); + else + rh = 0; +#ifdef DEBUG_SQRTREM_DEC + mp_print_str_h_dec("q", tabs, l, qh); + mp_print_str_h_dec("u", taba + l, h, rh); +#endif + + mp_add_ui_dec(tabs + l, qh, h); +#ifdef DEBUG_SQRTREM_DEC + mp_print_str_dec("s2", tabs, n); +#endif + + /* q = qh, tabs[l - 1 ... 0], r = taba[n - 1 ... l] */ + /* subtract q^2. if qh = 1 then q = B^l, so we can take shortcuts */ + if (qh) { + c = qh; + } else { + mp_mul_basecase_dec(taba + n, tabs, l, tabs, l); + c = mp_sub_dec(taba, taba, taba + n, 2 * l, 0); + } + rh -= mp_sub_ui_dec(taba + 2 * l, c, n - 2 * l); + if ((slimb_t)rh < 0) { + mp_sub_ui_dec(tabs, 1, n); + rh += mp_add_mul1_dec(taba, tabs, n, 2); + rh += mp_add_ui_dec(taba, 1, n); + } + return rh; +} + +/* 'taba' has 2*n limbs with n >= 1 and taba[2*n-1] >= B/4. Return (s, + r) with s=floor(sqrt(a)) and r=a-s^2. 0 <= r <= 2 * s. tabs has n + limbs. r is returned in the lower n limbs of taba. Its r[n] is the + returned value of the function. */ +int mp_sqrtrem_dec(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n) +{ + limb_t tmp_buf1[8]; + limb_t *tmp_buf; + mp_size_t n2; + n2 = n / 2 + 1; + if (n2 <= countof(tmp_buf1)) { + tmp_buf = tmp_buf1; + } else { + tmp_buf = bf_malloc(s, sizeof(limb_t) * n2); + if (!tmp_buf) + return -1; + } + taba[n] = mp_sqrtrem_rec_dec(tabs, taba, n, tmp_buf); + if (tmp_buf != tmp_buf1) + bf_free(s, tmp_buf); + return 0; +} + +/* return the number of leading zero digits, from 0 to LIMB_DIGITS */ +static int clz_dec(limb_t a) +{ + if (a == 0) + return LIMB_DIGITS; + switch(LIMB_BITS - 1 - clz(a)) { + case 0: /* 1-1 */ + return LIMB_DIGITS - 1; + case 1: /* 2-3 */ + return LIMB_DIGITS - 1; + case 2: /* 4-7 */ + return LIMB_DIGITS - 1; + case 3: /* 8-15 */ + if (a < 10) + return LIMB_DIGITS - 1; + else + return LIMB_DIGITS - 2; + case 4: /* 16-31 */ + return LIMB_DIGITS - 2; + case 5: /* 32-63 */ + return LIMB_DIGITS - 2; + case 6: /* 64-127 */ + if (a < 100) + return LIMB_DIGITS - 2; + else + return LIMB_DIGITS - 3; + case 7: /* 128-255 */ + return LIMB_DIGITS - 3; + case 8: /* 256-511 */ + return LIMB_DIGITS - 3; + case 9: /* 512-1023 */ + if (a < 1000) + return LIMB_DIGITS - 3; + else + return LIMB_DIGITS - 4; + case 10: /* 1024-2047 */ + return LIMB_DIGITS - 4; + case 11: /* 2048-4095 */ + return LIMB_DIGITS - 4; + case 12: /* 4096-8191 */ + return LIMB_DIGITS - 4; + case 13: /* 8192-16383 */ + if (a < 10000) + return LIMB_DIGITS - 4; + else + return LIMB_DIGITS - 5; + case 14: /* 16384-32767 */ + return LIMB_DIGITS - 5; + case 15: /* 32768-65535 */ + return LIMB_DIGITS - 5; + case 16: /* 65536-131071 */ + if (a < 100000) + return LIMB_DIGITS - 5; + else + return LIMB_DIGITS - 6; + case 17: /* 131072-262143 */ + return LIMB_DIGITS - 6; + case 18: /* 262144-524287 */ + return LIMB_DIGITS - 6; + case 19: /* 524288-1048575 */ + if (a < 1000000) + return LIMB_DIGITS - 6; + else + return LIMB_DIGITS - 7; + case 20: /* 1048576-2097151 */ + return LIMB_DIGITS - 7; + case 21: /* 2097152-4194303 */ + return LIMB_DIGITS - 7; + case 22: /* 4194304-8388607 */ + return LIMB_DIGITS - 7; + case 23: /* 8388608-16777215 */ + if (a < 10000000) + return LIMB_DIGITS - 7; + else + return LIMB_DIGITS - 8; + case 24: /* 16777216-33554431 */ + return LIMB_DIGITS - 8; + case 25: /* 33554432-67108863 */ + return LIMB_DIGITS - 8; + case 26: /* 67108864-134217727 */ + if (a < 100000000) + return LIMB_DIGITS - 8; + else + return LIMB_DIGITS - 9; +#if LIMB_BITS == 64 + case 27: /* 134217728-268435455 */ + return LIMB_DIGITS - 9; + case 28: /* 268435456-536870911 */ + return LIMB_DIGITS - 9; + case 29: /* 536870912-1073741823 */ + if (a < 1000000000) + return LIMB_DIGITS - 9; + else + return LIMB_DIGITS - 10; + case 30: /* 1073741824-2147483647 */ + return LIMB_DIGITS - 10; + case 31: /* 2147483648-4294967295 */ + return LIMB_DIGITS - 10; + case 32: /* 4294967296-8589934591 */ + return LIMB_DIGITS - 10; + case 33: /* 8589934592-17179869183 */ + if (a < 10000000000) + return LIMB_DIGITS - 10; + else + return LIMB_DIGITS - 11; + case 34: /* 17179869184-34359738367 */ + return LIMB_DIGITS - 11; + case 35: /* 34359738368-68719476735 */ + return LIMB_DIGITS - 11; + case 36: /* 68719476736-137438953471 */ + if (a < 100000000000) + return LIMB_DIGITS - 11; + else + return LIMB_DIGITS - 12; + case 37: /* 137438953472-274877906943 */ + return LIMB_DIGITS - 12; + case 38: /* 274877906944-549755813887 */ + return LIMB_DIGITS - 12; + case 39: /* 549755813888-1099511627775 */ + if (a < 1000000000000) + return LIMB_DIGITS - 12; + else + return LIMB_DIGITS - 13; + case 40: /* 1099511627776-2199023255551 */ + return LIMB_DIGITS - 13; + case 41: /* 2199023255552-4398046511103 */ + return LIMB_DIGITS - 13; + case 42: /* 4398046511104-8796093022207 */ + return LIMB_DIGITS - 13; + case 43: /* 8796093022208-17592186044415 */ + if (a < 10000000000000) + return LIMB_DIGITS - 13; + else + return LIMB_DIGITS - 14; + case 44: /* 17592186044416-35184372088831 */ + return LIMB_DIGITS - 14; + case 45: /* 35184372088832-70368744177663 */ + return LIMB_DIGITS - 14; + case 46: /* 70368744177664-140737488355327 */ + if (a < 100000000000000) + return LIMB_DIGITS - 14; + else + return LIMB_DIGITS - 15; + case 47: /* 140737488355328-281474976710655 */ + return LIMB_DIGITS - 15; + case 48: /* 281474976710656-562949953421311 */ + return LIMB_DIGITS - 15; + case 49: /* 562949953421312-1125899906842623 */ + if (a < 1000000000000000) + return LIMB_DIGITS - 15; + else + return LIMB_DIGITS - 16; + case 50: /* 1125899906842624-2251799813685247 */ + return LIMB_DIGITS - 16; + case 51: /* 2251799813685248-4503599627370495 */ + return LIMB_DIGITS - 16; + case 52: /* 4503599627370496-9007199254740991 */ + return LIMB_DIGITS - 16; + case 53: /* 9007199254740992-18014398509481983 */ + if (a < 10000000000000000) + return LIMB_DIGITS - 16; + else + return LIMB_DIGITS - 17; + case 54: /* 18014398509481984-36028797018963967 */ + return LIMB_DIGITS - 17; + case 55: /* 36028797018963968-72057594037927935 */ + return LIMB_DIGITS - 17; + case 56: /* 72057594037927936-144115188075855871 */ + if (a < 100000000000000000) + return LIMB_DIGITS - 17; + else + return LIMB_DIGITS - 18; + case 57: /* 144115188075855872-288230376151711743 */ + return LIMB_DIGITS - 18; + case 58: /* 288230376151711744-576460752303423487 */ + return LIMB_DIGITS - 18; + case 59: /* 576460752303423488-1152921504606846975 */ + if (a < 1000000000000000000) + return LIMB_DIGITS - 18; + else + return LIMB_DIGITS - 19; +#endif + default: + return 0; + } +} + +/* for debugging */ +void bfdec_print_str(const char *str, const bfdec_t *a) +{ + slimb_t i; + printf("%s=", str); + + if (a->expn == BF_EXP_NAN) { + printf("NaN"); + } else { + if (a->sign) + putchar('-'); + if (a->expn == BF_EXP_ZERO) { + putchar('0'); + } else if (a->expn == BF_EXP_INF) { + printf("Inf"); + } else { + printf("0."); + for(i = a->len - 1; i >= 0; i--) + printf("%0*" PRIu_LIMB, LIMB_DIGITS, a->tab[i]); + printf("e%" PRId_LIMB, a->expn); + } + } + printf("\n"); +} + +/* return != 0 if one digit between 0 and bit_pos inclusive is not zero. */ +static inline limb_t scan_digit_nz(const bfdec_t *r, slimb_t bit_pos) +{ + slimb_t pos; + limb_t v, q; + int shift; + + if (bit_pos < 0) + return 0; + pos = (limb_t)bit_pos / LIMB_DIGITS; + shift = (limb_t)bit_pos % LIMB_DIGITS; + fast_shr_rem_dec(q, v, r->tab[pos], shift + 1); + (void)q; + if (v != 0) + return 1; + pos--; + while (pos >= 0) { + if (r->tab[pos] != 0) + return 1; + pos--; + } + return 0; +} + +static limb_t get_digit(const limb_t *tab, limb_t len, slimb_t pos) +{ + slimb_t i; + int shift; + i = floor_div(pos, LIMB_DIGITS); + if (i < 0 || i >= len) + return 0; + shift = pos - i * LIMB_DIGITS; + return fast_shr_dec(tab[i], shift) % 10; +} + +#if 0 +static limb_t get_digits(const limb_t *tab, limb_t len, slimb_t pos) +{ + limb_t a0, a1; + int shift; + slimb_t i; + + i = floor_div(pos, LIMB_DIGITS); + shift = pos - i * LIMB_DIGITS; + if (i >= 0 && i < len) + a0 = tab[i]; + else + a0 = 0; + if (shift == 0) { + return a0; + } else { + i++; + if (i >= 0 && i < len) + a1 = tab[i]; + else + a1 = 0; + return fast_shr_dec(a0, shift) + + fast_urem(a1, &mp_pow_div[LIMB_DIGITS - shift]) * + mp_pow_dec[shift]; + } +} +#endif + +/* return the addend for rounding. Note that prec can be <= 0 for bf_rint() */ +static int bfdec_get_rnd_add(int *pret, const bfdec_t *r, limb_t l, + slimb_t prec, int rnd_mode) +{ + int add_one, inexact; + limb_t digit1, digit0; + + // bfdec_print_str("get_rnd_add", r); + if (rnd_mode == BF_RNDF) { + digit0 = 1; /* faithful rounding does not honor the INEXACT flag */ + } else { + /* starting limb for bit 'prec + 1' */ + digit0 = scan_digit_nz(r, l * LIMB_DIGITS - 1 - bf_max(0, prec + 1)); + } + + /* get the digit at 'prec' */ + digit1 = get_digit(r->tab, l, l * LIMB_DIGITS - 1 - prec); + inexact = (digit1 | digit0) != 0; + + add_one = 0; + switch(rnd_mode) { + case BF_RNDZ: + break; + case BF_RNDN: + if (digit1 == 5) { + if (digit0) { + add_one = 1; + } else { + /* round to even */ + add_one = + get_digit(r->tab, l, l * LIMB_DIGITS - 1 - (prec - 1)) & 1; + } + } else if (digit1 > 5) { + add_one = 1; + } + break; + case BF_RNDD: + case BF_RNDU: + if (r->sign == (rnd_mode == BF_RNDD)) + add_one = inexact; + break; + case BF_RNDNA: + case BF_RNDF: + add_one = (digit1 >= 5); + break; + case BF_RNDA: + add_one = inexact; + break; + default: + abort(); + } + + if (inexact) + *pret |= BF_ST_INEXACT; + return add_one; +} + +/* round to prec1 bits assuming 'r' is non zero and finite. 'r' is + assumed to have length 'l' (1 <= l <= r->len). prec1 can be + BF_PREC_INF. BF_FLAG_SUBNORMAL is not supported. Cannot fail with + BF_ST_MEM_ERROR. + */ +static int __bfdec_round(bfdec_t *r, limb_t prec1, bf_flags_t flags, limb_t l) +{ + int shift, add_one, rnd_mode, ret; + slimb_t i, bit_pos, pos, e_min, e_max, e_range, prec; + + /* XXX: align to IEEE 754 2008 for decimal numbers ? */ + e_range = (limb_t)1 << (bf_get_exp_bits(flags) - 1); + e_min = -e_range + 3; + e_max = e_range; + + if (flags & BF_FLAG_RADPNT_PREC) { + /* 'prec' is the precision after the decimal point */ + if (prec1 != BF_PREC_INF) + prec = r->expn + prec1; + else + prec = prec1; + } else if (unlikely(r->expn < e_min) && (flags & BF_FLAG_SUBNORMAL)) { + /* restrict the precision in case of potentially subnormal + result */ + assert(prec1 != BF_PREC_INF); + prec = prec1 - (e_min - r->expn); + } else { + prec = prec1; + } + + /* round to prec bits */ + rnd_mode = flags & BF_RND_MASK; + ret = 0; + add_one = bfdec_get_rnd_add(&ret, r, l, prec, rnd_mode); + + if (prec <= 0) { + if (add_one) { + bfdec_resize(r, 1); /* cannot fail because r is non zero */ + r->tab[0] = BF_DEC_BASE / 10; + r->expn += 1 - prec; + ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; + return ret; + } else { + goto underflow; + } + } else if (add_one) { + limb_t carry; + + /* add one starting at digit 'prec - 1' */ + bit_pos = l * LIMB_DIGITS - 1 - (prec - 1); + pos = bit_pos / LIMB_DIGITS; + carry = mp_pow_dec[bit_pos % LIMB_DIGITS]; + carry = mp_add_ui_dec(r->tab + pos, carry, l - pos); + if (carry) { + /* shift right by one digit */ + mp_shr_dec(r->tab + pos, r->tab + pos, l - pos, 1, 1); + r->expn++; + } + } + + /* check underflow */ + if (unlikely(r->expn < e_min)) { + if (flags & BF_FLAG_SUBNORMAL) { + /* if inexact, also set the underflow flag */ + if (ret & BF_ST_INEXACT) + ret |= BF_ST_UNDERFLOW; + } else { + underflow: + bfdec_set_zero(r, r->sign); + ret |= BF_ST_UNDERFLOW | BF_ST_INEXACT; + return ret; + } + } + + /* check overflow */ + if (unlikely(r->expn > e_max)) { + bfdec_set_inf(r, r->sign); + ret |= BF_ST_OVERFLOW | BF_ST_INEXACT; + return ret; + } + + /* keep the bits starting at 'prec - 1' */ + bit_pos = l * LIMB_DIGITS - 1 - (prec - 1); + i = floor_div(bit_pos, LIMB_DIGITS); + if (i >= 0) { + shift = smod(bit_pos, LIMB_DIGITS); + if (shift != 0) { + r->tab[i] = fast_shr_dec(r->tab[i], shift) * + mp_pow_dec[shift]; + } + } else { + i = 0; + } + /* remove trailing zeros */ + while (r->tab[i] == 0) + i++; + if (i > 0) { + l -= i; + memmove(r->tab, r->tab + i, l * sizeof(limb_t)); + } + bfdec_resize(r, l); /* cannot fail */ + return ret; +} + +/* Cannot fail with BF_ST_MEM_ERROR. */ +int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags) +{ + if (r->len == 0) + return 0; + return __bfdec_round(r, prec, flags, r->len); +} + +/* 'r' must be a finite number. Cannot fail with BF_ST_MEM_ERROR. */ +int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags) +{ + limb_t l, v; + int shift, ret; + + // bfdec_print_str("bf_renorm", r); + l = r->len; + while (l > 0 && r->tab[l - 1] == 0) + l--; + if (l == 0) { + /* zero */ + r->expn = BF_EXP_ZERO; + bfdec_resize(r, 0); /* cannot fail */ + ret = 0; + } else { + r->expn -= (r->len - l) * LIMB_DIGITS; + /* shift to have the MSB set to '1' */ + v = r->tab[l - 1]; + shift = clz_dec(v); + if (shift != 0) { + mp_shl_dec(r->tab, r->tab, l, shift, 0); + r->expn -= shift; + } + ret = __bfdec_round(r, prec1, flags, l); + } + // bf_print_str("r_final", r); + return ret; +} + +int bfdec_set_ui(bfdec_t *r, uint64_t v) +{ +#if LIMB_BITS == 32 + if (v >= BF_DEC_BASE * BF_DEC_BASE) { + if (bfdec_resize(r, 3)) + goto fail; + r->tab[0] = v % BF_DEC_BASE; + v /= BF_DEC_BASE; + r->tab[1] = v % BF_DEC_BASE; + r->tab[2] = v / BF_DEC_BASE; + r->expn = 3 * LIMB_DIGITS; + } else +#endif + if (v >= BF_DEC_BASE) { + if (bfdec_resize(r, 2)) + goto fail; + r->tab[0] = v % BF_DEC_BASE; + r->tab[1] = v / BF_DEC_BASE; + r->expn = 2 * LIMB_DIGITS; + } else { + if (bfdec_resize(r, 1)) + goto fail; + r->tab[0] = v; + r->expn = LIMB_DIGITS; + } + r->sign = 0; + return bfdec_normalize_and_round(r, BF_PREC_INF, 0); + fail: + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +int bfdec_set_si(bfdec_t *r, int64_t v) +{ + int ret; + if (v < 0) { + ret = bfdec_set_ui(r, -v); + r->sign = 1; + } else { + ret = bfdec_set_ui(r, v); + } + return ret; +} + +static int bfdec_add_internal(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, bf_flags_t flags, int b_neg) +{ + bf_context_t *s = r->ctx; + int is_sub, cmp_res, a_sign, b_sign, ret; + + a_sign = a->sign; + b_sign = b->sign ^ b_neg; + is_sub = a_sign ^ b_sign; + cmp_res = bfdec_cmpu(a, b); + if (cmp_res < 0) { + const bfdec_t *tmp; + tmp = a; + a = b; + b = tmp; + a_sign = b_sign; /* b_sign is never used later */ + } + /* abs(a) >= abs(b) */ + if (cmp_res == 0 && is_sub && a->expn < BF_EXP_INF) { + /* zero result */ + bfdec_set_zero(r, (flags & BF_RND_MASK) == BF_RNDD); + ret = 0; + } else if (a->len == 0 || b->len == 0) { + ret = 0; + if (a->expn >= BF_EXP_INF) { + if (a->expn == BF_EXP_NAN) { + /* at least one operand is NaN */ + bfdec_set_nan(r); + ret = 0; + } else if (b->expn == BF_EXP_INF && is_sub) { + /* infinities with different signs */ + bfdec_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + bfdec_set_inf(r, a_sign); + } + } else { + /* at least one zero and not subtract */ + if (bfdec_set(r, a)) + return BF_ST_MEM_ERROR; + r->sign = a_sign; + goto renorm; + } + } else { + slimb_t d, a_offset, b_offset, i, r_len; + limb_t carry; + limb_t *b1_tab; + int b_shift; + mp_size_t b1_len; + + d = a->expn - b->expn; + + /* XXX: not efficient in time and memory if the precision is + not infinite */ + r_len = bf_max(a->len, b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS); + if (bfdec_resize(r, r_len)) + goto fail; + r->sign = a_sign; + r->expn = a->expn; + + a_offset = r_len - a->len; + for(i = 0; i < a_offset; i++) + r->tab[i] = 0; + for(i = 0; i < a->len; i++) + r->tab[a_offset + i] = a->tab[i]; + + b_shift = d % LIMB_DIGITS; + if (b_shift == 0) { + b1_len = b->len; + b1_tab = (limb_t *)b->tab; + } else { + b1_len = b->len + 1; + b1_tab = bf_malloc(s, sizeof(limb_t) * b1_len); + if (!b1_tab) + goto fail; + b1_tab[0] = mp_shr_dec(b1_tab + 1, b->tab, b->len, b_shift, 0) * + mp_pow_dec[LIMB_DIGITS - b_shift]; + } + b_offset = r_len - (b->len + (d + LIMB_DIGITS - 1) / LIMB_DIGITS); + + if (is_sub) { + carry = mp_sub_dec(r->tab + b_offset, r->tab + b_offset, + b1_tab, b1_len, 0); + if (carry != 0) { + carry = mp_sub_ui_dec(r->tab + b_offset + b1_len, carry, + r_len - (b_offset + b1_len)); + assert(carry == 0); + } + } else { + carry = mp_add_dec(r->tab + b_offset, r->tab + b_offset, + b1_tab, b1_len, 0); + if (carry != 0) { + carry = mp_add_ui_dec(r->tab + b_offset + b1_len, carry, + r_len - (b_offset + b1_len)); + } + if (carry != 0) { + if (bfdec_resize(r, r_len + 1)) { + if (b_shift != 0) + bf_free(s, b1_tab); + goto fail; + } + r->tab[r_len] = 1; + r->expn += LIMB_DIGITS; + } + } + if (b_shift != 0) + bf_free(s, b1_tab); + renorm: + ret = bfdec_normalize_and_round(r, prec, flags); + } + return ret; + fail: + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +static int __bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bfdec_add_internal(r, a, b, prec, flags, 0); +} + +static int __bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bfdec_add_internal(r, a, b, prec, flags, 1); +} + +int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, + (bf_op2_func_t *)__bfdec_add); +} + +int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, + (bf_op2_func_t *)__bfdec_sub); +} + +int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + int ret, r_sign; + + if (a->len < b->len) { + const bfdec_t *tmp = a; + a = b; + b = tmp; + } + r_sign = a->sign ^ b->sign; + /* here b->len <= a->len */ + if (b->len == 0) { + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bfdec_set_nan(r); + ret = 0; + } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_INF) { + if ((a->expn == BF_EXP_INF && b->expn == BF_EXP_ZERO) || + (a->expn == BF_EXP_ZERO && b->expn == BF_EXP_INF)) { + bfdec_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + bfdec_set_inf(r, r_sign); + ret = 0; + } + } else { + bfdec_set_zero(r, r_sign); + ret = 0; + } + } else { + bfdec_t tmp, *r1 = NULL; + limb_t a_len, b_len; + limb_t *a_tab, *b_tab; + + a_len = a->len; + b_len = b->len; + a_tab = a->tab; + b_tab = b->tab; + + if (r == a || r == b) { + bfdec_init(r->ctx, &tmp); + r1 = r; + r = &tmp; + } + if (bfdec_resize(r, a_len + b_len)) { + bfdec_set_nan(r); + ret = BF_ST_MEM_ERROR; + goto done; + } + mp_mul_basecase_dec(r->tab, a_tab, a_len, b_tab, b_len); + r->sign = r_sign; + r->expn = a->expn + b->expn; + ret = bfdec_normalize_and_round(r, prec, flags); + done: + if (r == &tmp) + bfdec_move(r1, &tmp); + } + return ret; +} + +int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags) +{ + bfdec_t b; + int ret; + bfdec_init(r->ctx, &b); + ret = bfdec_set_si(&b, b1); + ret |= bfdec_mul(r, a, &b, prec, flags); + bfdec_delete(&b); + return ret; +} + +int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags) +{ + bfdec_t b; + int ret; + + bfdec_init(r->ctx, &b); + ret = bfdec_set_si(&b, b1); + ret |= bfdec_add(r, a, &b, prec, flags); + bfdec_delete(&b); + return ret; +} + +static int __bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, + limb_t prec, bf_flags_t flags) +{ + int ret, r_sign; + limb_t n, nb, precl; + + r_sign = a->sign ^ b->sign; + if (a->expn >= BF_EXP_INF || b->expn >= BF_EXP_INF) { + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bfdec_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF && b->expn == BF_EXP_INF) { + bfdec_set_nan(r); + return BF_ST_INVALID_OP; + } else if (a->expn == BF_EXP_INF) { + bfdec_set_inf(r, r_sign); + return 0; + } else { + bfdec_set_zero(r, r_sign); + return 0; + } + } else if (a->expn == BF_EXP_ZERO) { + if (b->expn == BF_EXP_ZERO) { + bfdec_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bfdec_set_zero(r, r_sign); + return 0; + } + } else if (b->expn == BF_EXP_ZERO) { + bfdec_set_inf(r, r_sign); + return BF_ST_DIVIDE_ZERO; + } + + nb = b->len; + if (prec == BF_PREC_INF) { + /* infinite precision: return BF_ST_INVALID_OP if not an exact + result */ + /* XXX: check */ + precl = nb + 1; + } else if (flags & BF_FLAG_RADPNT_PREC) { + /* number of digits after the decimal point */ + /* XXX: check (2 extra digits for rounding + 2 digits) */ + precl = (bf_max(a->expn - b->expn, 0) + 2 + + prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; + } else { + /* number of limbs of the quotient (2 extra digits for rounding) */ + precl = (prec + 2 + LIMB_DIGITS - 1) / LIMB_DIGITS; + } + n = bf_max(a->len, precl); + + { + limb_t *taba, na, i; + slimb_t d; + + na = n + nb; + taba = bf_malloc(r->ctx, (na + 1) * sizeof(limb_t)); + if (!taba) + goto fail; + d = na - a->len; + memset(taba, 0, d * sizeof(limb_t)); + memcpy(taba + d, a->tab, a->len * sizeof(limb_t)); + if (bfdec_resize(r, n + 1)) + goto fail1; + if (mp_div_dec(r->ctx, r->tab, taba, na, b->tab, nb)) { + fail1: + bf_free(r->ctx, taba); + goto fail; + } + /* see if non zero remainder */ + for(i = 0; i < nb; i++) { + if (taba[i] != 0) + break; + } + bf_free(r->ctx, taba); + if (i != nb) { + if (prec == BF_PREC_INF) { + bfdec_set_nan(r); + return BF_ST_INVALID_OP; + } else { + r->tab[0] |= 1; + } + } + r->expn = a->expn - b->expn + LIMB_DIGITS; + r->sign = r_sign; + ret = bfdec_normalize_and_round(r, prec, flags); + } + return ret; + fail: + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags) +{ + return bf_op2((bf_t *)r, (bf_t *)a, (bf_t *)b, prec, flags, + (bf_op2_func_t *)__bfdec_div); +} + +/* a and b must be finite numbers with a >= 0 and b > 0. 'q' is the + integer defined as floor(a/b) and r = a - q * b. */ +static void bfdec_tdivremu(bf_context_t *s, bfdec_t *q, bfdec_t *r, + const bfdec_t *a, const bfdec_t *b) +{ + if (bfdec_cmpu(a, b) < 0) { + bfdec_set_ui(q, 0); + bfdec_set(r, a); + } else { + bfdec_div(q, a, b, 0, BF_RNDZ | BF_FLAG_RADPNT_PREC); + bfdec_mul(r, q, b, BF_PREC_INF, BF_RNDZ); + bfdec_sub(r, a, r, BF_PREC_INF, BF_RNDZ); + } +} + +/* division and remainder. + + rnd_mode is the rounding mode for the quotient. The additional + rounding mode BF_RND_EUCLIDIAN is supported. + + 'q' is an integer. 'r' is rounded with prec and flags (prec can be + BF_PREC_INF). +*/ +int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, + limb_t prec, bf_flags_t flags, int rnd_mode) +{ + bf_context_t *s = q->ctx; + bfdec_t a1_s, *a1 = &a1_s; + bfdec_t b1_s, *b1 = &b1_s; + bfdec_t r1_s, *r1 = &r1_s; + int q_sign, res; + BOOL is_ceil, is_rndn; + + assert(q != a && q != b); + assert(r != a && r != b); + assert(q != r); + + if (a->len == 0 || b->len == 0) { + bfdec_set_zero(q, 0); + if (a->expn == BF_EXP_NAN || b->expn == BF_EXP_NAN) { + bfdec_set_nan(r); + return 0; + } else if (a->expn == BF_EXP_INF || b->expn == BF_EXP_ZERO) { + bfdec_set_nan(r); + return BF_ST_INVALID_OP; + } else { + bfdec_set(r, a); + return bfdec_round(r, prec, flags); + } + } + + q_sign = a->sign ^ b->sign; + is_rndn = (rnd_mode == BF_RNDN || rnd_mode == BF_RNDNA); + switch(rnd_mode) { + default: + case BF_RNDZ: + case BF_RNDN: + case BF_RNDNA: + is_ceil = FALSE; + break; + case BF_RNDD: + is_ceil = q_sign; + break; + case BF_RNDU: + is_ceil = q_sign ^ 1; + break; + case BF_RNDA: + is_ceil = TRUE; + break; + case BF_DIVREM_EUCLIDIAN: + is_ceil = a->sign; + break; + } + + a1->expn = a->expn; + a1->tab = a->tab; + a1->len = a->len; + a1->sign = 0; + + b1->expn = b->expn; + b1->tab = b->tab; + b1->len = b->len; + b1->sign = 0; + + // bfdec_print_str("a1", a1); + // bfdec_print_str("b1", b1); + /* XXX: could improve to avoid having a large 'q' */ + bfdec_tdivremu(s, q, r, a1, b1); + if (bfdec_is_nan(q) || bfdec_is_nan(r)) + goto fail; + // bfdec_print_str("q", q); + // bfdec_print_str("r", r); + + if (r->len != 0) { + if (is_rndn) { + bfdec_init(s, r1); + if (bfdec_set(r1, r)) + goto fail; + if (bfdec_mul_si(r1, r1, 2, BF_PREC_INF, BF_RNDZ)) { + bfdec_delete(r1); + goto fail; + } + res = bfdec_cmpu(r1, b); + bfdec_delete(r1); + if (res > 0 || + (res == 0 && + (rnd_mode == BF_RNDNA || + (get_digit(q->tab, q->len, q->len * LIMB_DIGITS - q->expn) & 1) != 0))) { + goto do_sub_r; + } + } else if (is_ceil) { + do_sub_r: + res = bfdec_add_si(q, q, 1, BF_PREC_INF, BF_RNDZ); + res |= bfdec_sub(r, r, b1, BF_PREC_INF, BF_RNDZ); + if (res & BF_ST_MEM_ERROR) + goto fail; + } + } + + r->sign ^= a->sign; + q->sign = q_sign; + return bfdec_round(r, prec, flags); + fail: + bfdec_set_nan(q); + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode) +{ + bfdec_t q_s, *q = &q_s; + int ret; + + bfdec_init(r->ctx, q); + ret = bfdec_divrem(q, r, a, b, prec, flags, rnd_mode); + bfdec_delete(q); + return ret; +} + +/* convert to integer (infinite precision) */ +int bfdec_rint(bfdec_t *r, int rnd_mode) +{ + return bfdec_round(r, 0, rnd_mode | BF_FLAG_RADPNT_PREC); +} + +int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags) +{ + bf_context_t *s = a->ctx; + int ret, k; + limb_t *a1, v; + slimb_t n, n1, prec1; + limb_t res; + + assert(r != a); + + if (a->len == 0) { + if (a->expn == BF_EXP_NAN) { + bfdec_set_nan(r); + } else if (a->expn == BF_EXP_INF && a->sign) { + goto invalid_op; + } else { + bfdec_set(r, a); + } + ret = 0; + } else if (a->sign || prec == BF_PREC_INF) { + invalid_op: + bfdec_set_nan(r); + ret = BF_ST_INVALID_OP; + } else { + if (flags & BF_FLAG_RADPNT_PREC) { + prec1 = bf_max(floor_div(a->expn + 1, 2) + prec, 1); + } else { + prec1 = prec; + } + /* convert the mantissa to an integer with at least 2 * + prec + 4 digits */ + n = (2 * (prec1 + 2) + 2 * LIMB_DIGITS - 1) / (2 * LIMB_DIGITS); + if (bfdec_resize(r, n)) + goto fail; + a1 = bf_malloc(s, sizeof(limb_t) * 2 * n); + if (!a1) + goto fail; + n1 = bf_min(2 * n, a->len); + memset(a1, 0, (2 * n - n1) * sizeof(limb_t)); + memcpy(a1 + 2 * n - n1, a->tab + a->len - n1, n1 * sizeof(limb_t)); + if (a->expn & 1) { + res = mp_shr_dec(a1, a1, 2 * n, 1, 0); + } else { + res = 0; + } + /* normalize so that a1 >= B^(2*n)/4. Not need for n = 1 + because mp_sqrtrem2_dec already does it */ + k = 0; + if (n > 1) { + v = a1[2 * n - 1]; + while (v < BF_DEC_BASE / 4) { + k++; + v *= 4; + } + if (k != 0) + mp_mul1_dec(a1, a1, 2 * n, 1 << (2 * k), 0); + } + if (mp_sqrtrem_dec(s, r->tab, a1, n)) { + bf_free(s, a1); + goto fail; + } + if (k != 0) + mp_div1_dec(r->tab, r->tab, n, 1 << k, 0); + if (!res) { + res = mp_scan_nz(a1, n + 1); + } + bf_free(s, a1); + if (!res) { + res = mp_scan_nz(a->tab, a->len - n1); + } + if (res != 0) + r->tab[0] |= 1; + r->sign = 0; + r->expn = (a->expn + 1) >> 1; + ret = bfdec_round(r, prec, flags); + } + return ret; + fail: + bfdec_set_nan(r); + return BF_ST_MEM_ERROR; +} + +/* The rounding mode is always BF_RNDZ. Return BF_ST_OVERFLOW if there + is an overflow and 0 otherwise. No memory error is possible. */ +int bfdec_get_int32(int *pres, const bfdec_t *a) +{ + uint32_t v; + int ret; + if (a->expn >= BF_EXP_INF) { + ret = 0; + if (a->expn == BF_EXP_INF) { + v = (uint32_t)INT32_MAX + a->sign; + /* XXX: return overflow ? */ + } else { + v = INT32_MAX; + } + } else if (a->expn <= 0) { + v = 0; + ret = 0; + } else if (a->expn <= 9) { + v = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn); + if (a->sign) + v = -v; + ret = 0; + } else if (a->expn == 10) { + uint64_t v1; + uint32_t v_max; +#if LIMB_BITS == 64 + v1 = fast_shr_dec(a->tab[a->len - 1], LIMB_DIGITS - a->expn); +#else + v1 = (uint64_t)a->tab[a->len - 1] * 10 + + get_digit(a->tab, a->len, (a->len - 1) * LIMB_DIGITS - 1); +#endif + v_max = (uint32_t)INT32_MAX + a->sign; + if (v1 > v_max) { + v = v_max; + ret = BF_ST_OVERFLOW; + } else { + v = v1; + if (a->sign) + v = -v; + ret = 0; + } + } else { + v = (uint32_t)INT32_MAX + a->sign; + ret = BF_ST_OVERFLOW; + } + *pres = v; + return ret; +} + +/* power to an integer with infinite precision */ +int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b) +{ + int ret, n_bits, i; + + assert(r != a); + if (b == 0) + return bfdec_set_ui(r, 1); + ret = bfdec_set(r, a); + n_bits = LIMB_BITS - clz(b); + for(i = n_bits - 2; i >= 0; i--) { + ret |= bfdec_mul(r, r, r, BF_PREC_INF, BF_RNDZ); + if ((b >> i) & 1) + ret |= bfdec_mul(r, r, a, BF_PREC_INF, BF_RNDZ); + } + return ret; +} + +char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags) +{ + return bf_ftoa_internal(plen, (const bf_t *)a, 10, prec, flags, TRUE); +} + +int bfdec_atof(bfdec_t *r, const char *str, const char **pnext, + limb_t prec, bf_flags_t flags) +{ + slimb_t dummy_exp; + return bf_atof_internal((bf_t *)r, &dummy_exp, str, pnext, 10, prec, + flags, TRUE); +} + +#endif /* USE_BF_DEC */ + +#ifdef USE_FFT_MUL +/***************************************************************/ +/* Integer multiplication with FFT */ + +/* or LIMB_BITS at bit position 'pos' in tab */ +static inline void put_bits(limb_t *tab, limb_t len, slimb_t pos, limb_t val) +{ + limb_t i; + int p; + + i = pos >> LIMB_LOG2_BITS; + p = pos & (LIMB_BITS - 1); + if (i < len) + tab[i] |= val << p; + if (p != 0) { + i++; + if (i < len) { + tab[i] |= val >> (LIMB_BITS - p); + } + } +} + +#if defined(__AVX2__) + +typedef double NTTLimb; + +/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */ +#define NTT_MOD_LOG2_MIN 50 +#define NTT_MOD_LOG2_MAX 51 +#define NB_MODS 5 +#define NTT_PROOT_2EXP 39 +static const int ntt_int_bits[NB_MODS] = { 254, 203, 152, 101, 50, }; + +static const limb_t ntt_mods[NB_MODS] = { 0x00073a8000000001, 0x0007858000000001, 0x0007a38000000001, 0x0007a68000000001, 0x0007fd8000000001, +}; + +static const limb_t ntt_proot[2][NB_MODS] = { + { 0x00056198d44332c8, 0x0002eb5d640aad39, 0x00047e31eaa35fd0, 0x0005271ac118a150, 0x00075e0ce8442bd5, }, + { 0x000461169761bcc5, 0x0002dac3cb2da688, 0x0004abc97751e3bf, 0x000656778fc8c485, 0x0000dc6469c269fa, }, +}; + +static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { + 0x00020e4da740da8e, 0x0004c3dc09c09c1d, 0x000063bd097b4271, 0x000799d8f18f18fd, + 0x0005384222222264, 0x000572b07c1f07fe, 0x00035cd08888889a, + 0x00066015555557e3, 0x000725960b60b623, + 0x0002fc1fa1d6ce12, +}; + +#else + +typedef limb_t NTTLimb; + +#if LIMB_BITS == 64 + +#define NTT_MOD_LOG2_MIN 61 +#define NTT_MOD_LOG2_MAX 62 +#define NB_MODS 5 +#define NTT_PROOT_2EXP 51 +static const int ntt_int_bits[NB_MODS] = { 307, 246, 185, 123, 61, }; + +static const limb_t ntt_mods[NB_MODS] = { 0x28d8000000000001, 0x2a88000000000001, 0x2ed8000000000001, 0x3508000000000001, 0x3aa8000000000001, +}; + +static const limb_t ntt_proot[2][NB_MODS] = { + { 0x1b8ea61034a2bea7, 0x21a9762de58206fb, 0x02ca782f0756a8ea, 0x278384537a3e50a1, 0x106e13fee74ce0ab, }, + { 0x233513af133e13b8, 0x1d13140d1c6f75f1, 0x12cde57f97e3eeda, 0x0d6149e23cbe654f, 0x36cd204f522a1379, }, +}; + +static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { + 0x08a9ed097b425eea, 0x18a44aaaaaaaaab3, 0x2493f57f57f57f5d, 0x126b8d0649a7f8d4, + 0x09d80ed7303b5ccc, 0x25b8bcf3cf3cf3d5, 0x2ce6ce63398ce638, + 0x0e31fad40a57eb59, 0x02a3529fd4a7f52f, + 0x3a5493e93e93e94a, +}; + +#elif LIMB_BITS == 32 + +/* we must have: modulo >= 1 << NTT_MOD_LOG2_MIN */ +#define NTT_MOD_LOG2_MIN 29 +#define NTT_MOD_LOG2_MAX 30 +#define NB_MODS 5 +#define NTT_PROOT_2EXP 20 +static const int ntt_int_bits[NB_MODS] = { 148, 119, 89, 59, 29, }; + +static const limb_t ntt_mods[NB_MODS] = { 0x0000000032b00001, 0x0000000033700001, 0x0000000036d00001, 0x0000000037300001, 0x000000003e500001, +}; + +static const limb_t ntt_proot[2][NB_MODS] = { + { 0x0000000032525f31, 0x0000000005eb3b37, 0x00000000246eda9f, 0x0000000035f25901, 0x00000000022f5768, }, + { 0x00000000051eba1a, 0x00000000107be10e, 0x000000001cd574e0, 0x00000000053806e6, 0x000000002cd6bf98, }, +}; + +static const limb_t ntt_mods_cr[NB_MODS * (NB_MODS - 1) / 2] = { + 0x000000000449559a, 0x000000001eba6ca9, 0x000000002ec18e46, 0x000000000860160b, + 0x000000000d321307, 0x000000000bf51120, 0x000000000f662938, + 0x000000000932ab3e, 0x000000002f40eef8, + 0x000000002e760905, +}; + +#endif /* LIMB_BITS */ + +#endif /* !AVX2 */ + +#if defined(__AVX2__) +#define NTT_TRIG_K_MAX 18 +#else +#define NTT_TRIG_K_MAX 19 +#endif + +typedef struct BFNTTState { + bf_context_t *ctx; + + /* used for mul_mod_fast() */ + limb_t ntt_mods_div[NB_MODS]; + + limb_t ntt_proot_pow[NB_MODS][2][NTT_PROOT_2EXP + 1]; + limb_t ntt_proot_pow_inv[NB_MODS][2][NTT_PROOT_2EXP + 1]; + NTTLimb *ntt_trig[NB_MODS][2][NTT_TRIG_K_MAX + 1]; + /* 1/2^n mod m */ + limb_t ntt_len_inv[NB_MODS][NTT_PROOT_2EXP + 1][2]; +#if defined(__AVX2__) + __m256d ntt_mods_cr_vec[NB_MODS * (NB_MODS - 1) / 2]; + __m256d ntt_mods_vec[NB_MODS]; + __m256d ntt_mods_inv_vec[NB_MODS]; +#else + limb_t ntt_mods_cr_inv[NB_MODS * (NB_MODS - 1) / 2]; +#endif +} BFNTTState; + +static NTTLimb *get_trig(BFNTTState *s, int k, int inverse, int m_idx); + +/* add modulo with up to (LIMB_BITS-1) bit modulo */ +static inline limb_t add_mod(limb_t a, limb_t b, limb_t m) +{ + limb_t r; + r = a + b; + if (r >= m) + r -= m; + return r; +} + +/* sub modulo with up to LIMB_BITS bit modulo */ +static inline limb_t sub_mod(limb_t a, limb_t b, limb_t m) +{ + limb_t r; + r = a - b; + if (r > a) + r += m; + return r; +} + +/* return (r0+r1*B) mod m + precondition: 0 <= r0+r1*B < 2^(64+NTT_MOD_LOG2_MIN) +*/ +static inline limb_t mod_fast(dlimb_t r, + limb_t m, limb_t m_inv) +{ + limb_t a1, q, t0, r1, r0; + + a1 = r >> NTT_MOD_LOG2_MIN; + + q = ((dlimb_t)a1 * m_inv) >> LIMB_BITS; + r = r - (dlimb_t)q * m - m * 2; + r1 = r >> LIMB_BITS; + t0 = (slimb_t)r1 >> 1; + r += m & t0; + r0 = r; + r1 = r >> LIMB_BITS; + r0 += m & r1; + return r0; +} + +/* faster version using precomputed modulo inverse. + precondition: 0 <= a * b < 2^(64+NTT_MOD_LOG2_MIN) */ +static inline limb_t mul_mod_fast(limb_t a, limb_t b, + limb_t m, limb_t m_inv) +{ + dlimb_t r; + r = (dlimb_t)a * (dlimb_t)b; + return mod_fast(r, m, m_inv); +} + +static inline limb_t init_mul_mod_fast(limb_t m) +{ + dlimb_t t; + assert(m < (limb_t)1 << NTT_MOD_LOG2_MAX); + assert(m >= (limb_t)1 << NTT_MOD_LOG2_MIN); + t = (dlimb_t)1 << (LIMB_BITS + NTT_MOD_LOG2_MIN); + return t / m; +} + +/* Faster version used when the multiplier is constant. 0 <= a < 2^64, + 0 <= b < m. */ +static inline limb_t mul_mod_fast2(limb_t a, limb_t b, + limb_t m, limb_t b_inv) +{ + limb_t r, q; + + q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS; + r = a * b - q * m; + if (r >= m) + r -= m; + return r; +} + +/* Faster version used when the multiplier is constant. 0 <= a < 2^64, + 0 <= b < m. Let r = a * b mod m. The return value is 'r' or 'r + + m'. */ +static inline limb_t mul_mod_fast3(limb_t a, limb_t b, + limb_t m, limb_t b_inv) +{ + limb_t r, q; + + q = ((dlimb_t)a * (dlimb_t)b_inv) >> LIMB_BITS; + r = a * b - q * m; + return r; +} + +static inline limb_t init_mul_mod_fast2(limb_t b, limb_t m) +{ + return ((dlimb_t)b << LIMB_BITS) / m; +} + +#ifdef __AVX2__ + +static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m) +{ + slimb_t v; + v = a; + if (v < 0) + v += m; + if (v >= m) + v -= m; + return v; +} + +static inline NTTLimb int_to_ntt_limb(limb_t a, limb_t m) +{ + return (slimb_t)a; +} + +static inline NTTLimb int_to_ntt_limb2(limb_t a, limb_t m) +{ + if (a >= (m / 2)) + a -= m; + return (slimb_t)a; +} + +/* return r + m if r < 0 otherwise r. */ +static inline __m256d ntt_mod1(__m256d r, __m256d m) +{ + return _mm256_blendv_pd(r, r + m, r); +} + +/* input: abs(r) < 2 * m. Output: abs(r) < m */ +static inline __m256d ntt_mod(__m256d r, __m256d mf, __m256d m2f) +{ + return _mm256_blendv_pd(r, r + m2f, r) - mf; +} + +/* input: abs(a*b) < 2 * m^2, output: abs(r) < m */ +static inline __m256d ntt_mul_mod(__m256d a, __m256d b, __m256d mf, + __m256d m_inv) +{ + __m256d r, q, ab1, ab0, qm0, qm1; + ab1 = a * b; + q = _mm256_round_pd(ab1 * m_inv, 0); /* round to nearest */ + qm1 = q * mf; + qm0 = _mm256_fmsub_pd(q, mf, qm1); /* low part */ + ab0 = _mm256_fmsub_pd(a, b, ab1); /* low part */ + r = (ab1 - qm1) + (ab0 - qm0); + return r; +} + +static void *bf_aligned_malloc(bf_context_t *s, size_t size, size_t align) +{ + void *ptr; + void **ptr1; + ptr = bf_malloc(s, size + sizeof(void *) + align - 1); + if (!ptr) + return NULL; + ptr1 = (void **)(((uintptr_t)ptr + sizeof(void *) + align - 1) & + ~(align - 1)); + ptr1[-1] = ptr; + return ptr1; +} + +static void bf_aligned_free(bf_context_t *s, void *ptr) +{ + if (!ptr) + return; + bf_free(s, ((void **)ptr)[-1]); +} + +static void *ntt_malloc(BFNTTState *s, size_t size) +{ + return bf_aligned_malloc(s->ctx, size, 64); +} + +static void ntt_free(BFNTTState *s, void *ptr) +{ + bf_aligned_free(s->ctx, ptr); +} + +static no_inline int ntt_fft(BFNTTState *s, + NTTLimb *out_buf, NTTLimb *in_buf, + NTTLimb *tmp_buf, int fft_len_log2, + int inverse, int m_idx) +{ + limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j; + NTTLimb *tab_in, *tab_out, *tmp, *trig; + __m256d m_inv, mf, m2f, c, a0, a1, b0, b1; + limb_t m; + int l; + + m = ntt_mods[m_idx]; + + m_inv = _mm256_set1_pd(1.0 / (double)m); + mf = _mm256_set1_pd(m); + m2f = _mm256_set1_pd(m * 2); + + n = (limb_t)1 << fft_len_log2; + assert(n >= 8); + stride_in = n / 2; + + tab_in = in_buf; + tab_out = tmp_buf; + trig = get_trig(s, fft_len_log2, inverse, m_idx); + if (!trig) + return -1; + p = 0; + for(k = 0; k < stride_in; k += 4) { + a0 = _mm256_load_pd(&tab_in[k]); + a1 = _mm256_load_pd(&tab_in[k + stride_in]); + c = _mm256_load_pd(trig); + trig += 4; + b0 = ntt_mod(a0 + a1, mf, m2f); + b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); + a0 = _mm256_permute2f128_pd(b0, b1, 0x20); + a1 = _mm256_permute2f128_pd(b0, b1, 0x31); + a0 = _mm256_permute4x64_pd(a0, 0xd8); + a1 = _mm256_permute4x64_pd(a1, 0xd8); + _mm256_store_pd(&tab_out[p], a0); + _mm256_store_pd(&tab_out[p + 4], a1); + p += 2 * 4; + } + tmp = tab_in; + tab_in = tab_out; + tab_out = tmp; + + trig = get_trig(s, fft_len_log2 - 1, inverse, m_idx); + if (!trig) + return -1; + p = 0; + for(k = 0; k < stride_in; k += 4) { + a0 = _mm256_load_pd(&tab_in[k]); + a1 = _mm256_load_pd(&tab_in[k + stride_in]); + c = _mm256_setr_pd(trig[0], trig[0], trig[1], trig[1]); + trig += 2; + b0 = ntt_mod(a0 + a1, mf, m2f); + b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); + a0 = _mm256_permute2f128_pd(b0, b1, 0x20); + a1 = _mm256_permute2f128_pd(b0, b1, 0x31); + _mm256_store_pd(&tab_out[p], a0); + _mm256_store_pd(&tab_out[p + 4], a1); + p += 2 * 4; + } + tmp = tab_in; + tab_in = tab_out; + tab_out = tmp; + + nb_blocks = n / 4; + fft_per_block = 4; + + l = fft_len_log2 - 2; + while (nb_blocks != 2) { + nb_blocks >>= 1; + p = 0; + k = 0; + trig = get_trig(s, l, inverse, m_idx); + if (!trig) + return -1; + for(i = 0; i < nb_blocks; i++) { + c = _mm256_set1_pd(trig[0]); + trig++; + for(j = 0; j < fft_per_block; j += 4) { + a0 = _mm256_load_pd(&tab_in[k + j]); + a1 = _mm256_load_pd(&tab_in[k + j + stride_in]); + b0 = ntt_mod(a0 + a1, mf, m2f); + b1 = ntt_mul_mod(a0 - a1, c, mf, m_inv); + _mm256_store_pd(&tab_out[p + j], b0); + _mm256_store_pd(&tab_out[p + j + fft_per_block], b1); + } + k += fft_per_block; + p += 2 * fft_per_block; + } + fft_per_block <<= 1; + l--; + tmp = tab_in; + tab_in = tab_out; + tab_out = tmp; + } + + tab_out = out_buf; + for(k = 0; k < stride_in; k += 4) { + a0 = _mm256_load_pd(&tab_in[k]); + a1 = _mm256_load_pd(&tab_in[k + stride_in]); + b0 = ntt_mod(a0 + a1, mf, m2f); + b1 = ntt_mod(a0 - a1, mf, m2f); + _mm256_store_pd(&tab_out[k], b0); + _mm256_store_pd(&tab_out[k + stride_in], b1); + } + return 0; +} + +static void ntt_vec_mul(BFNTTState *s, + NTTLimb *tab1, NTTLimb *tab2, limb_t fft_len_log2, + int k_tot, int m_idx) +{ + limb_t i, c_inv, n, m; + __m256d m_inv, mf, a, b, c; + + m = ntt_mods[m_idx]; + c_inv = s->ntt_len_inv[m_idx][k_tot][0]; + m_inv = _mm256_set1_pd(1.0 / (double)m); + mf = _mm256_set1_pd(m); + c = _mm256_set1_pd(int_to_ntt_limb(c_inv, m)); + n = (limb_t)1 << fft_len_log2; + for(i = 0; i < n; i += 4) { + a = _mm256_load_pd(&tab1[i]); + b = _mm256_load_pd(&tab2[i]); + a = ntt_mul_mod(a, b, mf, m_inv); + a = ntt_mul_mod(a, c, mf, m_inv); + _mm256_store_pd(&tab1[i], a); + } +} + +static no_inline void mul_trig(NTTLimb *buf, + limb_t n, limb_t c1, limb_t m, limb_t m_inv1) +{ + limb_t i, c2, c3, c4; + __m256d c, c_mul, a0, mf, m_inv; + assert(n >= 2); + + mf = _mm256_set1_pd(m); + m_inv = _mm256_set1_pd(1.0 / (double)m); + + c2 = mul_mod_fast(c1, c1, m, m_inv1); + c3 = mul_mod_fast(c2, c1, m, m_inv1); + c4 = mul_mod_fast(c2, c2, m, m_inv1); + c = _mm256_setr_pd(1, int_to_ntt_limb(c1, m), + int_to_ntt_limb(c2, m), int_to_ntt_limb(c3, m)); + c_mul = _mm256_set1_pd(int_to_ntt_limb(c4, m)); + for(i = 0; i < n; i += 4) { + a0 = _mm256_load_pd(&buf[i]); + a0 = ntt_mul_mod(a0, c, mf, m_inv); + _mm256_store_pd(&buf[i], a0); + c = ntt_mul_mod(c, c_mul, mf, m_inv); + } +} + +#else + +static void *ntt_malloc(BFNTTState *s, size_t size) +{ + return bf_malloc(s->ctx, size); +} + +static void ntt_free(BFNTTState *s, void *ptr) +{ + bf_free(s->ctx, ptr); +} + +static inline limb_t ntt_limb_to_int(NTTLimb a, limb_t m) +{ + if (a >= m) + a -= m; + return a; +} + +static inline NTTLimb int_to_ntt_limb(slimb_t a, limb_t m) +{ + return a; +} + +static no_inline int ntt_fft(BFNTTState *s, NTTLimb *out_buf, NTTLimb *in_buf, + NTTLimb *tmp_buf, int fft_len_log2, + int inverse, int m_idx) +{ + limb_t nb_blocks, fft_per_block, p, k, n, stride_in, i, j, m, m2; + NTTLimb *tab_in, *tab_out, *tmp, a0, a1, b0, b1, c, *trig, c_inv; + int l; + + m = ntt_mods[m_idx]; + m2 = 2 * m; + n = (limb_t)1 << fft_len_log2; + nb_blocks = n; + fft_per_block = 1; + stride_in = n / 2; + tab_in = in_buf; + tab_out = tmp_buf; + l = fft_len_log2; + while (nb_blocks != 2) { + nb_blocks >>= 1; + p = 0; + k = 0; + trig = get_trig(s, l, inverse, m_idx); + if (!trig) + return -1; + for(i = 0; i < nb_blocks; i++) { + c = trig[0]; + c_inv = trig[1]; + trig += 2; + for(j = 0; j < fft_per_block; j++) { + a0 = tab_in[k + j]; + a1 = tab_in[k + j + stride_in]; + b0 = add_mod(a0, a1, m2); + b1 = a0 - a1 + m2; + b1 = mul_mod_fast3(b1, c, m, c_inv); + tab_out[p + j] = b0; + tab_out[p + j + fft_per_block] = b1; + } + k += fft_per_block; + p += 2 * fft_per_block; + } + fft_per_block <<= 1; + l--; + tmp = tab_in; + tab_in = tab_out; + tab_out = tmp; + } + /* no twiddle in last step */ + tab_out = out_buf; + for(k = 0; k < stride_in; k++) { + a0 = tab_in[k]; + a1 = tab_in[k + stride_in]; + b0 = add_mod(a0, a1, m2); + b1 = sub_mod(a0, a1, m2); + tab_out[k] = b0; + tab_out[k + stride_in] = b1; + } + return 0; +} + +static void ntt_vec_mul(BFNTTState *s, + NTTLimb *tab1, NTTLimb *tab2, int fft_len_log2, + int k_tot, int m_idx) +{ + limb_t i, norm, norm_inv, a, n, m, m_inv; + + m = ntt_mods[m_idx]; + m_inv = s->ntt_mods_div[m_idx]; + norm = s->ntt_len_inv[m_idx][k_tot][0]; + norm_inv = s->ntt_len_inv[m_idx][k_tot][1]; + n = (limb_t)1 << fft_len_log2; + for(i = 0; i < n; i++) { + a = tab1[i]; + /* need to reduce the range so that the product is < + 2^(LIMB_BITS+NTT_MOD_LOG2_MIN) */ + if (a >= m) + a -= m; + a = mul_mod_fast(a, tab2[i], m, m_inv); + a = mul_mod_fast3(a, norm, m, norm_inv); + tab1[i] = a; + } +} + +static no_inline void mul_trig(NTTLimb *buf, + limb_t n, limb_t c_mul, limb_t m, limb_t m_inv) +{ + limb_t i, c0, c_mul_inv; + + c0 = 1; + c_mul_inv = init_mul_mod_fast2(c_mul, m); + for(i = 0; i < n; i++) { + buf[i] = mul_mod_fast(buf[i], c0, m, m_inv); + c0 = mul_mod_fast2(c0, c_mul, m, c_mul_inv); + } +} + +#endif /* !AVX2 */ + +static no_inline NTTLimb *get_trig(BFNTTState *s, + int k, int inverse, int m_idx) +{ + NTTLimb *tab; + limb_t i, n2, c, c_mul, m, c_mul_inv; + + if (k > NTT_TRIG_K_MAX) + return NULL; + + tab = s->ntt_trig[m_idx][inverse][k]; + if (tab) + return tab; + n2 = (limb_t)1 << (k - 1); + m = ntt_mods[m_idx]; +#ifdef __AVX2__ + tab = ntt_malloc(s, sizeof(NTTLimb) * n2); +#else + tab = ntt_malloc(s, sizeof(NTTLimb) * n2 * 2); +#endif + if (!tab) + return NULL; + c = 1; + c_mul = s->ntt_proot_pow[m_idx][inverse][k]; + c_mul_inv = s->ntt_proot_pow_inv[m_idx][inverse][k]; + for(i = 0; i < n2; i++) { +#ifdef __AVX2__ + tab[i] = int_to_ntt_limb2(c, m); +#else + tab[2 * i] = int_to_ntt_limb(c, m); + tab[2 * i + 1] = init_mul_mod_fast2(c, m); +#endif + c = mul_mod_fast2(c, c_mul, m, c_mul_inv); + } + s->ntt_trig[m_idx][inverse][k] = tab; + return tab; +} + +void fft_clear_cache(bf_context_t *s1) +{ + int m_idx, inverse, k; + BFNTTState *s = s1->ntt_state; + if (s) { + for(m_idx = 0; m_idx < NB_MODS; m_idx++) { + for(inverse = 0; inverse < 2; inverse++) { + for(k = 0; k < NTT_TRIG_K_MAX + 1; k++) { + if (s->ntt_trig[m_idx][inverse][k]) { + ntt_free(s, s->ntt_trig[m_idx][inverse][k]); + s->ntt_trig[m_idx][inverse][k] = NULL; + } + } + } + } +#if defined(__AVX2__) + bf_aligned_free(s1, s); +#else + bf_free(s1, s); +#endif + s1->ntt_state = NULL; + } +} + +#define STRIP_LEN 16 + +/* dst = buf1, src = buf2 */ +static int ntt_fft_partial(BFNTTState *s, NTTLimb *buf1, + int k1, int k2, limb_t n1, limb_t n2, int inverse, + limb_t m_idx) +{ + limb_t i, j, c_mul, c0, m, m_inv, strip_len, l; + NTTLimb *buf2, *buf3; + + buf2 = NULL; + buf3 = ntt_malloc(s, sizeof(NTTLimb) * n1); + if (!buf3) + goto fail; + if (k2 == 0) { + if (ntt_fft(s, buf1, buf1, buf3, k1, inverse, m_idx)) + goto fail; + } else { + strip_len = STRIP_LEN; + buf2 = ntt_malloc(s, sizeof(NTTLimb) * n1 * strip_len); + if (!buf2) + goto fail; + m = ntt_mods[m_idx]; + m_inv = s->ntt_mods_div[m_idx]; + c0 = s->ntt_proot_pow[m_idx][inverse][k1 + k2]; + c_mul = 1; + assert((n2 % strip_len) == 0); + for(j = 0; j < n2; j += strip_len) { + for(i = 0; i < n1; i++) { + for(l = 0; l < strip_len; l++) { + buf2[i + l * n1] = buf1[i * n2 + (j + l)]; + } + } + for(l = 0; l < strip_len; l++) { + if (inverse) + mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv); + if (ntt_fft(s, buf2 + l * n1, buf2 + l * n1, buf3, k1, inverse, m_idx)) + goto fail; + if (!inverse) + mul_trig(buf2 + l * n1, n1, c_mul, m, m_inv); + c_mul = mul_mod_fast(c_mul, c0, m, m_inv); + } + + for(i = 0; i < n1; i++) { + for(l = 0; l < strip_len; l++) { + buf1[i * n2 + (j + l)] = buf2[i + l *n1]; + } + } + } + ntt_free(s, buf2); + } + ntt_free(s, buf3); + return 0; + fail: + ntt_free(s, buf2); + ntt_free(s, buf3); + return -1; +} + + +/* dst = buf1, src = buf2, tmp = buf3 */ +static int ntt_conv(BFNTTState *s, NTTLimb *buf1, NTTLimb *buf2, + int k, int k_tot, limb_t m_idx) +{ + limb_t n1, n2, i; + int k1, k2; + + if (k <= NTT_TRIG_K_MAX) { + k1 = k; + } else { + /* recursive split of the FFT */ + k1 = bf_min(k / 2, NTT_TRIG_K_MAX); + } + k2 = k - k1; + n1 = (limb_t)1 << k1; + n2 = (limb_t)1 << k2; + + if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 0, m_idx)) + return -1; + if (ntt_fft_partial(s, buf2, k1, k2, n1, n2, 0, m_idx)) + return -1; + if (k2 == 0) { + ntt_vec_mul(s, buf1, buf2, k, k_tot, m_idx); + } else { + for(i = 0; i < n1; i++) { + ntt_conv(s, buf1 + i * n2, buf2 + i * n2, k2, k_tot, m_idx); + } + } + if (ntt_fft_partial(s, buf1, k1, k2, n1, n2, 1, m_idx)) + return -1; + return 0; +} + + +static no_inline void limb_to_ntt(BFNTTState *s, + NTTLimb *tabr, limb_t fft_len, + const limb_t *taba, limb_t a_len, int dpl, + int first_m_idx, int nb_mods) +{ + slimb_t i, n; + dlimb_t a, b; + int j, shift; + limb_t base_mask1, a0, a1, a2, r, m, m_inv; + +#if 0 + for(i = 0; i < a_len; i++) { + printf("%" PRId64 ": " FMT_LIMB "\n", + (int64_t)i, taba[i]); + } +#endif + memset(tabr, 0, sizeof(NTTLimb) * fft_len * nb_mods); + shift = dpl & (LIMB_BITS - 1); + if (shift == 0) + base_mask1 = -1; + else + base_mask1 = ((limb_t)1 << shift) - 1; + n = bf_min(fft_len, (a_len * LIMB_BITS + dpl - 1) / dpl); + for(i = 0; i < n; i++) { + a0 = get_bits(taba, a_len, i * dpl); + if (dpl <= LIMB_BITS) { + a0 &= base_mask1; + a = a0; + } else { + a1 = get_bits(taba, a_len, i * dpl + LIMB_BITS); + if (dpl <= (LIMB_BITS + NTT_MOD_LOG2_MIN)) { + a = a0 | ((dlimb_t)(a1 & base_mask1) << LIMB_BITS); + } else { + if (dpl > 2 * LIMB_BITS) { + a2 = get_bits(taba, a_len, i * dpl + LIMB_BITS * 2) & + base_mask1; + } else { + a1 &= base_mask1; + a2 = 0; + } + // printf("a=0x%016lx%016lx%016lx\n", a2, a1, a0); + a = (a0 >> (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) | + ((dlimb_t)a1 << (NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN)) | + ((dlimb_t)a2 << (LIMB_BITS + NTT_MOD_LOG2_MAX - NTT_MOD_LOG2_MIN)); + a0 &= ((limb_t)1 << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) - 1; + } + } + for(j = 0; j < nb_mods; j++) { + m = ntt_mods[first_m_idx + j]; + m_inv = s->ntt_mods_div[first_m_idx + j]; + r = mod_fast(a, m, m_inv); + if (dpl > (LIMB_BITS + NTT_MOD_LOG2_MIN)) { + b = ((dlimb_t)r << (LIMB_BITS - NTT_MOD_LOG2_MAX + NTT_MOD_LOG2_MIN)) | a0; + r = mod_fast(b, m, m_inv); + } + tabr[i + j * fft_len] = int_to_ntt_limb(r, m); + } + } +} + +#if defined(__AVX2__) + +#define VEC_LEN 4 + +typedef union { + __m256d v; + double d[4]; +} VecUnion; + +static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len, + const NTTLimb *buf, int fft_len_log2, int dpl, + int nb_mods) +{ + const limb_t *mods = ntt_mods + NB_MODS - nb_mods; + const __m256d *mods_cr_vec, *mf, *m_inv; + VecUnion y[NB_MODS]; + limb_t u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r; + slimb_t i, len, pos; + int j, k, l, shift, n_limb1, p; + dlimb_t t; + + j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2; + mods_cr_vec = s->ntt_mods_cr_vec + j; + mf = s->ntt_mods_vec + NB_MODS - nb_mods; + m_inv = s->ntt_mods_inv_vec + NB_MODS - nb_mods; + + shift = dpl & (LIMB_BITS - 1); + if (shift == 0) + base_mask1 = -1; + else + base_mask1 = ((limb_t)1 << shift) - 1; + n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS; + for(j = 0; j < NB_MODS; j++) + carry[j] = 0; + for(j = 0; j < NB_MODS; j++) + u[j] = 0; /* avoid warnings */ + memset(tabr, 0, sizeof(limb_t) * r_len); + fft_len = (limb_t)1 << fft_len_log2; + len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl); + len = (len + VEC_LEN - 1) & ~(VEC_LEN - 1); + i = 0; + while (i < len) { + for(j = 0; j < nb_mods; j++) + y[j].v = *(__m256d *)&buf[i + fft_len * j]; + + /* Chinese remainder to get mixed radix representation */ + l = 0; + for(j = 0; j < nb_mods - 1; j++) { + y[j].v = ntt_mod1(y[j].v, mf[j]); + for(k = j + 1; k < nb_mods; k++) { + y[k].v = ntt_mul_mod(y[k].v - y[j].v, + mods_cr_vec[l], mf[k], m_inv[k]); + l++; + } + } + y[j].v = ntt_mod1(y[j].v, mf[j]); + + for(p = 0; p < VEC_LEN; p++) { + /* back to normal representation */ + u[0] = (int64_t)y[nb_mods - 1].d[p]; + l = 1; + for(j = nb_mods - 2; j >= 1; j--) { + r = (int64_t)y[j].d[p]; + for(k = 0; k < l; k++) { + t = (dlimb_t)u[k] * mods[j] + r; + r = t >> LIMB_BITS; + u[k] = t; + } + u[l] = r; + l++; + } + /* XXX: for nb_mods = 5, l should be 4 */ + + /* last step adds the carry */ + r = (int64_t)y[0].d[p]; + for(k = 0; k < l; k++) { + t = (dlimb_t)u[k] * mods[j] + r + carry[k]; + r = t >> LIMB_BITS; + u[k] = t; + } + u[l] = r + carry[l]; + +#if 0 + printf("%" PRId64 ": ", i); + for(j = nb_mods - 1; j >= 0; j--) { + printf(" %019" PRIu64, u[j]); + } + printf("\n"); +#endif + + /* write the digits */ + pos = i * dpl; + for(j = 0; j < n_limb1; j++) { + put_bits(tabr, r_len, pos, u[j]); + pos += LIMB_BITS; + } + put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1); + /* shift by dpl digits and set the carry */ + if (shift == 0) { + for(j = n_limb1 + 1; j < nb_mods; j++) + carry[j - (n_limb1 + 1)] = u[j]; + } else { + for(j = n_limb1; j < nb_mods - 1; j++) { + carry[j - n_limb1] = (u[j] >> shift) | + (u[j + 1] << (LIMB_BITS - shift)); + } + carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift; + } + i++; + } + } +} +#else +static no_inline void ntt_to_limb(BFNTTState *s, limb_t *tabr, limb_t r_len, + const NTTLimb *buf, int fft_len_log2, int dpl, + int nb_mods) +{ + const limb_t *mods = ntt_mods + NB_MODS - nb_mods; + const limb_t *mods_cr, *mods_cr_inv; + limb_t y[NB_MODS], u[NB_MODS], carry[NB_MODS], fft_len, base_mask1, r; + slimb_t i, len, pos; + int j, k, l, shift, n_limb1; + dlimb_t t; + + j = NB_MODS * (NB_MODS - 1) / 2 - nb_mods * (nb_mods - 1) / 2; + mods_cr = ntt_mods_cr + j; + mods_cr_inv = s->ntt_mods_cr_inv + j; + + shift = dpl & (LIMB_BITS - 1); + if (shift == 0) + base_mask1 = -1; + else + base_mask1 = ((limb_t)1 << shift) - 1; + n_limb1 = ((unsigned)dpl - 1) / LIMB_BITS; + for(j = 0; j < NB_MODS; j++) + carry[j] = 0; + for(j = 0; j < NB_MODS; j++) + u[j] = 0; /* avoid warnings */ + memset(tabr, 0, sizeof(limb_t) * r_len); + fft_len = (limb_t)1 << fft_len_log2; + len = bf_min(fft_len, (r_len * LIMB_BITS + dpl - 1) / dpl); + for(i = 0; i < len; i++) { + for(j = 0; j < nb_mods; j++) { + y[j] = ntt_limb_to_int(buf[i + fft_len * j], mods[j]); + } + + /* Chinese remainder to get mixed radix representation */ + l = 0; + for(j = 0; j < nb_mods - 1; j++) { + for(k = j + 1; k < nb_mods; k++) { + limb_t m; + m = mods[k]; + /* Note: there is no overflow in the sub_mod() because + the modulos are sorted by increasing order */ + y[k] = mul_mod_fast2(y[k] - y[j] + m, + mods_cr[l], m, mods_cr_inv[l]); + l++; + } + } + + /* back to normal representation */ + u[0] = y[nb_mods - 1]; + l = 1; + for(j = nb_mods - 2; j >= 1; j--) { + r = y[j]; + for(k = 0; k < l; k++) { + t = (dlimb_t)u[k] * mods[j] + r; + r = t >> LIMB_BITS; + u[k] = t; + } + u[l] = r; + l++; + } + + /* last step adds the carry */ + r = y[0]; + for(k = 0; k < l; k++) { + t = (dlimb_t)u[k] * mods[j] + r + carry[k]; + r = t >> LIMB_BITS; + u[k] = t; + } + u[l] = r + carry[l]; + +#if 0 + printf("%" PRId64 ": ", (int64_t)i); + for(j = nb_mods - 1; j >= 0; j--) { + printf(" " FMT_LIMB, u[j]); + } + printf("\n"); +#endif + + /* write the digits */ + pos = i * dpl; + for(j = 0; j < n_limb1; j++) { + put_bits(tabr, r_len, pos, u[j]); + pos += LIMB_BITS; + } + put_bits(tabr, r_len, pos, u[n_limb1] & base_mask1); + /* shift by dpl digits and set the carry */ + if (shift == 0) { + for(j = n_limb1 + 1; j < nb_mods; j++) + carry[j - (n_limb1 + 1)] = u[j]; + } else { + for(j = n_limb1; j < nb_mods - 1; j++) { + carry[j - n_limb1] = (u[j] >> shift) | + (u[j + 1] << (LIMB_BITS - shift)); + } + carry[nb_mods - 1 - n_limb1] = u[nb_mods - 1] >> shift; + } + } +} +#endif + +static int ntt_static_init(bf_context_t *s1) +{ + BFNTTState *s; + int inverse, i, j, k, l; + limb_t c, c_inv, c_inv2, m, m_inv; + + if (s1->ntt_state) + return 0; +#if defined(__AVX2__) + s = bf_aligned_malloc(s1, sizeof(*s), 64); +#else + s = bf_malloc(s1, sizeof(*s)); +#endif + if (!s) + return -1; + memset(s, 0, sizeof(*s)); + s1->ntt_state = s; + s->ctx = s1; + + for(j = 0; j < NB_MODS; j++) { + m = ntt_mods[j]; + m_inv = init_mul_mod_fast(m); + s->ntt_mods_div[j] = m_inv; +#if defined(__AVX2__) + s->ntt_mods_vec[j] = _mm256_set1_pd(m); + s->ntt_mods_inv_vec[j] = _mm256_set1_pd(1.0 / (double)m); +#endif + c_inv2 = (m + 1) / 2; /* 1/2 */ + c_inv = 1; + for(i = 0; i <= NTT_PROOT_2EXP; i++) { + s->ntt_len_inv[j][i][0] = c_inv; + s->ntt_len_inv[j][i][1] = init_mul_mod_fast2(c_inv, m); + c_inv = mul_mod_fast(c_inv, c_inv2, m, m_inv); + } + + for(inverse = 0; inverse < 2; inverse++) { + c = ntt_proot[inverse][j]; + for(i = 0; i < NTT_PROOT_2EXP; i++) { + s->ntt_proot_pow[j][inverse][NTT_PROOT_2EXP - i] = c; + s->ntt_proot_pow_inv[j][inverse][NTT_PROOT_2EXP - i] = + init_mul_mod_fast2(c, m); + c = mul_mod_fast(c, c, m, m_inv); + } + } + } + + l = 0; + for(j = 0; j < NB_MODS - 1; j++) { + for(k = j + 1; k < NB_MODS; k++) { +#if defined(__AVX2__) + s->ntt_mods_cr_vec[l] = _mm256_set1_pd(int_to_ntt_limb2(ntt_mods_cr[l], + ntt_mods[k])); +#else + s->ntt_mods_cr_inv[l] = init_mul_mod_fast2(ntt_mods_cr[l], + ntt_mods[k]); +#endif + l++; + } + } + return 0; +} + +int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len) +{ + int dpl, fft_len_log2, n_bits, nb_mods, dpl_found, fft_len_log2_found; + int int_bits, nb_mods_found; + limb_t cost, min_cost; + + min_cost = -1; + dpl_found = 0; + nb_mods_found = 4; + fft_len_log2_found = 0; + for(nb_mods = 3; nb_mods <= NB_MODS; nb_mods++) { + int_bits = ntt_int_bits[NB_MODS - nb_mods]; + dpl = bf_min((int_bits - 4) / 2, + 2 * LIMB_BITS + 2 * NTT_MOD_LOG2_MIN - NTT_MOD_LOG2_MAX); + for(;;) { + fft_len_log2 = ceil_log2((len * LIMB_BITS + dpl - 1) / dpl); + if (fft_len_log2 > NTT_PROOT_2EXP) + goto next; + n_bits = fft_len_log2 + 2 * dpl; + if (n_bits <= int_bits) { + cost = ((limb_t)(fft_len_log2 + 1) << fft_len_log2) * nb_mods; + // printf("n=%d dpl=%d: cost=%" PRId64 "\n", nb_mods, dpl, (int64_t)cost); + if (cost < min_cost) { + min_cost = cost; + dpl_found = dpl; + nb_mods_found = nb_mods; + fft_len_log2_found = fft_len_log2; + } + break; + } + dpl--; + if (dpl == 0) + break; + } + next: ; + } + if (!dpl_found) + abort(); + /* limit dpl if possible to reduce fixed cost of limb/NTT conversion */ + if (dpl_found > (LIMB_BITS + NTT_MOD_LOG2_MIN) && + ((limb_t)(LIMB_BITS + NTT_MOD_LOG2_MIN) << fft_len_log2_found) >= + len * LIMB_BITS) { + dpl_found = LIMB_BITS + NTT_MOD_LOG2_MIN; + } + *pnb_mods = nb_mods_found; + *pdpl = dpl_found; + return fft_len_log2_found; +} + +/* return 0 if OK, -1 if memory error */ +static no_inline int fft_mul(bf_context_t *s1, + bf_t *res, limb_t *a_tab, limb_t a_len, + limb_t *b_tab, limb_t b_len, int mul_flags) +{ + BFNTTState *s; + int dpl, fft_len_log2, j, nb_mods, reduced_mem; + slimb_t len, fft_len; + NTTLimb *buf1, *buf2, *ptr; +#if defined(USE_MUL_CHECK) + limb_t ha, hb, hr, h_ref; +#endif + + if (ntt_static_init(s1)) + return -1; + s = s1->ntt_state; + + /* find the optimal number of digits per limb (dpl) */ + len = a_len + b_len; + fft_len_log2 = bf_get_fft_size(&dpl, &nb_mods, len); + fft_len = (uint64_t)1 << fft_len_log2; + // printf("len=%" PRId64 " fft_len_log2=%d dpl=%d\n", len, fft_len_log2, dpl); +#if defined(USE_MUL_CHECK) + ha = mp_mod1(a_tab, a_len, BF_CHKSUM_MOD, 0); + hb = mp_mod1(b_tab, b_len, BF_CHKSUM_MOD, 0); +#endif + if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) == 0) { + if (!(mul_flags & FFT_MUL_R_NORESIZE)) + bf_resize(res, 0); + } else if (mul_flags & FFT_MUL_R_OVERLAP_B) { + limb_t *tmp_tab, tmp_len; + /* it is better to free 'b' first */ + tmp_tab = a_tab; + a_tab = b_tab; + b_tab = tmp_tab; + tmp_len = a_len; + a_len = b_len; + b_len = tmp_len; + } + buf1 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods); + if (!buf1) + return -1; + limb_to_ntt(s, buf1, fft_len, a_tab, a_len, dpl, + NB_MODS - nb_mods, nb_mods); + if ((mul_flags & (FFT_MUL_R_OVERLAP_A | FFT_MUL_R_OVERLAP_B)) == + FFT_MUL_R_OVERLAP_A) { + if (!(mul_flags & FFT_MUL_R_NORESIZE)) + bf_resize(res, 0); + } + reduced_mem = (fft_len_log2 >= 14); + if (!reduced_mem) { + buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len * nb_mods); + if (!buf2) + goto fail; + limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl, + NB_MODS - nb_mods, nb_mods); + if (!(mul_flags & FFT_MUL_R_NORESIZE)) + bf_resize(res, 0); /* in case res == b */ + } else { + buf2 = ntt_malloc(s, sizeof(NTTLimb) * fft_len); + if (!buf2) + goto fail; + } + for(j = 0; j < nb_mods; j++) { + if (reduced_mem) { + limb_to_ntt(s, buf2, fft_len, b_tab, b_len, dpl, + NB_MODS - nb_mods + j, 1); + ptr = buf2; + } else { + ptr = buf2 + fft_len * j; + } + if (ntt_conv(s, buf1 + fft_len * j, ptr, + fft_len_log2, fft_len_log2, j + NB_MODS - nb_mods)) + goto fail; + } + if (!(mul_flags & FFT_MUL_R_NORESIZE)) + bf_resize(res, 0); /* in case res == b and reduced mem */ + ntt_free(s, buf2); + buf2 = NULL; + if (!(mul_flags & FFT_MUL_R_NORESIZE)) { + if (bf_resize(res, len)) + goto fail; + } + ntt_to_limb(s, res->tab, len, buf1, fft_len_log2, dpl, nb_mods); + ntt_free(s, buf1); +#if defined(USE_MUL_CHECK) + hr = mp_mod1(res->tab, len, BF_CHKSUM_MOD, 0); + h_ref = mul_mod(ha, hb, BF_CHKSUM_MOD); + if (hr != h_ref) { + printf("ntt_mul_error: len=%" PRId_LIMB " fft_len_log2=%d dpl=%d nb_mods=%d\n", + len, fft_len_log2, dpl, nb_mods); + // printf("ha=0x" FMT_LIMB" hb=0x" FMT_LIMB " hr=0x" FMT_LIMB " expected=0x" FMT_LIMB "\n", ha, hb, hr, h_ref); + exit(1); + } +#endif + return 0; + fail: + ntt_free(s, buf1); + ntt_free(s, buf2); + return -1; +} + +#else /* USE_FFT_MUL */ + +int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len) +{ + return 0; +} + +#endif /* !USE_FFT_MUL */ diff --git a/src/shared/quickjs/libbf.h b/src/shared/quickjs/libbf.h new file mode 100644 index 000000000..b247952b1 --- /dev/null +++ b/src/shared/quickjs/libbf.h @@ -0,0 +1,543 @@ +/* + * Tiny arbitrary precision floating point library + * + * Copyright (c) 2017-2021 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef LIBBF_H +#define LIBBF_H + +#include <stddef.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__SIZEOF_INT128__) && (INTPTR_MAX >= INT64_MAX) +#define LIMB_LOG2_BITS 6 +#else +#define LIMB_LOG2_BITS 5 +#endif + +#define LIMB_BITS (1 << LIMB_LOG2_BITS) + +#if LIMB_BITS == 64 +typedef __int128 int128_t; +typedef unsigned __int128 uint128_t; +typedef int64_t slimb_t; +typedef uint64_t limb_t; +typedef uint128_t dlimb_t; +#define BF_RAW_EXP_MIN INT64_MIN +#define BF_RAW_EXP_MAX INT64_MAX + +#define LIMB_DIGITS 19 +#define BF_DEC_BASE UINT64_C(10000000000000000000) + +#else + +typedef int32_t slimb_t; +typedef uint32_t limb_t; +typedef uint64_t dlimb_t; +#define BF_RAW_EXP_MIN INT32_MIN +#define BF_RAW_EXP_MAX INT32_MAX + +#define LIMB_DIGITS 9 +#define BF_DEC_BASE 1000000000U + +#endif + +/* in bits */ +/* minimum number of bits for the exponent */ +#define BF_EXP_BITS_MIN 3 +/* maximum number of bits for the exponent */ +#define BF_EXP_BITS_MAX (LIMB_BITS - 3) +/* extended range for exponent, used internally */ +#define BF_EXT_EXP_BITS_MAX (BF_EXP_BITS_MAX + 1) +/* minimum possible precision */ +#define BF_PREC_MIN 2 +/* minimum possible precision */ +#define BF_PREC_MAX (((limb_t)1 << (LIMB_BITS - 2)) - 2) +/* some operations support infinite precision */ +#define BF_PREC_INF (BF_PREC_MAX + 1) /* infinite precision */ + +#if LIMB_BITS == 64 +#define BF_CHKSUM_MOD (UINT64_C(975620677) * UINT64_C(9795002197)) +#else +#define BF_CHKSUM_MOD 975620677U +#endif + +#define BF_EXP_ZERO BF_RAW_EXP_MIN +#define BF_EXP_INF (BF_RAW_EXP_MAX - 1) +#define BF_EXP_NAN BF_RAW_EXP_MAX + +/* +/-zero is represented with expn = BF_EXP_ZERO and len = 0, + +/-infinity is represented with expn = BF_EXP_INF and len = 0, + NaN is represented with expn = BF_EXP_NAN and len = 0 (sign is ignored) + */ +typedef struct { + struct bf_context_t *ctx; + int sign; + slimb_t expn; + limb_t len; + limb_t *tab; +} bf_t; + +typedef struct { + /* must be kept identical to bf_t */ + struct bf_context_t *ctx; + int sign; + slimb_t expn; + limb_t len; + limb_t *tab; +} bfdec_t; + +typedef enum { + BF_RNDN, /* round to nearest, ties to even */ + BF_RNDZ, /* round to zero */ + BF_RNDD, /* round to -inf (the code relies on (BF_RNDD xor BF_RNDU) = 1) */ + BF_RNDU, /* round to +inf */ + BF_RNDNA, /* round to nearest, ties away from zero */ + BF_RNDA, /* round away from zero */ + BF_RNDF, /* faithful rounding (nondeterministic, either RNDD or RNDU, + inexact flag is always set) */ +} bf_rnd_t; + +/* allow subnormal numbers. Only available if the number of exponent + bits is <= BF_EXP_BITS_USER_MAX and prec != BF_PREC_INF. */ +#define BF_FLAG_SUBNORMAL (1 << 3) +/* 'prec' is the precision after the radix point instead of the whole + mantissa. Can only be used with bf_round() and + bfdec_[add|sub|mul|div|sqrt|round](). */ +#define BF_FLAG_RADPNT_PREC (1 << 4) + +#define BF_RND_MASK 0x7 +#define BF_EXP_BITS_SHIFT 5 +#define BF_EXP_BITS_MASK 0x3f + +/* shortcut for bf_set_exp_bits(BF_EXT_EXP_BITS_MAX) */ +#define BF_FLAG_EXT_EXP (BF_EXP_BITS_MASK << BF_EXP_BITS_SHIFT) + +/* contains the rounding mode and number of exponents bits */ +typedef uint32_t bf_flags_t; + +typedef void *bf_realloc_func_t(void *opaque, void *ptr, size_t size); + +typedef struct { + bf_t val; + limb_t prec; +} BFConstCache; + +typedef struct bf_context_t { + void *realloc_opaque; + bf_realloc_func_t *realloc_func; + BFConstCache log2_cache; + BFConstCache pi_cache; + struct BFNTTState *ntt_state; +} bf_context_t; + +static inline int bf_get_exp_bits(bf_flags_t flags) +{ + int e; + e = (flags >> BF_EXP_BITS_SHIFT) & BF_EXP_BITS_MASK; + if (e == BF_EXP_BITS_MASK) + return BF_EXP_BITS_MAX + 1; + else + return BF_EXP_BITS_MAX - e; +} + +static inline bf_flags_t bf_set_exp_bits(int n) +{ + return ((BF_EXP_BITS_MAX - n) & BF_EXP_BITS_MASK) << BF_EXP_BITS_SHIFT; +} + +/* returned status */ +#define BF_ST_INVALID_OP (1 << 0) +#define BF_ST_DIVIDE_ZERO (1 << 1) +#define BF_ST_OVERFLOW (1 << 2) +#define BF_ST_UNDERFLOW (1 << 3) +#define BF_ST_INEXACT (1 << 4) +/* indicate that a memory allocation error occured. NaN is returned */ +#define BF_ST_MEM_ERROR (1 << 5) + +#define BF_RADIX_MAX 36 /* maximum radix for bf_atof() and bf_ftoa() */ + +static inline slimb_t bf_max(slimb_t a, slimb_t b) +{ + if (a > b) + return a; + else + return b; +} + +static inline slimb_t bf_min(slimb_t a, slimb_t b) +{ + if (a < b) + return a; + else + return b; +} + +void bf_context_init(bf_context_t *s, bf_realloc_func_t *realloc_func, + void *realloc_opaque); +void bf_context_end(bf_context_t *s); +/* free memory allocated for the bf cache data */ +void bf_clear_cache(bf_context_t *s); + +static inline void *bf_realloc(bf_context_t *s, void *ptr, size_t size) +{ + return s->realloc_func(s->realloc_opaque, ptr, size); +} + +/* 'size' must be != 0 */ +static inline void *bf_malloc(bf_context_t *s, size_t size) +{ + return bf_realloc(s, NULL, size); +} + +static inline void bf_free(bf_context_t *s, void *ptr) +{ + /* must test ptr otherwise equivalent to malloc(0) */ + if (ptr) + bf_realloc(s, ptr, 0); +} + +void bf_init(bf_context_t *s, bf_t *r); + +static inline void bf_delete(bf_t *r) +{ + bf_context_t *s = r->ctx; + /* we accept to delete a zeroed bf_t structure */ + if (s && r->tab) { + bf_realloc(s, r->tab, 0); + } +} + +static inline void bf_neg(bf_t *r) +{ + r->sign ^= 1; +} + +static inline int bf_is_finite(const bf_t *a) +{ + return (a->expn < BF_EXP_INF); +} + +static inline int bf_is_nan(const bf_t *a) +{ + return (a->expn == BF_EXP_NAN); +} + +static inline int bf_is_zero(const bf_t *a) +{ + return (a->expn == BF_EXP_ZERO); +} + +static inline void bf_memcpy(bf_t *r, const bf_t *a) +{ + *r = *a; +} + +int bf_set_ui(bf_t *r, uint64_t a); +int bf_set_si(bf_t *r, int64_t a); +void bf_set_nan(bf_t *r); +void bf_set_zero(bf_t *r, int is_neg); +void bf_set_inf(bf_t *r, int is_neg); +int bf_set(bf_t *r, const bf_t *a); +void bf_move(bf_t *r, bf_t *a); +int bf_get_float64(const bf_t *a, double *pres, bf_rnd_t rnd_mode); +int bf_set_float64(bf_t *a, double d); + +int bf_cmpu(const bf_t *a, const bf_t *b); +int bf_cmp_full(const bf_t *a, const bf_t *b); +int bf_cmp(const bf_t *a, const bf_t *b); +static inline int bf_cmp_eq(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) == 0; +} + +static inline int bf_cmp_le(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) <= 0; +} + +static inline int bf_cmp_lt(const bf_t *a, const bf_t *b) +{ + return bf_cmp(a, b) < 0; +} + +int bf_add(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); +int bf_sub(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); +int bf_add_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, bf_flags_t flags); +int bf_mul(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); +int bf_mul_ui(bf_t *r, const bf_t *a, uint64_t b1, limb_t prec, bf_flags_t flags); +int bf_mul_si(bf_t *r, const bf_t *a, int64_t b1, limb_t prec, + bf_flags_t flags); +int bf_mul_2exp(bf_t *r, slimb_t e, limb_t prec, bf_flags_t flags); +int bf_div(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, bf_flags_t flags); +#define BF_DIVREM_EUCLIDIAN BF_RNDF +int bf_divrem(bf_t *q, bf_t *r, const bf_t *a, const bf_t *b, + limb_t prec, bf_flags_t flags, int rnd_mode); +int bf_rem(bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode); +int bf_remquo(slimb_t *pq, bf_t *r, const bf_t *a, const bf_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode); +/* round to integer with infinite precision */ +int bf_rint(bf_t *r, int rnd_mode); +int bf_round(bf_t *r, limb_t prec, bf_flags_t flags); +int bf_sqrtrem(bf_t *r, bf_t *rem1, const bf_t *a); +int bf_sqrt(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +slimb_t bf_get_exp_min(const bf_t *a); +int bf_logic_or(bf_t *r, const bf_t *a, const bf_t *b); +int bf_logic_xor(bf_t *r, const bf_t *a, const bf_t *b); +int bf_logic_and(bf_t *r, const bf_t *a, const bf_t *b); + +/* additional flags for bf_atof */ +/* do not accept hex radix prefix (0x or 0X) if radix = 0 or radix = 16 */ +#define BF_ATOF_NO_HEX (1 << 16) +/* accept binary (0b or 0B) or octal (0o or 0O) radix prefix if radix = 0 */ +#define BF_ATOF_BIN_OCT (1 << 17) +/* Do not parse NaN or Inf */ +#define BF_ATOF_NO_NAN_INF (1 << 18) +/* return the exponent separately */ +#define BF_ATOF_EXPONENT (1 << 19) + +int bf_atof(bf_t *a, const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags); +/* this version accepts prec = BF_PREC_INF and returns the radix + exponent */ +int bf_atof2(bf_t *r, slimb_t *pexponent, + const char *str, const char **pnext, int radix, + limb_t prec, bf_flags_t flags); +int bf_mul_pow_radix(bf_t *r, const bf_t *T, limb_t radix, + slimb_t expn, limb_t prec, bf_flags_t flags); + + +/* Conversion of floating point number to string. Return a null + terminated string or NULL if memory error. *plen contains its + length if plen != NULL. The exponent letter is "e" for base 10, + "p" for bases 2, 8, 16 with a binary exponent and "@" for the other + bases. */ + +#define BF_FTOA_FORMAT_MASK (3 << 16) + +/* fixed format: prec significant digits rounded with (flags & + BF_RND_MASK). Exponential notation is used if too many zeros are + needed.*/ +#define BF_FTOA_FORMAT_FIXED (0 << 16) +/* fractional format: prec digits after the decimal point rounded with + (flags & BF_RND_MASK) */ +#define BF_FTOA_FORMAT_FRAC (1 << 16) +/* free format: + + For binary radices with bf_ftoa() and for bfdec_ftoa(): use the minimum + number of digits to represent 'a'. The precision and the rounding + mode are ignored. + + For the non binary radices with bf_ftoa(): use as many digits as + necessary so that bf_atof() return the same number when using + precision 'prec', rounding to nearest and the subnormal + configuration of 'flags'. The result is meaningful only if 'a' is + already rounded to 'prec' bits. If the subnormal flag is set, the + exponent in 'flags' must also be set to the desired exponent range. +*/ +#define BF_FTOA_FORMAT_FREE (2 << 16) +/* same as BF_FTOA_FORMAT_FREE but uses the minimum number of digits + (takes more computation time). Identical to BF_FTOA_FORMAT_FREE for + binary radices with bf_ftoa() and for bfdec_ftoa(). */ +#define BF_FTOA_FORMAT_FREE_MIN (3 << 16) + +/* force exponential notation for fixed or free format */ +#define BF_FTOA_FORCE_EXP (1 << 20) +/* add 0x prefix for base 16, 0o prefix for base 8 or 0b prefix for + base 2 if non zero value */ +#define BF_FTOA_ADD_PREFIX (1 << 21) +/* return "Infinity" instead of "Inf" and add a "+" for positive + exponents */ +#define BF_FTOA_JS_QUIRKS (1 << 22) + +char *bf_ftoa(size_t *plen, const bf_t *a, int radix, limb_t prec, + bf_flags_t flags); + +/* modulo 2^n instead of saturation. NaN and infinity return 0 */ +#define BF_GET_INT_MOD (1 << 0) +int bf_get_int32(int *pres, const bf_t *a, int flags); +int bf_get_int64(int64_t *pres, const bf_t *a, int flags); +int bf_get_uint64(uint64_t *pres, const bf_t *a); + +/* the following functions are exported for testing only. */ +void mp_print_str(const char *str, const limb_t *tab, limb_t n); +void bf_print_str(const char *str, const bf_t *a); +int bf_resize(bf_t *r, limb_t len); +int bf_get_fft_size(int *pdpl, int *pnb_mods, limb_t len); +int bf_normalize_and_round(bf_t *r, limb_t prec1, bf_flags_t flags); +int bf_can_round(const bf_t *a, slimb_t prec, bf_rnd_t rnd_mode, slimb_t k); +slimb_t bf_mul_log2_radix(slimb_t a1, unsigned int radix, int is_inv, + int is_ceil1); +int mp_mul(bf_context_t *s, limb_t *result, + const limb_t *op1, limb_t op1_size, + const limb_t *op2, limb_t op2_size); +limb_t mp_add(limb_t *res, const limb_t *op1, const limb_t *op2, + limb_t n, limb_t carry); +limb_t mp_add_ui(limb_t *tab, limb_t b, size_t n); +int mp_sqrtrem(bf_context_t *s, limb_t *tabs, limb_t *taba, limb_t n); +int mp_recip(bf_context_t *s, limb_t *tabr, const limb_t *taba, limb_t n); +limb_t bf_isqrt(limb_t a); + +/* transcendental functions */ +int bf_const_log2(bf_t *T, limb_t prec, bf_flags_t flags); +int bf_const_pi(bf_t *T, limb_t prec, bf_flags_t flags); +int bf_exp(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_log(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +#define BF_POW_JS_QUIRKS (1 << 16) /* (+/-1)^(+/-Inf) = NaN, 1^NaN = NaN */ +int bf_pow(bf_t *r, const bf_t *x, const bf_t *y, limb_t prec, bf_flags_t flags); +int bf_cos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_sin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_tan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_atan(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_atan2(bf_t *r, const bf_t *y, const bf_t *x, + limb_t prec, bf_flags_t flags); +int bf_asin(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); +int bf_acos(bf_t *r, const bf_t *a, limb_t prec, bf_flags_t flags); + +/* decimal floating point */ + +static inline void bfdec_init(bf_context_t *s, bfdec_t *r) +{ + bf_init(s, (bf_t *)r); +} +static inline void bfdec_delete(bfdec_t *r) +{ + bf_delete((bf_t *)r); +} + +static inline void bfdec_neg(bfdec_t *r) +{ + r->sign ^= 1; +} + +static inline int bfdec_is_finite(const bfdec_t *a) +{ + return (a->expn < BF_EXP_INF); +} + +static inline int bfdec_is_nan(const bfdec_t *a) +{ + return (a->expn == BF_EXP_NAN); +} + +static inline int bfdec_is_zero(const bfdec_t *a) +{ + return (a->expn == BF_EXP_ZERO); +} + +static inline void bfdec_memcpy(bfdec_t *r, const bfdec_t *a) +{ + bf_memcpy((bf_t *)r, (const bf_t *)a); +} + +int bfdec_set_ui(bfdec_t *r, uint64_t a); +int bfdec_set_si(bfdec_t *r, int64_t a); + +static inline void bfdec_set_nan(bfdec_t *r) +{ + bf_set_nan((bf_t *)r); +} +static inline void bfdec_set_zero(bfdec_t *r, int is_neg) +{ + bf_set_zero((bf_t *)r, is_neg); +} +static inline void bfdec_set_inf(bfdec_t *r, int is_neg) +{ + bf_set_inf((bf_t *)r, is_neg); +} +static inline int bfdec_set(bfdec_t *r, const bfdec_t *a) +{ + return bf_set((bf_t *)r, (bf_t *)a); +} +static inline void bfdec_move(bfdec_t *r, bfdec_t *a) +{ + bf_move((bf_t *)r, (bf_t *)a); +} +static inline int bfdec_cmpu(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmpu((const bf_t *)a, (const bf_t *)b); +} +static inline int bfdec_cmp_full(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmp_full((const bf_t *)a, (const bf_t *)b); +} +static inline int bfdec_cmp(const bfdec_t *a, const bfdec_t *b) +{ + return bf_cmp((const bf_t *)a, (const bf_t *)b); +} +static inline int bfdec_cmp_eq(const bfdec_t *a, const bfdec_t *b) +{ + return bfdec_cmp(a, b) == 0; +} +static inline int bfdec_cmp_le(const bfdec_t *a, const bfdec_t *b) +{ + return bfdec_cmp(a, b) <= 0; +} +static inline int bfdec_cmp_lt(const bfdec_t *a, const bfdec_t *b) +{ + return bfdec_cmp(a, b) < 0; +} + +int bfdec_add(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_sub(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_add_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags); +int bfdec_mul(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_mul_si(bfdec_t *r, const bfdec_t *a, int64_t b1, limb_t prec, + bf_flags_t flags); +int bfdec_div(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags); +int bfdec_divrem(bfdec_t *q, bfdec_t *r, const bfdec_t *a, const bfdec_t *b, + limb_t prec, bf_flags_t flags, int rnd_mode); +int bfdec_rem(bfdec_t *r, const bfdec_t *a, const bfdec_t *b, limb_t prec, + bf_flags_t flags, int rnd_mode); +int bfdec_rint(bfdec_t *r, int rnd_mode); +int bfdec_sqrt(bfdec_t *r, const bfdec_t *a, limb_t prec, bf_flags_t flags); +int bfdec_round(bfdec_t *r, limb_t prec, bf_flags_t flags); +int bfdec_get_int32(int *pres, const bfdec_t *a); +int bfdec_pow_ui(bfdec_t *r, const bfdec_t *a, limb_t b); + +char *bfdec_ftoa(size_t *plen, const bfdec_t *a, limb_t prec, bf_flags_t flags); +int bfdec_atof(bfdec_t *r, const char *str, const char **pnext, + limb_t prec, bf_flags_t flags); + +/* the following functions are exported for testing only. */ +extern const limb_t mp_pow_dec[LIMB_DIGITS + 1]; +void bfdec_print_str(const char *str, const bfdec_t *a); +static inline int bfdec_resize(bfdec_t *r, limb_t len) +{ + return bf_resize((bf_t *)r, len); +} +int bfdec_normalize_and_round(bfdec_t *r, limb_t prec1, bf_flags_t flags); + +#ifdef __cplusplus +} /* extern "C" { */ +#endif + +#endif /* LIBBF_H */ diff --git a/src/shared/quickjs/libregexp-opcode.h b/src/shared/quickjs/libregexp-opcode.h index f90c23b34..f255e09f2 100644 --- a/src/shared/quickjs/libregexp-opcode.h +++ b/src/shared/quickjs/libregexp-opcode.h @@ -1,6 +1,6 @@ /* * Regular Expression Engine - * + * * Copyright (c) 2017-2018 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -50,8 +50,7 @@ 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(check_advance, 1) /* pop one stack element and check that it is different from the character position */ DEF(prev, 1) /* go to the previous char */ DEF(simple_greedy_quant, 17) diff --git a/src/shared/quickjs/libregexp.c b/src/shared/quickjs/libregexp.c index ad91f781a..4db042941 100644 --- a/src/shared/quickjs/libregexp.c +++ b/src/shared/quickjs/libregexp.c @@ -30,13 +30,11 @@ #include "cutils.h" #include "libregexp.h" +#include "libunicode.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 @@ -69,7 +67,7 @@ typedef struct { const uint8_t *buf_end; const uint8_t *buf_start; int re_flags; - BOOL is_utf16; + BOOL is_unicode; BOOL ignore_case; BOOL dotall; int capture_count; @@ -103,6 +101,7 @@ static const REOpCode reopcode_info[REOP_COUNT] = { #define RE_HEADER_FLAGS 0 #define RE_HEADER_CAPTURE_COUNT 1 #define RE_HEADER_STACK_SIZE 2 +#define RE_HEADER_BYTECODE_LEN 3 #define RE_HEADER_LEN 7 @@ -120,33 +119,6 @@ static int dbuf_insert(DynBuf *s, int pos, int 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, @@ -170,32 +142,6 @@ static const uint16_t char_range_s[] = { 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, @@ -215,7 +161,7 @@ typedef enum { CHAR_RANGE_W, } CharRangeEnum; -static const uint16_t *char_range_table[] = { +static const uint16_t * const char_range_table[] = { char_range_d, char_range_s, char_range_w, @@ -245,33 +191,8 @@ static int cr_init_char_range(REParseState *s, CharRange *cr, uint32_t c) 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, +static __maybe_unused void lre_dump_bytecode(const uint8_t *buf, int buf_len) { int pos, len, opcode, bc_len, re_flags, i; @@ -279,16 +200,16 @@ static MAYBE_UNUSED void lre_dump_bytecode(const uint8_t *buf, assert(buf_len >= RE_HEADER_LEN); - re_flags= buf[0]; - bc_len = get_u32(buf + 3); + re_flags = lre_get_flags(buf); + bc_len = get_u32(buf + RE_HEADER_BYTECODE_LEN); 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]); + re_flags, buf[RE_HEADER_CAPTURE_COUNT], buf[RE_HEADER_STACK_SIZE]); 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++) { + for(i = 1; i < buf[RE_HEADER_CAPTURE_COUNT]; i++) { if (i != 1) printf(","); printf("<%s>", p); @@ -335,7 +256,6 @@ static MAYBE_UNUSED void lre_dump_bytecode(const uint8_t *buf, 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); @@ -550,7 +470,7 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16) } c = (c << 4) | h; } - if (c >= 0xd800 && c < 0xdc00 && + if (is_hi_surrogate(c) && allow_utf16 == 2 && p[0] == '\\' && p[1] == 'u') { /* convert an escaped surrogate pair into a unicode char */ @@ -561,9 +481,9 @@ int lre_parse_escape(const uint8_t **pp, int allow_utf16) break; c1 = (c1 << 4) | h; } - if (i == 4 && c1 >= 0xdc00 && c1 < 0xe000) { + if (i == 4 && is_lo_surrogate(c1)) { p += 6; - c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000; + c = from_surrogate(c, c1); } } } @@ -752,10 +672,10 @@ static int get_class_atom(REParseState *s, CharRange *cr, if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (((c >= '0' && c <= '9') || c == '_') && - inclass && !s->is_utf16)) { /* Annex B.1.4 */ + inclass && !s->is_unicode)) { /* Annex B.1.4 */ c &= 0x1f; p++; - } else if (s->is_utf16) { + } else if (s->is_unicode) { goto invalid_escape; } else { /* otherwise return '\' and 'c' */ @@ -766,7 +686,7 @@ static int get_class_atom(REParseState *s, CharRange *cr, #ifdef CONFIG_ALL_UNICODE case 'p': case 'P': - if (s->is_utf16) { + if (s->is_unicode) { if (parse_unicode_property(s, cr, &p, (c == 'P'))) return -1; c = CLASS_RANGE_BASE; @@ -776,14 +696,14 @@ static int get_class_atom(REParseState *s, CharRange *cr, #endif default: p--; - ret = lre_parse_escape(&p, s->is_utf16 * 2); + ret = lre_parse_escape(&p, s->is_unicode * 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) { + } else if (s->is_unicode) { invalid_escape: return re_parse_error(s, "invalid escape sequence in regular expression"); } else { @@ -805,7 +725,7 @@ static int get_class_atom(REParseState *s, CharRange *cr, /* normal char */ if (c >= 128) { c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - if ((unsigned)c > 0xffff && !s->is_utf16) { + if ((unsigned)c > 0xffff && !s->is_unicode) { /* XXX: should handle non BMP-1 code points */ return re_parse_error(s, "malformed unicode char"); } @@ -867,11 +787,13 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp) cr_init(cr, s->opaque, lre_realloc); p = *pp; p++; /* skip '[' */ + invert = FALSE; if (*p == '^') { p++; invert = TRUE; } + for(;;) { if (*p == ']') break; @@ -881,7 +803,7 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp) if (*p == '-' && p[1] != ']') { const uint8_t *p0 = p + 1; if (c1 >= CLASS_RANGE_BASE) { - if (s->is_utf16) { + if (s->is_unicode) { cr_free(cr1); goto invalid_class_range; } @@ -893,7 +815,7 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp) goto fail; if (c2 >= CLASS_RANGE_BASE) { cr_free(cr1); - if (s->is_utf16) { + if (s->is_unicode) { goto invalid_class_range; } /* Annex B: match '-' character */ @@ -922,7 +844,7 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp) } } if (s->ignore_case) { - if (cr_canonicalize(cr)) + if (cr_regexp_canonicalize(cr, s->is_unicode)) goto memory_error; } if (invert) { @@ -943,22 +865,17 @@ static int re_parse_char_class(REParseState *s, const uint8_t **pp) } /* 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) + - true if the opcodes may not advance the char pointer + - false if the opcodes always advance the char pointer */ -static int re_check_advance(const uint8_t *bc_buf, int bc_buf_len) +static BOOL re_need_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]; + int pos, opcode, len; + uint32_t val; + BOOL ret; - ret = -2; /* not known yet */ + ret = TRUE; 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; @@ -976,8 +893,7 @@ static int re_check_advance(const uint8_t *bc_buf, int bc_buf_len) case REOP_dot: case REOP_any: simple_char: - if (ret == -2) - ret = 1; + ret = FALSE; break; case REOP_line_start: case REOP_line_end: @@ -991,41 +907,16 @@ static int re_check_advance(const uint8_t *bc_buf, int bc_buf_len) 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; + /* safe behavior: we cannot predict the outcome */ + return TRUE; } 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; } @@ -1071,11 +962,10 @@ static int re_is_simple_quantifier(const uint8_t *bc_buf, int bc_buf_len) } /* '*pp' is the first char after '<' */ -static int re_parse_group_name(char *buf, int buf_size, - const uint8_t **pp, BOOL is_utf16) +static int re_parse_group_name(char *buf, int buf_size, const uint8_t **pp) { - const uint8_t *p; - uint32_t c; + const uint8_t *p, *p1; + uint32_t c, d; char *q; p = *pp; @@ -1086,11 +976,18 @@ static int re_parse_group_name(char *buf, int buf_size, p++; if (*p != 'u') return -1; - c = lre_parse_escape(&p, is_utf16 * 2); + c = lre_parse_escape(&p, 2); // accept surrogate pairs } else if (c == '>') { break; } else if (c >= 128) { c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); + if (is_hi_surrogate(c)) { + d = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p1); + if (is_lo_surrogate(d)) { + c = from_surrogate(c, d); + p = p1; + } + } } else { p++; } @@ -1140,8 +1037,7 @@ static int re_parse_captures(REParseState *s, int *phas_named_captures, /* potential named capture */ if (capture_name) { p += 3; - if (re_parse_group_name(name, sizeof(name), &p, - s->is_utf16) == 0) { + if (re_parse_group_name(name, sizeof(name), &p) == 0) { if (!strcmp(name, capture_name)) return capture_index; } @@ -1196,9 +1092,10 @@ static int find_group_name(REParseState *s, const char *name) size_t len, name_len; int capture_index; - name_len = strlen(name); p = (char *)s->group_names.buf; + if (!p) return -1; buf_end = (char *)s->group_names.buf + s->group_names.size; + name_len = strlen(name); capture_index = 1; while (p < buf_end) { len = strlen(p); @@ -1243,7 +1140,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) re_emit_op(s, REOP_prev); break; case '{': - if (s->is_utf16) { + if (s->is_unicode) { return re_parse_error(s, "syntax error"); } else if (!is_digit(p[1])) { /* Annex B: we accept '{' not followed by digits as a @@ -1295,7 +1192,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) lookahead: /* Annex B allows lookahead to be used as an atom for the quantifiers */ - if (!s->is_utf16 && !is_backward_lookahead) { + if (!s->is_unicode && !is_backward_lookahead) { last_atom_start = s->byte_code.size; last_capture_count = s->capture_count; } @@ -1314,7 +1211,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) } else if (p[2] == '<') { p += 3; if (re_parse_group_name(s->u.tmp_buf, sizeof(s->u.tmp_buf), - &p, s->is_utf16)) { + &p)) { return re_parse_error(s, "invalid group name"); } if (find_group_name(s, s->u.tmp_buf) > 0) { @@ -1371,15 +1268,15 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) /* 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)) + if (s->is_unicode || 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)) + &p1)) { + if (s->is_unicode || re_has_named_captures(s)) return re_parse_error(s, "invalid group name"); else goto parse_class_atom; @@ -1390,7 +1287,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) 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)) + if (s->is_unicode || re_has_named_captures(s)) return re_parse_error(s, "group name not defined"); else goto parse_class_atom; @@ -1402,7 +1299,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) case '0': p += 2; c = 0; - if (s->is_utf16) { + if (s->is_unicode) { if (is_digit(*p)) { return re_parse_error(s, "invalid decimal escape in regular expression"); } @@ -1424,7 +1321,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) c = parse_digits(&p, FALSE); if (c < 0 || (c >= s->capture_count && c >= re_count_captures(s))) { - if (!s->is_utf16) { + if (!s->is_unicode) { /* Annex B.1.4: accept legacy octal */ p = q; if (*p <= '7') { @@ -1466,13 +1363,13 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) break; case ']': case '}': - if (s->is_utf16) + if (s->is_unicode) 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) + if ((int)c < 0) return -1; normal_char: last_atom_start = s->byte_code.size; @@ -1488,7 +1385,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) return -1; } else { if (s->ignore_case) - c = lre_canonicalize(c, s->is_utf16); + c = lre_canonicalize(c, s->is_unicode); if (c <= 0xffff) re_emit_op_u16(s, REOP_char, c); else @@ -1524,7 +1421,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) /* 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) + if (s->is_unicode) goto invalid_quant_count; break; } @@ -1543,7 +1440,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) quant_max = INT32_MAX; /* infinity */ } } - if (*p != '}' && !s->is_utf16) { + if (*p != '}' && !s->is_unicode) { /* Annex B: normal atom if invalid '{' syntax */ p = p1; break; @@ -1591,8 +1488,12 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) 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); + /* the spec tells that if there is no advance when + running the atom after the first quant_min times, + then there is no match. We remove this test when we + are sure the atom always advances the position. */ + add_zero_advance_check = re_need_check_advance(s->byte_code.buf + last_atom_start, + s->byte_code.size - last_atom_start); } else { add_zero_advance_check = FALSE; } @@ -1612,38 +1513,34 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) } 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) { + } else if (quant_max == 1 || quant_max == INT32_MAX) { + BOOL has_goto = (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); + len + 5 * has_goto + add_zero_advance_check * 2); 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); + re_emit_op(s, REOP_check_advance); } + if (has_goto) + re_emit_goto(s, REOP_goto, last_atom_start); } else { - if (dbuf_insert(&s->byte_code, last_atom_start, 10)) + if (dbuf_insert(&s->byte_code, last_atom_start, 10 + add_zero_advance_check)) 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); + put_u32(s->byte_code.buf + pos, len + 5 + add_zero_advance_check * 2); + pos += 4; + if (add_zero_advance_check) { + s->byte_code.buf[pos++] = REOP_push_char_pos; + re_emit_op(s, REOP_check_advance); + } re_emit_goto(s, REOP_loop, last_atom_start + 5); re_emit_op(s, REOP_drop); } @@ -1667,22 +1564,25 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) 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); + len + 5 + add_zero_advance_check * 2); 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); + re_emit_op(s, REOP_check_advance); + 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); + re_emit_op_u32(s, REOP_split_goto_first + greedy, + len + 5 + add_zero_advance_check * 2); + 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_op(s, REOP_check_advance); re_emit_goto(s, REOP_loop, pos); re_emit_op(s, REOP_drop); } @@ -1796,7 +1696,7 @@ static int compute_stack_size(const uint8_t *bc_buf, int bc_buf_len) } break; case REOP_drop: - case REOP_bne_char_pos: + case REOP_check_advance: assert(stack_size > 0); stack_size--; break; @@ -1832,7 +1732,7 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, 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); + s->is_unicode = ((re_flags & LRE_FLAG_UNICODE) != 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); @@ -1890,7 +1790,8 @@ uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, 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); + put_u32(s->byte_code.buf + RE_HEADER_BYTECODE_LEN, + s->byte_code.size - RE_HEADER_LEN); /* add the named groups if needed */ if (s->group_names.size > (s->capture_count - 1)) { @@ -1921,93 +1822,86 @@ static BOOL is_word_char(uint32_t c) (c == '_')); } -#define GET_CHAR(c, cptr, cbuf_end) \ +#define GET_CHAR(c, cptr, cbuf_end, cbuf_type) \ do { \ if (cbuf_type == 0) { \ - (c) = *(cptr)++; \ + c = *cptr++; \ } else { \ - uint32_t __c1; \ - (c) = *(uint16_t *)(cptr); \ - (cptr) += 2; \ - if ((c) >= 0xd800 && (c) < 0xdc00 && \ - cbuf_type == 2 && (cptr) < (cbuf_end)) { \ - __c1 = *(uint16_t *)(cptr); \ - if (__c1 >= 0xdc00 && __c1 < 0xe000) { \ - (c) = ((((c) & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \ - (cptr) += 2; \ + const uint16_t *_p = (const uint16_t *)cptr; \ + const uint16_t *_end = (const uint16_t *)cbuf_end; \ + c = *_p++; \ + if (is_hi_surrogate(c) && cbuf_type == 2) { \ + if (_p < _end && is_lo_surrogate(*_p)) { \ + c = from_surrogate(c, *_p++); \ } \ } \ + cptr = (const void *)_p; \ } \ } 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; \ +#define PEEK_CHAR(c, cptr, cbuf_end, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + c = cptr[0]; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr; \ + const uint16_t *_end = (const uint16_t *)cbuf_end; \ + c = *_p++; \ + if (is_hi_surrogate(c) && cbuf_type == 2) { \ + if (_p < _end && is_lo_surrogate(*_p)) { \ + c = from_surrogate(c, *_p); \ } \ } \ - } \ + } \ } 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; \ +#define PEEK_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + c = cptr[-1]; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr - 1; \ + const uint16_t *_start = (const uint16_t *)cbuf_start; \ + c = *_p; \ + if (is_lo_surrogate(c) && cbuf_type == 2) { \ + if (_p > _start && is_hi_surrogate(_p[-1])) { \ + c = from_surrogate(*--_p, c); \ } \ } \ } \ } 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; \ +#define GET_PREV_CHAR(c, cptr, cbuf_start, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + cptr--; \ + c = cptr[0]; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr - 1; \ + const uint16_t *_start = (const uint16_t *)cbuf_start; \ + c = *_p; \ + if (is_lo_surrogate(c) && cbuf_type == 2) { \ + if (_p > _start && is_hi_surrogate(_p[-1])) { \ + c = from_surrogate(*--_p, c); \ } \ } \ + cptr = (const void *)_p; \ } \ } 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; \ +#define PREV_CHAR(cptr, cbuf_start, cbuf_type) \ + do { \ + if (cbuf_type == 0) { \ + cptr--; \ + } else { \ + const uint16_t *_p = (const uint16_t *)cptr - 1; \ + const uint16_t *_start = (const uint16_t *)cbuf_start; \ + if (is_lo_surrogate(*_p) && cbuf_type == 2) { \ + if (_p > _start && is_hi_surrogate(_p[-1])) { \ + --_p; \ } \ } \ + cptr = (const void *)_p; \ } \ } while (0) @@ -2038,7 +1932,7 @@ typedef struct { int stack_size_max; BOOL multi_line; BOOL ignore_case; - BOOL is_utf16; + BOOL is_unicode; void *opaque; /* used for stack overflow check */ size_t state_size; @@ -2049,7 +1943,7 @@ typedef struct { static int push_state(REExecContext *s, uint8_t **capture, - const StackInt *stack, size_t stack_len, + StackInt *stack, size_t stack_len, const uint8_t *pc, const uint8_t *cptr, REExecStateEnum type, size_t count) { @@ -2147,7 +2041,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, /* go backward */ char_count = get_u32(pc + 12); for(i = 0; i < char_count; i++) { - PREV_CHAR(cptr, s->cbuf); + PREV_CHAR(cptr, s->cbuf, cbuf_type); } pc = (pc + 16) + (int)get_u32(pc); rs->cptr = cptr; @@ -2182,9 +2076,9 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, test_char: if (cptr >= cbuf_end) goto no_match; - GET_CHAR(c, cptr, cbuf_end); + GET_CHAR(c, cptr, cbuf_end, cbuf_type); if (s->ignore_case) { - c = lre_canonicalize(c, s->is_utf16); + c = lre_canonicalize(c, s->is_unicode); } if (val != c) goto no_match; @@ -2229,7 +2123,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, break; if (!s->multi_line) goto no_match; - PEEK_PREV_CHAR(c, cptr, s->cbuf); + PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type); if (!is_line_terminator(c)) goto no_match; break; @@ -2238,21 +2132,21 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, break; if (!s->multi_line) goto no_match; - PEEK_CHAR(c, cptr, cbuf_end); + PEEK_CHAR(c, cptr, cbuf_end, cbuf_type); 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); + GET_CHAR(c, cptr, cbuf_end, cbuf_type); 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); + GET_CHAR(c, cptr, cbuf_end, cbuf_type); break; case REOP_save_start: case REOP_save_end: @@ -2292,11 +2186,9 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, 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; + case REOP_check_advance: + if (stack[--stack_len] == (uintptr_t)cptr) + goto no_match; break; case REOP_word_boundary: case REOP_not_word_boundary: @@ -2306,14 +2198,14 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, if (cptr == s->cbuf) { v1 = FALSE; } else { - PEEK_PREV_CHAR(c, cptr, s->cbuf); + PEEK_PREV_CHAR(c, cptr, s->cbuf, cbuf_type); v1 = is_word_char(c); } /* current char */ if (cptr >= cbuf_end) { v2 = FALSE; } else { - PEEK_CHAR(c, cptr, cbuf_end); + PEEK_CHAR(c, cptr, cbuf_end, cbuf_type); v2 = is_word_char(c); } if (v1 ^ v2 ^ (REOP_not_word_boundary - opcode)) @@ -2338,11 +2230,11 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, while (cptr1 < cptr1_end) { if (cptr >= cbuf_end) goto no_match; - GET_CHAR(c1, cptr1, cptr1_end); - GET_CHAR(c2, cptr, cbuf_end); + GET_CHAR(c1, cptr1, cptr1_end, cbuf_type); + GET_CHAR(c2, cptr, cbuf_end, cbuf_type); if (s->ignore_case) { - c1 = lre_canonicalize(c1, s->is_utf16); - c2 = lre_canonicalize(c2, s->is_utf16); + c1 = lre_canonicalize(c1, s->is_unicode); + c2 = lre_canonicalize(c2, s->is_unicode); } if (c1 != c2) goto no_match; @@ -2352,11 +2244,11 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, 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); + GET_PREV_CHAR(c1, cptr1, cptr1_start, cbuf_type); + GET_PREV_CHAR(c2, cptr, s->cbuf, cbuf_type); if (s->ignore_case) { - c1 = lre_canonicalize(c1, s->is_utf16); - c2 = lre_canonicalize(c2, s->is_utf16); + c1 = lre_canonicalize(c1, s->is_unicode); + c2 = lre_canonicalize(c2, s->is_unicode); } if (c1 != c2) goto no_match; @@ -2373,9 +2265,9 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, pc += 2; if (cptr >= cbuf_end) goto no_match; - GET_CHAR(c, cptr, cbuf_end); + GET_CHAR(c, cptr, cbuf_end, cbuf_type); if (s->ignore_case) { - c = lre_canonicalize(c, s->is_utf16); + c = lre_canonicalize(c, s->is_unicode); } idx_min = 0; low = get_u16(pc + 0 * 4); @@ -2413,9 +2305,9 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, pc += 2; if (cptr >= cbuf_end) goto no_match; - GET_CHAR(c, cptr, cbuf_end); + GET_CHAR(c, cptr, cbuf_end, cbuf_type); if (s->ignore_case) { - c = lre_canonicalize(c, s->is_utf16); + c = lre_canonicalize(c, s->is_unicode); } idx_min = 0; low = get_u32(pc + 0 * 8); @@ -2445,7 +2337,7 @@ static intptr_t lre_exec_backtrack(REExecContext *s, uint8_t **capture, /* go to the previous char */ if (cptr == s->cbuf) goto no_match; - PREV_CHAR(cptr, s->cbuf); + PREV_CHAR(cptr, s->cbuf, cbuf_type); break; case REOP_simple_greedy_quant: { @@ -2504,16 +2396,16 @@ int lre_exec(uint8_t **capture, int re_flags, i, alloca_size, ret; StackInt *stack_buf; - re_flags = bc_buf[RE_HEADER_FLAGS]; + re_flags = lre_get_flags(bc_buf); 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->is_unicode = (re_flags & LRE_FLAG_UNICODE) != 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) + if (s->cbuf_type == 1 && s->is_unicode) s->cbuf_type = 2; s->opaque = opaque; @@ -2551,8 +2443,8 @@ 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); + re_bytecode_len = get_u32(bc_buf + RE_HEADER_BYTECODE_LEN); + return (const char *)(bc_buf + RE_HEADER_LEN + re_bytecode_len); } #ifdef TEST @@ -2569,25 +2461,26 @@ void *lre_realloc(void *opaque, void *ptr, size_t size) int main(int argc, char **argv) { - int len, ret, i; + int len, flags, 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); + if (argc < 4) { + printf("usage: %s regexp flags input\n", argv[0]); + return 1; } + flags = atoi(argv[2]); bc = lre_compile(&len, error_msg, sizeof(error_msg), argv[1], - strlen(argv[1]), 0, NULL); + strlen(argv[1]), flags, NULL); if (!bc) { fprintf(stderr, "error: %s\n", error_msg); exit(1); } - input = argv[2]; + input = argv[3]; input_len = strlen(input); ret = lre_exec(capture, bc, (uint8_t *)input, 0, input_len, 0, NULL); diff --git a/src/shared/quickjs/libregexp.h b/src/shared/quickjs/libregexp.h index 9aedb7e93..7af7ece0f 100644 --- a/src/shared/quickjs/libregexp.h +++ b/src/shared/quickjs/libregexp.h @@ -1,6 +1,6 @@ /* * Regular Expression Engine - * + * * Copyright (c) 2017-2018 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -25,18 +25,15 @@ #define LIBREGEXP_H #include <stddef.h> - -#include "libunicode.h" - -#define LRE_BOOL int /* for documentation purposes */ +#include <stdint.h> #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_UNICODE (1 << 4) #define LRE_FLAG_STICKY (1 << 5) - +#define LRE_FLAG_INDICES (1 << 6) /* Unused by libregexp, just recorded. */ #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, @@ -50,43 +47,9 @@ int lre_exec(uint8_t **capture, 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); +/* must be provided by the user, return non zero if overflow */ +int 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 index 1727525fb..72d495e78 100644 --- a/src/shared/quickjs/libunicode-table.h +++ b/src/shared/quickjs/libunicode-table.h @@ -160,40 +160,45 @@ static const uint16_t case_conv_ext[58] = { 0x006b, 0x00e5, }; -static const uint8_t unicode_prop_Cased1_table[188] = { +static const uint8_t unicode_prop_Cased1_table[196] = { 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, + 0x80, 0x41, 0x62, 0x80, 0xa6, 0x80, 0x4b, 0x72, + 0x80, 0x4c, 0x02, 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, 0x9b, 0x12, 0x82, 0x43, 0x34, 0xa2, + 0x06, 0x80, 0x8d, 0x60, 0x5c, 0x15, 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, 0x80, 0x9e, 0x80, 0x98, 0x07, - 0x47, 0x33, 0x89, 0x80, 0x93, 0x52, 0x10, 0x99, + 0x9e, 0x80, 0x98, 0x07, 0x47, 0x33, 0x89, 0x80, + 0x93, 0x2d, 0x41, 0x04, 0xbd, 0x50, 0xc1, 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_Cased1_index[21] = { + 0xb9, 0x02, 0xe0, // 002B9 at 39 + 0xc0, 0x1d, 0x20, // 01DC0 at 65 + 0xe5, 0x2c, 0x20, // 02CE5 at 97 + 0xb1, 0x07, 0x21, // 107B1 at 129 + 0xc1, 0xd6, 0x21, // 1D6C1 at 161 + 0x4a, 0xf1, 0x01, // 1F14A at 192 + 0x8a, 0xf1, 0x01, // 1F18A at 224 (upper bound) }; -static const uint8_t unicode_prop_Case_Ignorable_table[720] = { +static const uint8_t unicode_prop_Case_Ignorable_table[737] = { 0xa6, 0x05, 0x80, 0x8a, 0x80, 0xa2, 0x00, 0x80, 0xc6, 0x03, 0x00, 0x03, 0x01, 0x81, 0x41, 0xf6, 0x40, 0xbf, 0x19, 0x18, 0x88, 0x08, 0x80, 0x40, @@ -215,7 +220,7 @@ static const uint8_t unicode_prop_Case_Ignorable_table[720] = { 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, + 0x88, 0xe1, 0x01, 0x88, 0x88, 0x00, 0x86, 0xc8, 0x81, 0x9a, 0x00, 0x00, 0x80, 0xb6, 0x8d, 0x04, 0x01, 0x84, 0x8a, 0x80, 0xa3, 0x88, 0x80, 0xe5, 0x18, 0x28, 0x09, 0x81, 0x98, 0x0b, 0x82, 0x8f, @@ -256,49 +261,66 @@ static const uint8_t unicode_prop_Case_Ignorable_table[720] = { 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, + 0x41, 0x82, 0x81, 0xcf, 0x82, 0xc5, 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, 0x01, 0x80, 0x40, 0x9c, 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, 0x8a, 0x81, 0xb3, 0x24, 0x00, 0x80, + 0x54, 0xec, 0x90, 0x85, 0x8e, 0x60, 0x36, 0x99, + 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, 0x84, 0xbd, 0xa0, 0x80, 0x40, 0x9f, 0x8d, + 0x41, 0x6f, 0x80, 0xbc, 0x83, 0x41, 0xfa, 0x84, + 0x43, 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, + 0xbe, 0x05, 0x00, // 005BE at 32 + 0xfe, 0x07, 0x00, // 007FE at 64 + 0x52, 0x0a, 0xa0, // 00A52 at 101 + 0xc1, 0x0b, 0x00, // 00BC1 at 128 + 0x82, 0x0d, 0x00, // 00D82 at 160 + 0x3f, 0x10, 0x80, // 0103F at 196 + 0xd4, 0x17, 0x40, // 017D4 at 226 + 0xcf, 0x1a, 0x20, // 01ACF at 257 + 0xf5, 0x1c, 0x00, // 01CF5 at 288 + 0x80, 0x20, 0x00, // 02080 at 320 + 0x16, 0xa0, 0x00, // 0A016 at 352 + 0xc6, 0xa8, 0x00, // 0A8C6 at 384 + 0xc2, 0xaa, 0x60, // 0AAC2 at 419 + 0x56, 0xfe, 0x20, // 0FE56 at 449 + 0xb1, 0x07, 0x01, // 107B1 at 480 + 0x75, 0x10, 0x01, // 11075 at 512 + 0xeb, 0x12, 0x21, // 112EB at 545 + 0x41, 0x16, 0x01, // 11641 at 576 + 0x5c, 0x1a, 0x01, // 11A5C at 608 + 0x43, 0x1f, 0x01, // 11F43 at 640 + 0x2e, 0xcf, 0x41, // 1CF2E at 674 + 0x25, 0xe0, 0x01, // 1E025 at 704 + 0xf0, 0x01, 0x0e, // E01F0 at 736 (upper bound) }; -static const uint8_t unicode_prop_ID_Start_table[1079] = { +static const uint8_t unicode_prop_ID_Start_table[1100] = { 0xc0, 0x99, 0x85, 0x99, 0xae, 0x80, 0x89, 0x03, 0x04, 0x96, 0x80, 0x9e, 0x80, 0x41, 0xc9, 0x83, 0x8b, 0x8d, 0x26, 0x00, 0x80, 0x40, 0x80, 0x20, @@ -392,67 +414,92 @@ static const uint8_t unicode_prop_ID_Start_table[1079] = { 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, + 0x94, 0x00, 0x80, 0xa2, 0x91, 0x80, 0x98, 0x92, + 0x81, 0xbe, 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, 0x8e, 0x00, 0x8c, 0x80, + 0xa1, 0xfb, 0x80, 0xce, 0x43, 0x99, 0xe5, 0xee, + 0x90, 0x40, 0xc3, 0x4a, 0x4b, 0xe0, 0x8e, 0x44, + 0x2f, 0x90, 0x85, 0x4f, 0xb8, 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, + 0x18, 0x30, 0x08, 0x41, 0x22, 0x8e, 0x80, 0x9c, + 0x11, 0x80, 0x8d, 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, 0x2d, 0x41, 0x04, 0xbd, 0x40, 0x91, + 0xac, 0x89, 0x86, 0x8f, 0x80, 0x41, 0x40, 0x9d, + 0x91, 0xab, 0x41, 0xe3, 0x9b, 0x42, 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, 0x39, + 0x85, 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d, 0x5d, + 0x30, 0x4c, 0x1e, 0x42, 0x1d, 0x45, 0xe1, 0x53, + 0x4a, 0x84, 0x50, 0x5f, }; -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_Start_index[105] = { + 0xf6, 0x03, 0x20, // 003F6 at 33 + 0xa6, 0x07, 0x00, // 007A6 at 64 + 0xa9, 0x09, 0x20, // 009A9 at 97 + 0xb1, 0x0a, 0x00, // 00AB1 at 128 + 0xba, 0x0b, 0x20, // 00BBA at 161 + 0x3b, 0x0d, 0x20, // 00D3B at 193 + 0xc7, 0x0e, 0x20, // 00EC7 at 225 + 0x49, 0x12, 0x00, // 01249 at 256 + 0x9b, 0x16, 0x00, // 0169B at 288 + 0xac, 0x19, 0x00, // 019AC at 320 + 0xc0, 0x1d, 0x80, // 01DC0 at 356 + 0x80, 0x20, 0x20, // 02080 at 385 + 0x70, 0x2d, 0x00, // 02D70 at 416 + 0x00, 0x32, 0x00, // 03200 at 448 + 0xda, 0xa7, 0x00, // 0A7DA at 480 + 0x4c, 0xaa, 0x20, // 0AA4C at 513 + 0xc7, 0xd7, 0x20, // 0D7C7 at 545 + 0xfc, 0xfd, 0x20, // 0FDFC at 577 + 0x9d, 0x02, 0x21, // 1029D at 609 + 0x96, 0x05, 0x01, // 10596 at 640 + 0xf3, 0x08, 0x01, // 108F3 at 672 + 0xb3, 0x0c, 0x21, // 10CB3 at 705 + 0x73, 0x11, 0x61, // 11173 at 739 + 0x34, 0x13, 0x01, // 11334 at 768 + 0x1b, 0x17, 0x21, // 1171B at 801 + 0x8a, 0x1a, 0x01, // 11A8A at 832 + 0x34, 0x1f, 0x21, // 11F34 at 865 + 0xbf, 0x6a, 0x01, // 16ABF at 896 + 0x23, 0xb1, 0xa1, // 1B123 at 933 + 0xad, 0xd4, 0x01, // 1D4AD at 960 + 0x6f, 0xd7, 0x01, // 1D76F at 992 + 0xff, 0xe7, 0x61, // 1E7FF at 1027 + 0x5e, 0xee, 0x01, // 1EE5E at 1056 + 0xe1, 0xeb, 0x22, // 2EBE1 at 1089 + 0xb0, 0x23, 0x03, // 323B0 at 1120 (upper bound) }; -static const uint8_t unicode_prop_ID_Continue1_table[640] = { +static const uint8_t unicode_prop_ID_Continue1_table[660] = { 0xaf, 0x89, 0xa4, 0x80, 0xd6, 0x80, 0x42, 0x47, 0xef, 0x96, 0x80, 0x40, 0xfa, 0x84, 0x41, 0x08, 0xac, 0x00, 0x01, 0x01, 0x00, 0xc7, 0x8a, 0xaf, @@ -470,85 +517,101 @@ static const uint8_t unicode_prop_ID_Continue1_table[640] = { 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, + 0x10, 0x1e, 0x81, 0x8a, 0x09, 0x89, 0x10, 0x8b, + 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, 0x30, 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, 0xcf, 0x82, 0xc5, 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, 0x08, 0x40, 0x9c, 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, 0x88, 0x08, 0x80, + 0xaf, 0x32, 0x84, 0x8c, 0x89, 0x54, 0xe5, 0x05, + 0x8e, 0x60, 0x36, 0x09, 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, 0xe3, + 0x80, 0x40, 0x9f, 0x86, 0x88, 0x89, 0x41, 0x63, + 0x80, 0xbc, 0x8d, 0x41, 0xf1, 0x8d, 0x43, 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, +static const uint8_t unicode_prop_ID_Continue1_index[63] = { + 0xfa, 0x06, 0x00, // 006FA at 32 + 0x70, 0x09, 0x00, // 00970 at 64 + 0xf0, 0x0a, 0x40, // 00AF0 at 98 + 0x57, 0x0c, 0x00, // 00C57 at 128 + 0xf0, 0x0d, 0x60, // 00DF0 at 163 + 0xc7, 0x0f, 0x20, // 00FC7 at 193 + 0xea, 0x17, 0x40, // 017EA at 226 + 0x05, 0x1b, 0x00, // 01B05 at 256 + 0x41, 0x20, 0x00, // 02041 at 288 + 0x0c, 0xa8, 0x80, // 0A80C at 324 + 0x37, 0xaa, 0x20, // 0AA37 at 353 + 0x50, 0xfe, 0x20, // 0FE50 at 385 + 0x3a, 0x0d, 0x21, // 10D3A at 417 + 0x74, 0x11, 0x01, // 11174 at 448 + 0x5a, 0x14, 0x21, // 1145A at 481 + 0x44, 0x19, 0x81, // 11944 at 516 + 0x5a, 0x1d, 0xa1, // 11D5A at 549 + 0xf5, 0x6a, 0x21, // 16AF5 at 577 + 0x45, 0xd2, 0x41, // 1D245 at 610 + 0xaf, 0xe2, 0x21, // 1E2AF at 641 + 0xf0, 0x01, 0x0e, // E01F0 at 672 (upper bound) }; #ifdef CONFIG_ALL_UNICODE -static const uint8_t unicode_cc_table[881] = { +static const uint8_t unicode_cc_table[899] = { 0xb2, 0xcf, 0xd4, 0x00, 0xe8, 0x03, 0xdc, 0x00, 0xe8, 0x00, 0xd8, 0x04, 0xdc, 0x01, 0xca, 0x03, 0xdc, 0x01, 0xca, 0x0a, 0xdc, 0x04, 0x01, 0x03, @@ -631,52 +694,72 @@ static const uint8_t unicode_cc_table[881] = { 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, + 0x52, 0xc1, 0xb0, 0x1f, 0x02, 0xdc, 0xb0, 0x15, + 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, 0xb1, 0x78, + 0x01, 0x09, 0xb8, 0x43, 0x7c, 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, + 0x33, 0xc0, 0xb0, 0x6f, 0xc6, 0xb1, 0x46, 0xc0, + 0xb0, 0x0c, 0xc3, 0xb1, 0xcb, 0x01, 0xe8, 0x00, + 0xdc, 0xc0, 0xb3, 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 uint8_t unicode_cc_index[87] = { + 0x4d, 0x03, 0x00, // 0034D at 32 + 0x97, 0x05, 0x20, // 00597 at 65 + 0xc6, 0x05, 0x00, // 005C6 at 96 + 0xe7, 0x06, 0x00, // 006E7 at 128 + 0x45, 0x07, 0x00, // 00745 at 160 + 0x9c, 0x08, 0x00, // 0089C at 192 + 0x4d, 0x09, 0x00, // 0094D at 224 + 0x3c, 0x0b, 0x00, // 00B3C at 256 + 0x3d, 0x0d, 0x00, // 00D3D at 288 + 0x36, 0x0f, 0x00, // 00F36 at 320 + 0x38, 0x10, 0x20, // 01038 at 353 + 0x3a, 0x19, 0x00, // 0193A at 384 + 0xcb, 0x1a, 0x20, // 01ACB at 417 + 0xd3, 0x1c, 0x00, // 01CD3 at 448 + 0xcf, 0x1d, 0x00, // 01DCF at 480 + 0xe2, 0x20, 0x00, // 020E2 at 512 + 0x2e, 0x30, 0x20, // 0302E at 545 + 0x2b, 0xa9, 0x20, // 0A92B at 577 + 0xed, 0xab, 0x00, // 0ABED at 608 + 0x39, 0x0a, 0x01, // 10A39 at 640 + 0x51, 0x0f, 0x01, // 10F51 at 672 + 0x73, 0x11, 0x01, // 11173 at 704 + 0x75, 0x13, 0x01, // 11375 at 736 + 0x2b, 0x17, 0x21, // 1172B at 769 + 0x3f, 0x1c, 0x21, // 11C3F at 801 + 0x9e, 0xbc, 0x21, // 1BC9E at 833 + 0x08, 0xe0, 0x01, // 1E008 at 864 + 0x44, 0xe9, 0x01, // 1E944 at 896 + 0x4b, 0xe9, 0x01, // 1E94B at 928 (upper bound) }; -static const uint32_t unicode_decomp_table1[693] = { +static const uint32_t unicode_decomp_table1[699] = { 0x00280081, 0x002a0097, 0x002a8081, 0x002bc097, 0x002c8115, 0x002d0097, 0x002d4081, 0x002e0097, 0x002e4115, 0x002f0199, 0x00302016, 0x00400842, @@ -840,20 +923,21 @@ static const uint32_t unicode_decomp_table1[693] = { 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, + 0x75fb051f, 0x75fd851f, 0x780c049f, 0x780e419f, + 0x780f059f, 0x7811c203, 0x7812d0ad, 0x781b0103, + 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] = { +static const uint16_t unicode_decomp_table2[699] = { 0x0020, 0x0000, 0x0061, 0x0002, 0x0004, 0x0006, 0x03bc, 0x0008, 0x000a, 0x000c, 0x0015, 0x0095, 0x00a5, 0x00b9, 0x00c1, 0x00c3, 0x00c7, 0x00cb, 0x00d1, 0x00d7, 0x00dd, 0x00e0, 0x00e6, 0x00f8, @@ -935,15 +1019,16 @@ static const uint16_t unicode_decomp_table2[693] = { 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, + 0x1df4, 0x1dfe, 0x1e00, 0x1e02, 0x1e04, 0x1e06, 0x1e08, 0x1e0a, + 0x1e0c, 0x1e0e, 0x1e16, 0x1e39, 0x1e3d, 0x1e43, 0x1e60, 0x062d, + 0x1e68, 0x1e74, 0x062c, 0x1e84, 0x1ef4, 0x1f00, 0x1f13, 0x1f25, + 0x1f38, 0x1f3a, 0x1f3e, 0x1f44, 0x1f4a, 0x1f4c, 0x1f50, 0x1f52, + 0x1f5a, 0x1f5d, 0x1f5f, 0x1f65, 0x1f67, 0x30b5, 0x1f6d, 0x1fc5, + 0x1fdb, 0x1fdf, 0x1fe1, 0x1fe6, 0x2033, 0x2044, 0x2145, 0x2155, + 0x215b, 0x2255, 0x2373, }; -static const uint8_t unicode_decomp_data[9292] = { +static const uint8_t unicode_decomp_data[9345] = { 0x20, 0x88, 0x20, 0x84, 0x32, 0x33, 0x20, 0x81, 0x20, 0xa7, 0x31, 0x6f, 0x31, 0xd0, 0x34, 0x31, 0xd0, 0x32, 0x33, 0xd0, 0x34, 0x41, 0x80, 0x41, @@ -1905,207 +1990,214 @@ static const uint8_t unicode_decomp_data[9292] = { 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, + 0x30, 0x04, 0x3a, 0x04, 0x3e, 0x04, 0x4b, 0x04, + 0x4d, 0x04, 0x4e, 0x04, 0x89, 0xa6, 0x30, 0x04, + 0xa9, 0x26, 0x28, 0xb9, 0x7f, 0x9f, 0x00, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0a, + 0x0b, 0x0e, 0x0f, 0x11, 0x13, 0x14, 0x15, 0x16, + 0x17, 0x18, 0x1a, 0x1b, 0x61, 0x26, 0x25, 0x2f, + 0x7b, 0x51, 0xa6, 0xb1, 0x04, 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, 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, + 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] = { @@ -2313,7 +2405,7 @@ static const char unicode_gc_name_table[] = "C,Other" "\0" ; -static const uint8_t unicode_gc_table[3897] = { +static const uint8_t unicode_gc_table[3948] = { 0xfa, 0x18, 0x17, 0x56, 0x0d, 0x56, 0x12, 0x13, 0x16, 0x0c, 0x16, 0x11, 0x36, 0xe9, 0x02, 0x36, 0x4c, 0x36, 0xe1, 0x12, 0x12, 0x16, 0x13, 0x0e, @@ -2399,409 +2491,415 @@ static const uint8_t unicode_gc_table[3897] = { 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, + 0x25, 0x07, 0xe0, 0x04, 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, 0xc6, + 0x00, 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, - 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, + 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, 0x43, + 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, 0x25, 0x06, 0xe0, + 0x36, 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, + 0xc0, 0xf6, 0x02, 0xe0, 0x80, 0x6e, 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, 0xc0, + 0x26, 0x05, 0x07, 0xe5, 0x05, 0x00, 0xe5, 0x1a, + 0x27, 0x86, 0x40, 0x27, 0x06, 0x07, 0x06, 0xf6, + 0x05, 0xe9, 0x02, 0xe0, 0x4e, 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, 0xa8, 0xfb, 0x08, 0x06, 0xa5, 0xe6, + 0x07, 0xe0, 0x8f, 0x22, 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, 0x07, 0x05, 0xe0, 0x15, 0x45, 0x20, 0x05, + 0xe0, 0x06, 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, 0x72, 0xeb, 0x0c, 0xe0, 0x04, 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, 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, + 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, 0xa0, 0xa2, 0xe0, + 0x80, 0x4d, 0xc6, 0x00, 0xe6, 0x09, 0x20, 0xc6, + 0x00, 0x26, 0x00, 0x86, 0x80, 0xe4, 0x36, 0xe0, + 0x19, 0x06, 0xe0, 0x68, 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, 0x81, + 0x48, 0xe5, 0x13, 0x04, 0x66, 0xe9, 0x02, 0xe0, + 0x82, 0x5e, 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, 0x60, 0xef, 0x09, 0x40, 0xef, 0x05, 0x40, + 0xef, 0x6f, 0x60, 0xef, 0x57, 0xa0, 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, + 0xef, 0x05, 0x40, 0xef, 0x01, 0xc0, 0xef, 0x26, + 0x00, 0xcf, 0xe0, 0x00, 0xef, 0x06, 0x60, 0xef, + 0x01, 0xc0, 0xef, 0x01, 0xc0, 0xef, 0x80, 0x0b, + 0x00, 0xef, 0x2f, 0xe0, 0x1d, 0xe9, 0x02, 0xe0, + 0x83, 0x7e, 0xe5, 0xc0, 0x66, 0x58, 0xe0, 0x18, + 0xe5, 0x8f, 0xb2, 0xa0, 0xe5, 0x80, 0x56, 0x20, + 0xe5, 0x95, 0xfa, 0xe0, 0x06, 0xe5, 0x9c, 0xa9, + 0xe0, 0x8b, 0x97, 0xe5, 0x81, 0x96, 0xe0, 0x85, + 0x5a, 0xe5, 0x92, 0xc3, 0x80, 0xe5, 0x8f, 0xd8, + 0xe0, 0xca, 0x9b, 0xc9, 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 { @@ -2868,6 +2966,7 @@ typedef enum { UNICODE_SCRIPT_Kaithi, UNICODE_SCRIPT_Kannada, UNICODE_SCRIPT_Katakana, + UNICODE_SCRIPT_Kawi, UNICODE_SCRIPT_Kayah_Li, UNICODE_SCRIPT_Kharoshthi, UNICODE_SCRIPT_Khmer, @@ -2902,6 +3001,7 @@ typedef enum { UNICODE_SCRIPT_Multani, UNICODE_SCRIPT_Myanmar, UNICODE_SCRIPT_Nabataean, + UNICODE_SCRIPT_Nag_Mundari, UNICODE_SCRIPT_Nandinagari, UNICODE_SCRIPT_New_Tai_Lue, UNICODE_SCRIPT_Newa, @@ -3033,6 +3133,7 @@ static const char unicode_script_name_table[] = "Kaithi,Kthi" "\0" "Kannada,Knda" "\0" "Katakana,Kana" "\0" + "Kawi,Kawi" "\0" "Kayah_Li,Kali" "\0" "Kharoshthi,Khar" "\0" "Khmer,Khmr" "\0" @@ -3067,6 +3168,7 @@ static const char unicode_script_name_table[] = "Multani,Mult" "\0" "Myanmar,Mymr" "\0" "Nabataean,Nbat" "\0" + "Nag_Mundari,Nagm" "\0" "Nandinagari,Nand" "\0" "New_Tai_Lue,Talu" "\0" "Newa,Newa" "\0" @@ -3134,12 +3236,12 @@ static const char unicode_script_name_table[] = "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, +static const uint8_t unicode_script_table[2720] = { + 0xc0, 0x19, 0x99, 0x47, 0x85, 0x19, 0x99, 0x47, + 0xae, 0x19, 0x80, 0x47, 0x8e, 0x19, 0x80, 0x47, + 0x84, 0x19, 0x96, 0x47, 0x80, 0x19, 0x9e, 0x47, + 0x80, 0x19, 0xe1, 0x60, 0x47, 0xa6, 0x19, 0x84, + 0x47, 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, @@ -3152,11 +3254,11 @@ static const uint8_t unicode_script_table[2690] = { 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, + 0x04, 0x80, 0x19, 0xa1, 0x04, 0x8d, 0x8b, 0x00, + 0xbb, 0x8b, 0x01, 0x82, 0x8b, 0xaf, 0x04, 0xb1, + 0x95, 0x0d, 0xba, 0x66, 0x01, 0x82, 0x66, 0xad, + 0x7f, 0x01, 0x8e, 0x7f, 0x00, 0x9b, 0x52, 0x01, + 0x80, 0x52, 0x00, 0x8a, 0x8b, 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, @@ -3176,43 +3278,43 @@ static const uint8_t unicode_script_table[2690] = { 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, + 0x82, 0x74, 0x00, 0x87, 0x74, 0x01, 0x81, 0x74, + 0x01, 0x95, 0x74, 0x00, 0x86, 0x74, 0x00, 0x81, + 0x74, 0x00, 0x84, 0x74, 0x01, 0x88, 0x74, 0x01, + 0x81, 0x74, 0x01, 0x82, 0x74, 0x06, 0x82, 0x74, + 0x03, 0x81, 0x74, 0x00, 0x84, 0x74, 0x01, 0x91, + 0x74, 0x09, 0x81, 0x92, 0x00, 0x85, 0x92, 0x02, + 0x82, 0x92, 0x00, 0x83, 0x92, 0x02, 0x81, 0x92, + 0x00, 0x80, 0x92, 0x00, 0x81, 0x92, 0x02, 0x81, + 0x92, 0x02, 0x82, 0x92, 0x02, 0x8b, 0x92, 0x03, + 0x84, 0x92, 0x02, 0x82, 0x92, 0x00, 0x83, 0x92, + 0x01, 0x80, 0x92, 0x05, 0x80, 0x92, 0x0d, 0x94, + 0x92, 0x04, 0x8c, 0x94, 0x00, 0x82, 0x94, 0x00, + 0x96, 0x94, 0x00, 0x8f, 0x94, 0x01, 0x88, 0x94, + 0x00, 0x82, 0x94, 0x00, 0x83, 0x94, 0x06, 0x81, + 0x94, 0x00, 0x82, 0x94, 0x01, 0x80, 0x94, 0x01, + 0x83, 0x94, 0x01, 0x89, 0x94, 0x06, 0x88, 0x94, 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, + 0x01, 0x89, 0x3d, 0x00, 0x82, 0x3d, 0x0b, 0x8c, + 0x51, 0x00, 0x82, 0x51, 0x00, 0xb2, 0x51, 0x00, + 0x82, 0x51, 0x00, 0x85, 0x51, 0x03, 0x8f, 0x51, + 0x01, 0x99, 0x51, 0x00, 0x82, 0x85, 0x00, 0x91, + 0x85, 0x02, 0x97, 0x85, 0x00, 0x88, 0x85, 0x00, + 0x80, 0x85, 0x01, 0x86, 0x85, 0x02, 0x80, 0x85, + 0x03, 0x85, 0x85, 0x00, 0x80, 0x85, 0x00, 0x87, + 0x85, 0x05, 0x89, 0x85, 0x01, 0x82, 0x85, 0x0b, + 0xb9, 0x96, 0x03, 0x80, 0x19, 0x9b, 0x96, 0x24, + 0x81, 0x46, 0x00, 0x80, 0x46, 0x00, 0x84, 0x46, + 0x00, 0x97, 0x46, 0x00, 0x80, 0x46, 0x00, 0x96, + 0x46, 0x01, 0x84, 0x46, 0x00, 0x80, 0x46, 0x00, + 0x86, 0x46, 0x00, 0x89, 0x46, 0x01, 0x83, 0x46, + 0x1f, 0xc7, 0x97, 0x00, 0xa3, 0x97, 0x03, 0xa6, + 0x97, 0x00, 0xa3, 0x97, 0x00, 0x8e, 0x97, 0x00, + 0x86, 0x97, 0x83, 0x19, 0x81, 0x97, 0x24, 0xe0, + 0x3f, 0x60, 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, @@ -3222,32 +3324,32 @@ static const uint8_t unicode_script_table[2690] = { 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, + 0xe2, 0x1f, 0x12, 0x9c, 0x69, 0x02, 0xca, 0x7e, + 0x82, 0x19, 0x8a, 0x7e, 0x06, 0x95, 0x8c, 0x08, + 0x80, 0x8c, 0x94, 0x33, 0x81, 0x19, 0x08, 0x93, + 0x11, 0x0b, 0x8c, 0x8d, 0x00, 0x82, 0x8d, 0x00, + 0x81, 0x8d, 0x0b, 0xdd, 0x42, 0x01, 0x89, 0x42, + 0x05, 0x89, 0x42, 0x05, 0x81, 0x5d, 0x81, 0x19, + 0x80, 0x5d, 0x80, 0x19, 0x93, 0x5d, 0x05, 0xd8, + 0x5d, 0x06, 0xaa, 0x5d, 0x04, 0xc5, 0x12, 0x09, + 0x9e, 0x49, 0x00, 0x8b, 0x49, 0x03, 0x8b, 0x49, + 0x03, 0x80, 0x49, 0x02, 0x8b, 0x49, 0x9d, 0x8e, + 0x01, 0x84, 0x8e, 0x0a, 0xab, 0x64, 0x03, 0x99, + 0x64, 0x05, 0x8a, 0x64, 0x02, 0x81, 0x64, 0x9f, + 0x42, 0x9b, 0x10, 0x01, 0x81, 0x10, 0xbe, 0x8f, + 0x00, 0x9c, 0x8f, 0x01, 0x8a, 0x8f, 0x05, 0x89, + 0x8f, 0x05, 0x8d, 0x8f, 0x01, 0x9e, 0x38, 0x30, + 0xcc, 0x07, 0x02, 0xae, 0x07, 0x00, 0xbf, 0x89, + 0xb3, 0x0a, 0x07, 0x83, 0x0a, 0xb7, 0x48, 0x02, + 0x8e, 0x48, 0x02, 0x82, 0x48, 0xaf, 0x6a, 0x88, 0x1d, 0x06, 0xaa, 0x28, 0x01, 0x82, 0x28, 0x87, - 0x87, 0x07, 0x82, 0x38, 0x80, 0x19, 0x8c, 0x38, + 0x89, 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, + 0x80, 0x19, 0x04, 0xa5, 0x47, 0x84, 0x2c, 0x80, + 0x1d, 0xb0, 0x47, 0x84, 0x2c, 0x83, 0x47, 0x84, + 0x2c, 0x8c, 0x47, 0x80, 0x1d, 0xc5, 0x47, 0x80, + 0x2c, 0xbf, 0x38, 0xe0, 0x9f, 0x47, 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, @@ -3255,18 +3357,18 @@ static const uint8_t unicode_script_table[2690] = { 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, + 0x47, 0x01, 0x8a, 0x19, 0x80, 0x47, 0x8e, 0x19, + 0x00, 0x8c, 0x47, 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, + 0x81, 0x47, 0x85, 0x19, 0x80, 0x47, 0x9a, 0x19, + 0x80, 0x47, 0x90, 0x19, 0xa8, 0x47, 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, + 0xdf, 0x29, 0x9f, 0x47, 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, + 0x80, 0x28, 0x01, 0xb7, 0x98, 0x06, 0x81, 0x98, + 0x0d, 0x80, 0x98, 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, @@ -3282,27 +3384,27 @@ static const uint8_t unicode_script_table[2690] = { 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, + 0xf0, 0x41, 0x9f, 0x30, 0xe4, 0x2c, 0xa2, 0x02, + 0xb6, 0xa2, 0x08, 0xaf, 0x4c, 0xe0, 0xcb, 0x9d, 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, + 0xe0, 0x05, 0x47, 0x82, 0x19, 0xbf, 0x47, 0x04, + 0x81, 0x47, 0x00, 0x80, 0x47, 0x00, 0x84, 0x47, + 0x17, 0x8d, 0x47, 0xac, 0x8a, 0x02, 0x89, 0x19, + 0x05, 0xb7, 0x7a, 0x07, 0xc5, 0x80, 0x07, 0x8b, + 0x80, 0x05, 0x9f, 0x20, 0xad, 0x40, 0x80, 0x19, + 0x80, 0x40, 0xa3, 0x7d, 0x0a, 0x80, 0x7d, 0x9c, 0x31, 0x02, 0xcd, 0x3b, 0x00, 0x80, 0x19, 0x89, - 0x3b, 0x03, 0x81, 0x3b, 0x9e, 0x5f, 0x00, 0xb6, + 0x3b, 0x03, 0x81, 0x3b, 0x9e, 0x60, 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, + 0x83, 0x16, 0x9f, 0x60, 0xc2, 0x90, 0x17, 0x84, + 0x90, 0x96, 0x57, 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, + 0x86, 0x27, 0x00, 0xaa, 0x47, 0x80, 0x19, 0x88, + 0x47, 0x80, 0x2c, 0x83, 0x47, 0x81, 0x19, 0x03, + 0xcf, 0x17, 0xad, 0x57, 0x01, 0x89, 0x57, 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, + 0x01, 0xe0, 0x09, 0x30, 0x25, 0x86, 0x47, 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, @@ -3311,130 +3413,134 @@ static const uint8_t unicode_script_table[2690] = { 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, + 0x01, 0x80, 0x19, 0x00, 0x9f, 0x19, 0x99, 0x47, + 0x85, 0x19, 0x99, 0x47, 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, + 0x86, 0x19, 0x09, 0x84, 0x19, 0x01, 0x8b, 0x4b, + 0x00, 0x99, 0x4b, 0x00, 0x92, 0x4b, 0x00, 0x81, + 0x4b, 0x00, 0x8e, 0x4b, 0x01, 0x8d, 0x4b, 0x21, + 0xe0, 0x1a, 0x4b, 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, + 0x38, 0x60, 0x21, 0x9c, 0x4d, 0x02, 0xb0, 0x13, + 0x0e, 0x80, 0x38, 0x9a, 0x19, 0x03, 0xa3, 0x6c, + 0x08, 0x82, 0x6c, 0x9a, 0x2a, 0x04, 0xaa, 0x6e, + 0x04, 0x9d, 0x9c, 0x00, 0x80, 0x9c, 0xa3, 0x6f, + 0x03, 0x8d, 0x6f, 0x29, 0xcf, 0x1f, 0xaf, 0x82, + 0x9d, 0x76, 0x01, 0x89, 0x76, 0x05, 0xa3, 0x75, + 0x03, 0xa3, 0x75, 0x03, 0xa7, 0x25, 0x07, 0xb3, + 0x14, 0x0a, 0x80, 0x14, 0x8a, 0x9e, 0x00, 0x8e, + 0x9e, 0x00, 0x86, 0x9e, 0x00, 0x81, 0x9e, 0x00, + 0x8a, 0x9e, 0x00, 0x8e, 0x9e, 0x00, 0x86, 0x9e, + 0x00, 0x81, 0x9e, 0x42, 0xe0, 0xd6, 0x4a, 0x08, + 0x95, 0x4a, 0x09, 0x87, 0x4a, 0x17, 0x85, 0x47, + 0x00, 0xa9, 0x47, 0x00, 0x88, 0x47, 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, + 0x95, 0x37, 0x00, 0x88, 0x37, 0x9f, 0x78, 0x9e, + 0x61, 0x07, 0x88, 0x61, 0x2f, 0x92, 0x34, 0x00, + 0x81, 0x34, 0x04, 0x84, 0x34, 0x9b, 0x7b, 0x02, + 0x80, 0x7b, 0x99, 0x4e, 0x04, 0x80, 0x4e, 0x3f, + 0x9f, 0x5a, 0x97, 0x59, 0x03, 0x93, 0x59, 0x01, + 0xad, 0x59, 0x83, 0x41, 0x00, 0x81, 0x41, 0x04, + 0x87, 0x41, 0x00, 0x82, 0x41, 0x00, 0x9c, 0x41, + 0x01, 0x82, 0x41, 0x03, 0x89, 0x41, 0x06, 0x88, + 0x41, 0x06, 0x9f, 0x71, 0x9f, 0x6d, 0x1f, 0xa6, + 0x53, 0x03, 0x8b, 0x53, 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, + 0x39, 0x04, 0x87, 0x39, 0x91, 0x7c, 0x06, 0x83, + 0x7c, 0x0b, 0x86, 0x7c, 0x4f, 0xc8, 0x72, 0x36, + 0xb2, 0x6b, 0x0c, 0xb2, 0x6b, 0x06, 0x85, 0x6b, 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, + 0x04, 0x00, 0xa9, 0xa1, 0x00, 0x82, 0xa1, 0x01, + 0x81, 0xa1, 0x4a, 0x82, 0x04, 0xa7, 0x70, 0x07, + 0xa9, 0x86, 0x15, 0x99, 0x73, 0x25, 0x9b, 0x18, + 0x13, 0x96, 0x26, 0x08, 0xcd, 0x0e, 0x03, 0xa3, + 0x0e, 0x08, 0x80, 0x0e, 0xc2, 0x3c, 0x09, 0x80, + 0x3c, 0x01, 0x98, 0x87, 0x06, 0x89, 0x87, 0x05, + 0xb4, 0x15, 0x00, 0x91, 0x15, 0x07, 0xa6, 0x50, + 0x08, 0xdf, 0x81, 0x00, 0x93, 0x85, 0x0a, 0x91, + 0x43, 0x00, 0xae, 0x43, 0x3d, 0x86, 0x5f, 0x00, + 0x80, 0x5f, 0x00, 0x83, 0x5f, 0x00, 0x8e, 0x5f, + 0x00, 0x8a, 0x5f, 0x05, 0xba, 0x45, 0x04, 0x89, + 0x45, 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, 0x65, 0x00, 0x84, 0x65, 0x1d, + 0xc7, 0x99, 0x07, 0x89, 0x99, 0x60, 0x45, 0xb5, + 0x83, 0x01, 0xa5, 0x83, 0x21, 0xc4, 0x5c, 0x0a, + 0x89, 0x5c, 0x05, 0x8c, 0x5d, 0x12, 0xb9, 0x91, + 0x05, 0x89, 0x91, 0x35, 0x9a, 0x02, 0x01, 0x8e, + 0x02, 0x03, 0x96, 0x02, 0x60, 0x58, 0xbb, 0x22, + 0x60, 0x03, 0xd2, 0xa0, 0x0b, 0x80, 0xa0, 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, + 0x63, 0x01, 0xad, 0x63, 0x01, 0x8a, 0x63, 0x1a, + 0xc7, 0xa3, 0x07, 0xd2, 0x88, 0x0c, 0x8f, 0x12, + 0xb8, 0x79, 0x06, 0x89, 0x20, 0x60, 0x95, 0x88, + 0x0c, 0x00, 0xac, 0x0c, 0x00, 0x8d, 0x0c, 0x09, + 0x9c, 0x0c, 0x02, 0x9f, 0x54, 0x01, 0x95, 0x54, + 0x00, 0x8d, 0x54, 0x48, 0x86, 0x55, 0x00, 0x81, + 0x55, 0x00, 0xab, 0x55, 0x02, 0x80, 0x55, 0x00, + 0x81, 0x55, 0x00, 0x88, 0x55, 0x07, 0x89, 0x55, + 0x05, 0x85, 0x2e, 0x00, 0x81, 0x2e, 0x00, 0xa4, + 0x2e, 0x00, 0x81, 0x2e, 0x00, 0x85, 0x2e, 0x06, + 0x89, 0x2e, 0x60, 0xd5, 0x98, 0x4f, 0x06, 0x90, + 0x3f, 0x00, 0xa8, 0x3f, 0x02, 0x9b, 0x3f, 0x55, + 0x80, 0x4c, 0x0e, 0xb1, 0x92, 0x0c, 0x80, 0x92, + 0xe3, 0x39, 0x1b, 0x60, 0x05, 0xe0, 0x0e, 0x1b, + 0x00, 0x84, 0x1b, 0x0a, 0xe0, 0x63, 0x1b, 0x69, + 0xeb, 0xe0, 0x02, 0x1e, 0x0c, 0xe3, 0xf5, 0x24, + 0x6f, 0x49, 0xe1, 0xe6, 0x03, 0x70, 0x11, 0x58, + 0xe1, 0xd8, 0x08, 0x06, 0x9e, 0x5e, 0x00, 0x89, + 0x5e, 0x03, 0x81, 0x5e, 0xce, 0x9a, 0x00, 0x89, + 0x9a, 0x05, 0x9d, 0x09, 0x01, 0x85, 0x09, 0x09, + 0xc5, 0x77, 0x09, 0x89, 0x77, 0x00, 0x86, 0x77, + 0x00, 0x94, 0x77, 0x04, 0x92, 0x77, 0x62, 0x4f, + 0xda, 0x56, 0x60, 0x04, 0xca, 0x5b, 0x03, 0xb8, + 0x5b, 0x06, 0x90, 0x5b, 0x3f, 0x80, 0x93, 0x80, + 0x67, 0x81, 0x30, 0x80, 0x44, 0x0a, 0x81, 0x30, + 0x0d, 0xf0, 0x07, 0x97, 0x93, 0x07, 0xe2, 0x9f, + 0x93, 0xe1, 0x75, 0x44, 0x29, 0x88, 0x93, 0x70, + 0x12, 0x86, 0x83, 0x3e, 0x00, 0x86, 0x3e, 0x00, + 0x81, 0x3e, 0x00, 0x80, 0x3e, 0xe0, 0xbe, 0x36, + 0x82, 0x3e, 0x0e, 0x80, 0x36, 0x1c, 0x82, 0x36, + 0x01, 0x80, 0x3e, 0x0d, 0x83, 0x3e, 0x07, 0xe1, + 0x2b, 0x67, 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, + 0x38, 0xbc, 0x19, 0x14, 0xc5, 0x2c, 0x60, 0x19, + 0x93, 0x19, 0x0b, 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, 0x84, + 0x0e, 0x84, 0x84, 0x00, 0x8e, 0x84, 0x63, 0xef, + 0x9e, 0x47, 0x05, 0x85, 0x47, 0x60, 0x74, 0x86, + 0x29, 0x00, 0x90, 0x29, 0x01, 0x86, 0x29, 0x00, + 0x81, 0x29, 0x00, 0x84, 0x29, 0x04, 0xbd, 0x1d, + 0x20, 0x80, 0x1d, 0x60, 0x0f, 0xac, 0x68, 0x02, + 0x8d, 0x68, 0x01, 0x89, 0x68, 0x03, 0x81, 0x68, + 0x60, 0xdf, 0x9e, 0x9b, 0x10, 0xb9, 0x9f, 0x04, + 0x80, 0x9f, 0x61, 0x6f, 0xa9, 0x62, 0x62, 0x85, 0x86, 0x27, 0x00, 0x83, 0x27, 0x00, 0x81, 0x27, - 0x00, 0x8e, 0x27, 0x00, 0xe0, 0x64, 0x57, 0x01, - 0x8f, 0x57, 0x28, 0xcb, 0x01, 0x03, 0x89, 0x01, + 0x00, 0x8e, 0x27, 0x00, 0xe0, 0x64, 0x58, 0x01, + 0x8f, 0x58, 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, @@ -3455,86 +3561,85 @@ static const uint8_t unicode_script_table[2690] = { 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, 0x90, 0x19, 0x02, 0x8c, 0x19, 0x02, + 0xe0, 0x16, 0x19, 0x03, 0xde, 0x19, 0x05, 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, + 0xf3, 0x19, 0x0b, 0x8d, 0x19, 0x01, 0x8c, 0x19, + 0x02, 0x88, 0x19, 0x06, 0xad, 0x19, 0x00, 0x86, + 0x19, 0x07, 0x8d, 0x19, 0x03, 0x88, 0x19, 0x06, + 0x88, 0x19, 0x06, 0xe0, 0x32, 0x19, 0x00, 0xb6, + 0x19, 0x24, 0x89, 0x19, 0x63, 0xa5, 0xf0, 0x96, + 0x7f, 0x30, 0x1f, 0xef, 0xd9, 0x30, 0x05, 0xe0, + 0x7d, 0x30, 0x01, 0xf0, 0x06, 0x21, 0x30, 0x0d, + 0xf0, 0x0c, 0xd0, 0x30, 0x6b, 0xbe, 0xe1, 0xbd, + 0x30, 0x65, 0x81, 0xf0, 0x02, 0xea, 0x30, 0x04, + 0xef, 0xff, 0x30, 0x7a, 0xcb, 0xf0, 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, + 0x00, 0x01, 0x2c, 0x1c, 0x00, 0x0c, 0x01, 0x47, + 0x80, 0x92, 0x00, 0x00, 0x02, 0x1d, 0x6e, 0x00, + 0x02, 0x1d, 0x29, 0x01, 0x02, 0x1d, 0x47, 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, + 0x04, 0x66, 0x32, 0x8b, 0x95, 0xa1, 0x0d, 0x00, + 0x00, 0x06, 0x04, 0x66, 0x32, 0x8b, 0x95, 0xa1, + 0x00, 0x03, 0x04, 0x8b, 0x95, 0x01, 0x00, 0x00, + 0x07, 0x01, 0x04, 0x66, 0x32, 0x8b, 0x95, 0xa1, + 0x1f, 0x00, 0x00, 0x09, 0x01, 0x04, 0x52, 0x53, + 0x73, 0x7c, 0x32, 0x86, 0x8b, 0x09, 0x00, 0x0a, + 0x02, 0x04, 0x8b, 0x09, 0x00, 0x09, 0x03, 0x04, + 0x95, 0xa1, 0x05, 0x00, 0x00, 0x02, 0x04, 0x8b, 0x62, 0x00, 0x00, 0x02, 0x04, 0x32, 0x81, 0xfb, 0x00, 0x00, 0x0d, 0x0b, 0x20, 0x2b, 0x2d, 0x2f, - 0x3d, 0x46, 0x50, 0x72, 0x7f, 0x90, 0x92, 0x97, + 0x3d, 0x47, 0x51, 0x74, 0x81, 0x92, 0x94, 0x99, 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, + 0x47, 0x51, 0x74, 0x92, 0x94, 0x99, 0x10, 0x00, + 0x00, 0x14, 0x0b, 0x20, 0x22, 0x2e, 0x55, 0x2b, + 0x2d, 0x2f, 0x3d, 0x50, 0x51, 0x63, 0x74, 0x45, + 0x85, 0x8a, 0x91, 0x92, 0x94, 0x99, 0x00, 0x15, + 0x0b, 0x20, 0x22, 0x2e, 0x55, 0x2b, 0x2d, 0x2f, + 0x3d, 0x49, 0x50, 0x51, 0x63, 0x74, 0x45, 0x85, + 0x8a, 0x91, 0x92, 0x94, 0x99, 0x09, 0x04, 0x20, + 0x22, 0x3c, 0x50, 0x75, 0x00, 0x09, 0x03, 0x0b, + 0x15, 0x8a, 0x75, 0x00, 0x09, 0x02, 0x2f, 0x5f, + 0x75, 0x00, 0x09, 0x02, 0x2d, 0x43, 0x80, 0x75, + 0x00, 0x0d, 0x02, 0x2b, 0x92, 0x80, 0x71, 0x00, + 0x09, 0x02, 0x3d, 0x63, 0x82, 0xcf, 0x00, 0x09, + 0x03, 0x15, 0x60, 0x8e, 0x80, 0x30, 0x00, 0x00, + 0x02, 0x28, 0x47, 0x85, 0xb8, 0x00, 0x01, 0x04, + 0x11, 0x33, 0x8d, 0x8c, 0x80, 0x4a, 0x00, 0x01, + 0x02, 0x5d, 0x7a, 0x00, 0x00, 0x00, 0x02, 0x5d, + 0x7a, 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, + 0x20, 0x81, 0x00, 0x02, 0x0b, 0x20, 0x00, 0x02, + 0x20, 0x81, 0x00, 0x06, 0x20, 0x3d, 0x51, 0x74, + 0x92, 0x94, 0x00, 0x01, 0x20, 0x01, 0x02, 0x20, + 0x81, 0x01, 0x01, 0x20, 0x00, 0x02, 0x20, 0x81, 0x00, 0x02, 0x0b, 0x20, 0x06, 0x01, 0x20, 0x00, - 0x02, 0x20, 0x61, 0x00, 0x02, 0x0b, 0x20, 0x01, + 0x02, 0x20, 0x63, 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, + 0x20, 0x00, 0x08, 0x0b, 0x20, 0x2b, 0x3d, 0x63, + 0x74, 0x94, 0x99, 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, + 0x01, 0x63, 0x80, 0x44, 0x00, 0x01, 0x01, 0x2c, + 0x35, 0x00, 0x00, 0x02, 0x1d, 0x8b, 0x00, 0x00, + 0x00, 0x01, 0x8b, 0x81, 0xb3, 0x00, 0x00, 0x02, + 0x47, 0x5d, 0x80, 0x3f, 0x00, 0x00, 0x03, 0x20, + 0x2b, 0x47, 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, 0xa2, 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, + 0x3e, 0xa2, 0x00, 0x00, 0x00, 0x05, 0x0d, 0x31, 0x30, 0x36, 0x3e, 0x07, 0x06, 0x0d, 0x31, 0x30, - 0x36, 0x3e, 0xa0, 0x03, 0x05, 0x0d, 0x31, 0x30, + 0x36, 0x3e, 0xa2, 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, @@ -3542,7 +3647,7 @@ static const uint8_t unicode_script_ext_table[828] = { 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, + 0x0d, 0x31, 0x30, 0x36, 0x3e, 0xa2, 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, @@ -3550,32 +3655,32 @@ static const uint8_t unicode_script_ext_table[828] = { 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, + 0x80, 0x0f, 0x00, 0x07, 0x02, 0x30, 0x47, 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, + 0x43, 0x3d, 0x3c, 0x50, 0x51, 0x5c, 0x63, 0x45, + 0x91, 0x99, 0x02, 0x0d, 0x20, 0x22, 0x2d, 0x2f, + 0x43, 0x3d, 0x3c, 0x50, 0x5c, 0x63, 0x45, 0x91, + 0x99, 0x03, 0x0b, 0x20, 0x22, 0x2d, 0x2f, 0x43, + 0x3c, 0x50, 0x5c, 0x45, 0x91, 0x99, 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, + 0x02, 0x20, 0x92, 0x39, 0x00, 0x00, 0x03, 0x40, + 0x47, 0x60, 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, + 0x66, 0x80, 0x31, 0x00, 0x00, 0x02, 0x04, 0x95, + 0x09, 0x00, 0x00, 0x02, 0x04, 0x95, 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, + 0x3e, 0xa2, 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, + 0x00, 0x01, 0x03, 0x1e, 0x1c, 0x4b, 0x00, 0x02, + 0x1c, 0x4b, 0x03, 0x00, 0x2c, 0x03, 0x1c, 0x4a, + 0x4b, 0x02, 0x00, 0x08, 0x02, 0x1c, 0x4b, 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, 0x00, 0x02, 0x53, 0x73, 0x87, 0x8d, 0x00, + 0x00, 0x02, 0x2b, 0x92, 0x00, 0x00, 0x00, 0x02, + 0x2b, 0x92, 0x36, 0x00, 0x01, 0x02, 0x2b, 0x92, + 0x8c, 0x12, 0x00, 0x01, 0x02, 0x2b, 0x92, 0x00, + 0x00, 0x00, 0x02, 0x2b, 0x92, 0xc0, 0x5c, 0x4b, 0x00, 0x03, 0x01, 0x23, 0x96, 0x3b, 0x00, 0x11, 0x01, 0x30, 0x9e, 0x5d, 0x00, 0x01, 0x01, 0x30, 0xce, 0xcd, 0x2d, 0x00, @@ -3616,7 +3721,7 @@ static const uint8_t unicode_prop_Other_Math_table[200] = { 0x80, 0x89, 0x80, 0x90, 0x22, 0x04, 0x80, 0x90, }; -static const uint8_t unicode_prop_Other_Alphabetic_table[417] = { +static const uint8_t unicode_prop_Other_Alphabetic_table[428] = { 0x43, 0x44, 0x80, 0x42, 0x69, 0x8d, 0x00, 0x01, 0x01, 0x00, 0xc7, 0x8a, 0xaf, 0x8c, 0x06, 0x8f, 0x80, 0xe4, 0x33, 0x19, 0x0b, 0x80, 0xa2, 0x80, @@ -3628,59 +3733,61 @@ static const uint8_t unicode_prop_Other_Alphabetic_table[417] = { 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, + 0x22, 0x10, 0x82, 0x89, 0x80, 0xa7, 0x84, 0xb8, 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, + 0xb9, 0x30, 0x10, 0x17, 0x81, 0x8a, 0x81, 0x8e, + 0x80, 0x8b, 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, + 0x92, 0x88, 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, 0x8a, 0x82, 0xac, 0x88, 0x88, 0x80, 0xbc, + 0x82, 0xa3, 0x8b, 0x91, 0x81, 0xb8, 0x82, 0xaf, + 0x8c, 0x8d, 0x81, 0xdb, 0x88, 0x08, 0x28, 0x08, + 0x40, 0x9c, 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, 0x88, 0x08, + 0x80, 0xaf, 0x32, 0x82, 0x60, 0x50, 0x0d, 0x00, + 0xb6, 0x33, 0xdc, 0x81, 0x60, 0x4c, 0xab, 0x80, + 0x60, 0x23, 0x60, 0x30, 0x90, 0x0e, 0x01, 0x04, + 0xe3, 0x80, 0x48, 0xb6, 0x80, 0x47, 0xe7, 0x99, + 0x85, 0x99, 0x85, 0x99, }; -static const uint8_t unicode_prop_Other_Lowercase_table[59] = { +static const uint8_t unicode_prop_Other_Lowercase_table[69] = { 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, + 0x31, 0x9d, 0x84, 0xdf, 0x80, 0xb3, 0x80, 0x4d, + 0x80, 0x80, 0x4c, 0x2e, 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, 0x80, 0x12, 0x81, 0x43, 0x61, 0x83, 0x88, + 0x80, 0x60, 0x5c, 0x15, 0x01, 0x10, 0xa9, 0x80, + 0x88, 0x60, 0xd8, 0x74, 0xbd, }; static const uint8_t unicode_prop_Other_Uppercase_table[15] = { @@ -3742,71 +3849,70 @@ static const uint8_t unicode_prop_Changes_When_Titlecased1_table[22] = { 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_Casefolded1_table[29] = { + 0x41, 0xef, 0x80, 0x41, 0x9e, 0x80, 0x9e, 0x80, + 0x5a, 0xe4, 0x83, 0x40, 0xb5, 0x00, 0x00, 0x00, + 0x80, 0xde, 0x06, 0x06, 0x80, 0x8a, 0x09, 0x81, + 0x89, 0x10, 0x81, 0x8d, 0x80, }; -static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[448] = { +static const uint8_t unicode_prop_Changes_When_NFKC_Casefolded1_table[447] = { 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, + 0x10, 0x82, 0xf3, 0x80, 0x8b, 0x80, 0x40, 0x84, + 0x01, 0x01, 0x80, 0xa2, 0x01, 0x80, 0x40, 0xbb, + 0x88, 0x9e, 0x29, 0x84, 0xda, 0x08, 0x81, 0x89, + 0x80, 0xa3, 0x04, 0x02, 0x04, 0x08, 0x07, 0x80, + 0x9e, 0x80, 0xa0, 0x82, 0x9c, 0x80, 0x42, 0x28, + 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, + 0xd5, 0x83, 0x40, 0xb5, 0x00, 0x00, 0x00, 0x80, + 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xb7, 0x05, 0x00, 0x13, 0x05, 0x11, 0x02, 0x0c, + 0x11, 0x00, 0x00, 0x0c, 0x15, 0x05, 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, 0xc2, + 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, 0x48, 0x2f, + 0xbd, 0x4d, 0x91, 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] = { @@ -3834,7 +3940,7 @@ static const uint8_t unicode_prop_Deprecated_table[23] = { 0x42, 0xb8, 0x81, 0x6d, 0xdc, 0xd5, 0x80, }; -static const uint8_t unicode_prop_Diacritic_table[391] = { +static const uint8_t unicode_prop_Diacritic_table[399] = { 0xdd, 0x00, 0x80, 0xc6, 0x05, 0x03, 0x01, 0x81, 0x41, 0xf6, 0x40, 0x9e, 0x07, 0x25, 0x90, 0x0b, 0x80, 0x88, 0x81, 0x40, 0xfc, 0x84, 0x40, 0xd0, @@ -3869,20 +3975,21 @@ static const uint8_t unicode_prop_Diacritic_table[391] = { 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, + 0x42, 0x3a, 0x85, 0x41, 0xd4, 0x82, 0xc5, 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, 0x56, 0xae, 0x8e, + 0x60, 0x36, 0x99, 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, 0x4e, 0x81, + 0xbd, 0x40, 0xc1, 0x86, 0x41, 0x76, 0x80, 0xbc, 0x83, 0x45, 0xdf, 0x86, 0xec, 0x10, 0x82, }; @@ -3914,16 +4021,16 @@ static const uint8_t unicode_prop_IDS_Trinary_Operator_table[4] = { 0x60, 0x2f, 0xf1, 0x81, }; -static const uint8_t unicode_prop_Ideographic_table[66] = { +static const uint8_t unicode_prop_Ideographic_table[69] = { 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, + 0x39, 0x85, 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x4c, 0x1e, 0x42, 0x1d, 0x45, 0xe1, - 0x53, 0x4a, + 0x53, 0x4a, 0x84, 0x50, 0x5f, }; static const uint8_t unicode_prop_Join_Control_table[4] = { @@ -3979,7 +4086,7 @@ static const uint8_t unicode_prop_Regional_Indicator_table[4] = { 0x61, 0xf1, 0xe5, 0x99, }; -static const uint8_t unicode_prop_Sentence_Terminal_table[194] = { +static const uint8_t unicode_prop_Sentence_Terminal_table[196] = { 0xa0, 0x80, 0x8b, 0x80, 0x8f, 0x80, 0x45, 0x48, 0x80, 0x40, 0x92, 0x82, 0x40, 0xb3, 0x80, 0xaa, 0x82, 0x40, 0xf5, 0x80, 0xbc, 0x00, 0x02, 0x81, @@ -4001,13 +4108,13 @@ static const uint8_t unicode_prop_Sentence_Terminal_table[194] = { 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, + 0xa3, 0x81, 0x42, 0xb3, 0x81, 0xc9, 0x81, 0x60, + 0x4b, 0x28, 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] = { +static const uint8_t unicode_prop_Soft_Dotted_table[79] = { 0xe8, 0x81, 0x40, 0xc3, 0x80, 0x41, 0x18, 0x80, 0x9d, 0x80, 0xb3, 0x80, 0x93, 0x80, 0x41, 0x3f, 0x80, 0xe1, 0x00, 0x80, 0x59, 0x08, 0x80, 0xb2, @@ -4017,10 +4124,10 @@ static const uint8_t unicode_prop_Soft_Dotted_table[74] = { 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, + 0x85, 0x80, 0x41, 0x30, 0x81, 0x99, 0x80, }; -static const uint8_t unicode_prop_Terminal_Punctuation_table[246] = { +static const uint8_t unicode_prop_Terminal_Punctuation_table[248] = { 0xa0, 0x80, 0x89, 0x00, 0x80, 0x8a, 0x0a, 0x80, 0x43, 0x3d, 0x07, 0x80, 0x42, 0x00, 0x80, 0xb8, 0x80, 0xc7, 0x80, 0x8d, 0x00, 0x82, 0x40, 0xb3, @@ -4048,19 +4155,19 @@ static const uint8_t unicode_prop_Terminal_Punctuation_table[246] = { 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, + 0x82, 0xac, 0x80, 0x42, 0x84, 0x81, 0xc9, 0x81, + 0x45, 0x2a, 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] = { +static const uint8_t unicode_prop_Unified_Ideograph_table[45] = { 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, + 0xa6, 0xdf, 0x9f, 0x50, 0x39, 0x85, 0x40, 0xdd, 0x81, 0x56, 0x81, 0x8d, 0x5d, 0x30, 0x54, 0x1e, - 0x53, 0x4a, + 0x53, 0x4a, 0x84, 0x50, 0x5f, }; static const uint8_t unicode_prop_Variation_Selector_table[13] = { @@ -4125,11 +4232,11 @@ static const uint8_t unicode_prop_Emoji_table[239] = { 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, + 0x02, 0x05, 0xd5, 0xaf, 0xc5, 0x27, 0x0a, 0x83, + 0x89, 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, + 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x88, 0x86, 0xad, + 0x06, 0x87, 0x8d, 0x83, 0x88, 0x86, 0x88, }; static const uint8_t unicode_prop_Emoji_Component_table[28] = { @@ -4152,7 +4259,7 @@ static const uint8_t unicode_prop_Emoji_Modifier_Base_table[71] = { 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, + 0x10, 0x8c, 0x40, 0xe4, 0x82, 0xa9, 0x88, }; static const uint8_t unicode_prop_Emoji_Presentation_table[145] = { @@ -4170,11 +4277,11 @@ static const uint8_t unicode_prop_Emoji_Presentation_table[145] = { 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, + 0xc5, 0x28, 0x12, 0x0a, 0x1b, 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, + 0x89, 0x80, 0x40, 0xb8, 0xef, 0x8c, 0x82, 0x88, + 0x86, 0xad, 0x06, 0x87, 0x8d, 0x83, 0x88, 0x86, + 0x88, }; static const uint8_t unicode_prop_Extended_Pictographic_table[156] = { @@ -4447,3 +4554,4 @@ static const uint16_t unicode_prop_len_table[] = { }; #endif /* CONFIG_ALL_UNICODE */ +/* 62 tables / 32261 bytes, 5 index / 345 bytes */ diff --git a/src/shared/quickjs/libunicode.c b/src/shared/quickjs/libunicode.c index 112da72da..482c51944 100644 --- a/src/shared/quickjs/libunicode.c +++ b/src/shared/quickjs/libunicode.c @@ -43,11 +43,111 @@ enum { 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, + RUN_TYPE_UF_EXT2, + RUN_TYPE_LF_EXT2, + RUN_TYPE_UF_EXT3, }; +static int lre_case_conv1(uint32_t c, int conv_type) +{ + uint32_t res[LRE_CC_RES_LEN_MAX]; + lre_case_conv(res, c, conv_type); + return res[0]; +} + +/* case conversion using the table entry 'idx' with value 'v' */ +static int lre_case_conv_entry(uint32_t *res, uint32_t c, int conv_type, uint32_t idx, uint32_t v) +{ + uint32_t code, data, type, a, is_lower; + is_lower = (conv_type != 0); + type = (v >> (32 - 17 - 7 - 4)) & 0xf; + data = ((v & 0xf) << 8) | case_conv_table2[idx]; + code = v >> (32 - 17); + 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_LF_EXT2: + if (!is_lower) + break; + res[0] = c - code + case_conv_ext[data >> 6]; + res[1] = case_conv_ext[data & 0x3f]; + return 2; + case RUN_TYPE_UF_EXT2: + if (conv_type == 1) + break; + res[0] = c - code + case_conv_ext[data >> 6]; + res[1] = case_conv_ext[data & 0x3f]; + if (conv_type == 2) { + /* convert to lower */ + res[0] = lre_case_conv1(res[0], 1); + res[1] = lre_case_conv1(res[1], 1); + } + return 2; + default: + case RUN_TYPE_UF_EXT3: + if (conv_type == 1) + break; + res[0] = case_conv_ext[data >> 8]; + res[1] = case_conv_ext[(data >> 4) & 0xf]; + res[2] = case_conv_ext[data & 0xf]; + if (conv_type == 2) { + /* convert to lower */ + res[0] = lre_case_conv1(res[0], 1); + res[1] = lre_case_conv1(res[1], 1); + res[2] = lre_case_conv1(res[2], 1); + } + return 3; + } + res[0] = c; + return 1; +} + /* conv_type: 0 = to upper 1 = to lower @@ -66,10 +166,9 @@ int lre_case_conv(uint32_t *res, uint32_t c, int conv_type) } } } else { - uint32_t v, code, data, type, len, a, is_lower; + uint32_t v, code, len; 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) { @@ -82,74 +181,7 @@ int lre_case_conv(uint32_t *res, uint32_t c, int conv_type) } 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; + return lre_case_conv_entry(res, c, conv_type, idx, v); } } } @@ -157,13 +189,80 @@ int lre_case_conv(uint32_t *res, uint32_t c, int conv_type) return 1; } +static int lre_case_folding_entry(uint32_t c, uint32_t idx, uint32_t v, BOOL is_unicode) +{ + uint32_t res[LRE_CC_RES_LEN_MAX]; + int len; + + if (is_unicode) { + len = lre_case_conv_entry(res, c, 2, idx, v); + if (len == 1) { + c = res[0]; + } else { + /* handle the few specific multi-character cases (see + unicode_gen.c:dump_case_folding_special_cases()) */ + if (c == 0xfb06) { + c = 0xfb05; + } else if (c == 0x01fd3) { + c = 0x390; + } else if (c == 0x01fe3) { + c = 0x3b0; + } + } + } 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_entry(res, c, FALSE, idx, v); + if (len == 1 && res[0] >= 128) + c = res[0]; + } + } + return c; +} + +/* JS regexp specific rules for case folding */ +int lre_canonicalize(uint32_t c, BOOL is_unicode) +{ + if (c < 128) { + /* fast case */ + if (is_unicode) { + if (c >= 'A' && c <= 'Z') { + c = c - 'A' + 'a'; + } + } else { + if (c >= 'a' && c <= 'z') { + c = c - 'a' + 'A'; + } + } + } else { + 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 lre_case_folding_entry(c, idx, v, is_unicode); + } + } + } + return c; +} + 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 @@ -214,6 +313,14 @@ static BOOL lre_is_in_table(uint32_t c, const uint8_t *table, return FALSE; /* outside the table */ p = table + pos; bit = 0; + /* Compressed run length encoding: + 00..3F: 2 packed lengths: 3-bit + 3-bit + 40..5F: 5-bits plus extra byte for length + 60..7F: 5-bits plus 2 extra bytes for length + 80..FF: 7-bit length + lengths must be incremented to get character count + Ranges alternate between false and true return value. + */ for(;;) { b = *p++; if (b < 64) { @@ -271,7 +378,7 @@ BOOL lre_is_case_ignorable(uint32_t c) /* character range */ -static maybe_unused void cr_dump(CharRange *cr) +static __maybe_unused void cr_dump(CharRange *cr) { int i; for(i = 0; i < cr->len; i++) @@ -327,7 +434,7 @@ static void cr_compress(CharRange *cr) { int i, j, k, len; uint32_t *pt; - + pt = cr->points; len = cr->len; i = 0; @@ -730,6 +837,13 @@ static int unicode_get_cc(uint32_t c) if (pos < 0) return 0; p = unicode_cc_table + pos; + /* Compressed run length encoding: + - 2 high order bits are combining class type + - 0:0, 1:230, 2:extra byte linear progression, 3:extra byte + - 00..2F: range length (add 1) + - 30..37: 3-bit range-length + 1 extra byte + - 38..3F: 3-bit range-length + 2 extra byte + */ for(;;) { b = *p++; type = b >> 6; @@ -1082,6 +1196,15 @@ static int unicode_general_category1(CharRange *cr, uint32_t gc_mask) p = unicode_gc_table; p_end = unicode_gc_table + countof(unicode_gc_table); c = 0; + /* Compressed range encoding: + initial byte: + bits 0..4: category number (special case 31) + bits 5..7: range length (add 1) + special case bits 5..7 == 7: read an extra byte + - 00..7F: range length (add 7 + 1) + - 80..BF: 6-bits plus extra byte for range length (add 7 + 128) + - C0..FF: 6-bits plus 2 extra bytes for range length (add 7 + 128 + 16384) + */ while (p < p_end) { b = *p++; n = b >> 5; @@ -1135,6 +1258,14 @@ static int unicode_prop1(CharRange *cr, int prop_idx) p_end = p + unicode_prop_len_table[prop_idx]; c = 0; bit = 0; + /* Compressed range encoding: + 00..3F: 2 packed lengths: 3-bit + 3-bit + 40..5F: 5-bits plus extra byte for length + 60..7F: 5-bits plus 2 extra bytes for length + 80..FF: 7-bit length + lengths must be incremented to get character count + Ranges alternate between false and true return value. + */ while (p < p_end) { c0 = c; b = *p++; @@ -1179,11 +1310,11 @@ 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(UF_D1_EXT) | MR(U_EXT) | MR(UF_EXT2) | MR(UF_EXT3), - MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(L_EXT2), + MR(L) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_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), + MR(UF) | MR(LF) | MR(UL) | MR(LSU) | MR(U2L_399_EXT2) | MR(LF_EXT) | MR(LF_EXT2) | MR(UF_D20) | MR(UF_D1_EXT) | MR(LF_EXT) | MR(UF_EXT2) | MR(UF_EXT3), }; #undef MR uint32_t mask, v, code, type, len, i, idx; @@ -1237,6 +1368,135 @@ static int unicode_case1(CharRange *cr, int case_mask) return 0; } +static int point_cmp(const void *p1, const void *p2, void *arg) +{ + uint32_t v1 = *(uint32_t *)p1; + uint32_t v2 = *(uint32_t *)p2; + return (v1 > v2) - (v1 < v2); +} + +static void cr_sort_and_remove_overlap(CharRange *cr) +{ + uint32_t start, end, start1, end1, i, j; + + /* the resulting ranges are not necessarily sorted and may overlap */ + rqsort(cr->points, cr->len / 2, sizeof(cr->points[0]) * 2, point_cmp, NULL); + j = 0; + for(i = 0; i < cr->len; ) { + start = cr->points[i]; + end = cr->points[i + 1]; + i += 2; + while (i < cr->len) { + start1 = cr->points[i]; + end1 = cr->points[i + 1]; + if (start1 > end) { + /* |------| + * |-------| */ + break; + } else if (end1 <= end) { + /* |------| + * |--| */ + i += 2; + } else { + /* |------| + * |-------| */ + end = end1; + i += 2; + } + } + cr->points[j] = start; + cr->points[j + 1] = end; + j += 2; + } + cr->len = j; +} + +/* canonicalize a character set using the JS regex case folding rules + (see lre_canonicalize()) */ +int cr_regexp_canonicalize(CharRange *cr, BOOL is_unicode) +{ + CharRange cr_inter, cr_mask, cr_result, cr_sub; + uint32_t v, code, len, i, idx, start, end, c, d_start, d_end, d; + + cr_init(&cr_mask, cr->mem_opaque, cr->realloc_func); + cr_init(&cr_inter, cr->mem_opaque, cr->realloc_func); + cr_init(&cr_result, cr->mem_opaque, cr->realloc_func); + cr_init(&cr_sub, cr->mem_opaque, cr->realloc_func); + + if (unicode_case1(&cr_mask, is_unicode ? CASE_F : CASE_U)) + goto fail; + if (cr_op(&cr_inter, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER)) + goto fail; + + if (cr_invert(&cr_mask)) + goto fail; + if (cr_op(&cr_sub, cr_mask.points, cr_mask.len, cr->points, cr->len, CR_OP_INTER)) + goto fail; + + /* cr_inter = cr & cr_mask */ + /* cr_sub = cr & ~cr_mask */ + + /* use the case conversion table to compute the result */ + d_start = -1; + d_end = -1; + idx = 0; + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + for(i = 0; i < cr_inter.len; i += 2) { + start = cr_inter.points[i]; + end = cr_inter.points[i + 1]; + + for(c = start; c < end; c++) { + for(;;) { + if (c >= code && c < code + len) + break; + idx++; + assert(idx < countof(case_conv_table1)); + v = case_conv_table1[idx]; + code = v >> (32 - 17); + len = (v >> (32 - 17 - 7)) & 0x7f; + } + d = lre_case_folding_entry(c, idx, v, is_unicode); + /* try to merge with the current interval */ + if (d_start == -1) { + d_start = d; + d_end = d + 1; + } else if (d_end == d) { + d_end++; + } else { + cr_add_interval(&cr_result, d_start, d_end); + d_start = d; + d_end = d + 1; + } + } + } + if (d_start != -1) { + if (cr_add_interval(&cr_result, d_start, d_end)) + goto fail; + } + + /* the resulting ranges are not necessarily sorted and may overlap */ + cr_sort_and_remove_overlap(&cr_result); + + /* or with the character not affected by the case folding */ + cr->len = 0; + if (cr_op(cr, cr_result.points, cr_result.len, cr_sub.points, cr_sub.len, CR_OP_UNION)) + goto fail; + + cr_free(&cr_inter); + cr_free(&cr_mask); + cr_free(&cr_result); + cr_free(&cr_sub); + return 0; + fail: + cr_free(&cr_inter); + cr_free(&cr_mask); + cr_free(&cr_result); + cr_free(&cr_sub); + return -1; +} + typedef enum { POP_GC, POP_PROP, @@ -1556,3 +1816,97 @@ int unicode_prop(CharRange *cr, const char *prop_name) } #endif /* CONFIG_ALL_UNICODE */ + +/*---- lre codepoint categorizing functions ----*/ + +#define S UNICODE_C_SPACE +#define D UNICODE_C_DIGIT +#define X UNICODE_C_XDIGIT +#define U UNICODE_C_UPPER +#define L UNICODE_C_LOWER +#define _ UNICODE_C_UNDER +#define d UNICODE_C_DOLLAR + +uint8_t const lre_ctype_bits[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, S, S, S, S, S, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + S, 0, 0, 0, d, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + X|D, X|D, X|D, X|D, X|D, X|D, X|D, X|D, + X|D, X|D, 0, 0, 0, 0, 0, 0, + + 0, X|U, X|U, X|U, X|U, X|U, X|U, U, + U, U, U, U, U, U, U, U, + U, U, U, U, U, U, U, U, + U, U, U, 0, 0, 0, 0, _, + + 0, X|L, X|L, X|L, X|L, X|L, X|L, L, + L, L, L, L, L, L, L, L, + L, L, L, L, L, L, L, L, + L, L, L, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + S, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +#undef S +#undef D +#undef X +#undef U +#undef L +#undef _ +#undef d + +/* 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_non_ascii(uint32_t c) +{ + size_t i, n; + + n = countof(char_range_s); + for(i = 5; i < n; i += 2) { + uint32_t low = char_range_s[i]; + uint32_t high = char_range_s[i + 1]; + if (c < low) + return FALSE; + if (c < high) + return TRUE; + } + return FALSE; +} diff --git a/src/shared/quickjs/libunicode.h b/src/shared/quickjs/libunicode.h index cfa600a50..cc2f244c7 100644 --- a/src/shared/quickjs/libunicode.h +++ b/src/shared/quickjs/libunicode.h @@ -1,6 +1,6 @@ /* * Unicode utilities - * + * * Copyright (c) 2017-2018 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -24,26 +24,13 @@ #ifndef LIBUNICODE_H #define LIBUNICODE_H -#include <inttypes.h> - -#define LRE_BOOL int /* for documentation purposes */ +#include <stdint.h> /* 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 { @@ -101,10 +88,14 @@ int cr_op(CharRange *cr, const uint32_t *a_pt, int a_len, int cr_invert(CharRange *cr); -#ifdef CONFIG_ALL_UNICODE +int cr_regexp_canonicalize(CharRange *cr, int is_unicode); -LRE_BOOL lre_is_id_start(uint32_t c); -LRE_BOOL lre_is_id_continue(uint32_t c); +typedef enum { + UNICODE_NFC, + UNICODE_NFD, + UNICODE_NFKC, + UNICODE_NFKD, +} UnicodeNormalizationEnum; int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, UnicodeNormalizationEnum n_type, @@ -112,13 +103,80 @@ int unicode_normalize(uint32_t **pdst, const uint32_t *src, int src_len, /* Unicode character range functions */ -int unicode_script(CharRange *cr, - const char *script_name, LRE_BOOL is_ext); +int unicode_script(CharRange *cr, const char *script_name, int 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 */ +int lre_case_conv(uint32_t *res, uint32_t c, int conv_type); +int lre_canonicalize(uint32_t c, int is_unicode); + +/* Code point type categories */ +enum { + UNICODE_C_SPACE = (1 << 0), + UNICODE_C_DIGIT = (1 << 1), + UNICODE_C_UPPER = (1 << 2), + UNICODE_C_LOWER = (1 << 3), + UNICODE_C_UNDER = (1 << 4), + UNICODE_C_DOLLAR = (1 << 5), + UNICODE_C_XDIGIT = (1 << 6), +}; +extern uint8_t const lre_ctype_bits[256]; + +/* zero or non-zero return value */ +int lre_is_cased(uint32_t c); +int lre_is_case_ignorable(uint32_t c); +int lre_is_id_start(uint32_t c); +int lre_is_id_continue(uint32_t c); + +static inline int lre_is_space_byte(uint8_t c) { + return lre_ctype_bits[c] & UNICODE_C_SPACE; +} + +static inline int lre_is_id_start_byte(uint8_t c) { + return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER | + UNICODE_C_UNDER | UNICODE_C_DOLLAR); +} -#undef LRE_BOOL +static inline int lre_is_id_continue_byte(uint8_t c) { + return lre_ctype_bits[c] & (UNICODE_C_UPPER | UNICODE_C_LOWER | + UNICODE_C_UNDER | UNICODE_C_DOLLAR | + UNICODE_C_DIGIT); +} + +int lre_is_space_non_ascii(uint32_t c); + +static inline int lre_is_space(uint32_t c) { + if (c < 256) + return lre_is_space_byte(c); + else + return lre_is_space_non_ascii(c); +} + +static inline int lre_js_is_ident_first(uint32_t c) { + if (c < 128) { + return lre_is_id_start_byte(c); + } else { +#ifdef CONFIG_ALL_UNICODE + return lre_is_id_start(c); +#else + return !lre_is_space_non_ascii(c); +#endif + } +} + +static inline int lre_js_is_ident_next(uint32_t c) { + if (c < 128) { + return lre_is_id_continue_byte(c); + } else { + /* ZWNJ and ZWJ are accepted in identifiers */ + if (c >= 0x200C && c <= 0x200D) + return TRUE; +#ifdef CONFIG_ALL_UNICODE + return lre_is_id_continue(c); +#else + return !lre_is_space_non_ascii(c); +#endif + } +} #endif /* LIBUNICODE_H */ diff --git a/src/shared/quickjs/list.h b/src/shared/quickjs/list.h index e7f51a9d9..809831115 100644 --- a/src/shared/quickjs/list.h +++ b/src/shared/quickjs/list.h @@ -36,8 +36,7 @@ struct list_head { #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))) +#define list_entry(el, type, member) container_of(el, type, member) static inline void init_list_head(struct list_head *head) { @@ -46,8 +45,8 @@ static inline void init_list_head(struct list_head *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) +static inline void __list_add(struct list_head *el, + struct list_head *prev, struct list_head *next) { prev->next = el; el->prev = prev; @@ -58,13 +57,13 @@ static inline void list_add_impl(struct list_head *el, /* add 'el' at the head of the list 'head' (= after element head) */ static inline void list_add(struct list_head *el, struct list_head *head) { - list_add_impl(el, head, head->next); + __list_add(el, head, head->next); } /* add 'el' at the end of the list 'head' (= before element head) */ static inline void list_add_tail(struct list_head *el, struct list_head *head) { - list_add_impl(el, head->prev, head); + __list_add(el, head->prev, head); } static inline void list_del(struct list_head *el) diff --git a/src/shared/quickjs/quickjs-atom.h b/src/shared/quickjs/quickjs-atom.h index 4c2279452..f4d5838d4 100644 --- a/src/shared/quickjs/quickjs-atom.h +++ b/src/shared/quickjs/quickjs-atom.h @@ -1,6 +1,6 @@ /* * QuickJS atom definitions - * + * * Copyright (c) 2017-2018 Fabrice Bellard * Copyright (c) 2017-2018 Charlie Gordon * @@ -82,6 +82,7 @@ DEF(length, "length") DEF(fileName, "fileName") DEF(lineNumber, "lineNumber") DEF(message, "message") +DEF(cause, "cause") DEF(errors, "errors") DEF(stack, "stack") DEF(name, "name") @@ -166,22 +167,23 @@ DEF(revoke, "revoke") DEF(async, "async") DEF(exec, "exec") DEF(groups, "groups") +DEF(indices, "indices") DEF(status, "status") DEF(reason, "reason") DEF(globalThis, "globalThis") -#ifdef CONFIG_BIGNUM DEF(bigint, "bigint") +#ifdef CONFIG_BIGNUM DEF(bigfloat, "bigfloat") DEF(bigdecimal, "bigdecimal") DEF(roundingMode, "roundingMode") DEF(maximumSignificantDigits, "maximumSignificantDigits") DEF(maximumFractionDigits, "maximumFractionDigits") #endif -#ifdef CONFIG_ATOMICS +/* the following 3 atoms are only used with CONFIG_ATOMICS */ DEF(not_equal, "not-equal") DEF(timed_out, "timed-out") DEF(ok, "ok") -#endif +/* */ DEF(toJSON, "toJSON") /* class names */ DEF(Object, "Object") @@ -202,22 +204,20 @@ DEF(RegExp, "RegExp") DEF(ArrayBuffer, "ArrayBuffer") DEF(SharedArrayBuffer, "SharedArrayBuffer") /* must keep same order as class IDs for typed arrays */ -DEF(Uint8ClampedArray, "Uint8ClampedArray") +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") +#ifdef CONFIG_BIGNUM DEF(BigFloat, "BigFloat") DEF(BigFloatEnv, "BigFloatEnv") DEF(BigDecimal, "BigDecimal") @@ -269,5 +269,5 @@ 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 index c731a14a9..1e1821259 100644 --- a/src/shared/quickjs/quickjs-opcode.h +++ b/src/shared/quickjs/quickjs-opcode.h @@ -1,6 +1,6 @@ /* * QuickJS opcode definitions - * + * * Copyright (c) 2017-2018 Fabrice Bellard * Copyright (c) 2017-2018 Charlie Gordon * @@ -165,14 +165,15 @@ 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( 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(get_loc_checkthis, 3, 0, 1, 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) @@ -182,6 +183,7 @@ 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( nip_catch, 1, 2, 1, none) /* catch ... a -> a */ DEF( to_object, 1, 1, 1, none) //DEF( to_string, 1, 1, 1, none) @@ -208,7 +210,6 @@ 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) @@ -256,12 +257,13 @@ 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) +DEF( private_in, 1, 2, 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) +DEF( nop, 1, 0, 0, none) /* temporary opcodes: never emitted in the final bytecode */ @@ -270,6 +272,8 @@ 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 */ +/* the following opcodes must be in the same order as the 'with_x' and + get_var_undef, get_var and put_var opcodes */ 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 */ @@ -277,12 +281,15 @@ def(scope_delete_var, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 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_var_checkthis, 7, 0, 1, atom_u16) /* emitted in phase 1, removed in phase 2, only used to return 'this' in derived class constructors */ 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(scope_put_private_field, 7, 2, 0, atom_u16) /* obj value ->, emitted in phase 1, removed in phase 2 */ +def(scope_in_private_field, 7, 1, 1, atom_u16) /* obj -> res emitted in phase 1, removed in phase 2 */ +def(get_field_opt_chain, 5, 1, 1, atom) /* emitted in phase 1, removed in phase 2 */ +def(get_array_el_opt_chain, 1, 2, 1, none) /* 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 diff --git a/src/shared/quickjs/quickjs.c b/src/shared/quickjs/quickjs.c index 1e3b97d51..3bafe6948 100644 --- a/src/shared/quickjs/quickjs.c +++ b/src/shared/quickjs/quickjs.c @@ -58,9 +58,8 @@ #include "list.h" #include "quickjs.h" #include "libregexp.h" -#ifdef CONFIG_BIGNUM +#include "libunicode.h" #include "libbf.h" -#endif #define OPTIMIZE 0 #define SHORT_OPCODES 1 @@ -102,6 +101,7 @@ 8: dump stdlib functions 16: dump bytecode in hex 32: dump line number table + 64: dump compute_stack_size */ //#define DUMP_BYTECODE (1) /* dump the occurence of the automatic GC */ @@ -176,15 +176,13 @@ enum { 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 */ +#ifdef CONFIG_BIGNUM JS_CLASS_BIG_FLOAT, /* u.object_data */ JS_CLASS_FLOAT_ENV, /* u.float_env */ JS_CLASS_BIG_DECIMAL, /* u.object_data */ @@ -232,14 +230,14 @@ typedef enum JSErrorEnum { JS_NATIVE_ERROR_COUNT, /* number of different NativeError objects */ } JSErrorEnum; -#define JS_MAX_LOCAL_VARS 65536 +#define JS_MAX_LOCAL_VARS 65535 #define JS_STACK_SIZE_MAX 65534 #define JS_STRING_LEN_MAX ((1 << 30) - 1) #ifdef __GNUC__ -#define warn_unused __attribute__((warn_unused_result)) +#define __exception __attribute__((warn_unused_result)) #else -#define warn_unused +#define __exception #endif typedef struct JSShape JSShape; @@ -254,7 +252,6 @@ typedef enum { 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 { @@ -272,7 +269,6 @@ typedef struct { int64_t exponent); int (*mul_pow10)(JSContext *ctx, JSValue *sp); } JSNumericOperations; -#endif struct JSRuntime { JSMallocFunctions mf; @@ -324,6 +320,8 @@ struct JSRuntime { JSModuleNormalizeFunc *module_normalize_func; JSModuleLoaderFunc *module_loader_func; void *module_loader_opaque; + /* timestamp for internal use in module evaluation */ + int64_t module_async_evaluation_next_timestamp; BOOL can_block : 8; /* TRUE if Atomics.wait can block */ /* used to allocate, free and clone SharedArrayBuffers */ @@ -334,9 +332,9 @@ struct JSRuntime { 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; +#ifdef CONFIG_BIGNUM JSNumericOperations bigfloat_ops; JSNumericOperations bigdecimal_ops; uint32_t operator_count; @@ -357,17 +355,18 @@ struct JSClass { #define JS_MODE_STRICT (1 << 0) #define JS_MODE_STRIP (1 << 1) #define JS_MODE_MATH (1 << 2) +#define JS_MODE_ASYNC (1 << 3) /* async function */ 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 */ + struct list_head var_ref_list; /* list of JSVarRef.var_ref_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 */ + int js_mode; /* for C functions, only JS_MODE_MATH may be set */ /* only used in generators. Current stack pointer value. NULL if the function is running. */ JSValue *cur_sp; @@ -398,13 +397,8 @@ typedef struct JSVarRef { union { JSGCObjectHeader header; /* must come first */ struct { - int _gc_ref_count; /* corresponds to header.ref_count */ - uint8_t _gc_mark; /* corresponds to header.mark/gc_obj_type */ - - /* 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 - */ + int __gc_ref_count; /* corresponds to header.ref_count */ + uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ uint8_t is_detached : 1; uint8_t is_arg : 1; uint16_t var_idx; /* index of the corresponding function variable on @@ -413,16 +407,15 @@ typedef struct JSVarRef { }; 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 */ + union { + JSValue value; /* used when is_detached = TRUE */ + struct { + struct list_head var_ref_link; /* JSStackFrame.var_ref_list list */ + struct JSAsyncFunctionState *async_func; /* != NULL if async stack frame */ + }; /* used when is_detached = FALSE */ + }; } 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 { @@ -430,6 +423,13 @@ typedef struct JSBigFloat { bf_t num; } JSBigFloat; +#ifdef CONFIG_BIGNUM +typedef struct JSFloatEnv { + limb_t prec; + bf_flags_t flags; + unsigned int status; +} JSFloatEnv; + typedef struct JSBigDecimal { JSRefCountHeader header; /* must come first, 32-bit */ bfdec_t num; @@ -473,15 +473,14 @@ struct JSContext { 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 */ +#ifdef CONFIG_BIGNUM 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 */ @@ -598,6 +597,7 @@ typedef struct JSVarDef { uint8_t is_const : 1; uint8_t is_lexical : 1; uint8_t is_captured : 1; + uint8_t is_static_private : 1; /* only used during private class field parsing */ uint8_t var_kind : 4; /* see JSVarKindEnum */ /* only used during compilation: function pool index for lexical variables with var_kind = @@ -638,7 +638,8 @@ typedef struct JSFunctionBytecode { 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 is_direct_or_indirect_eval : 1; /* used by JS_GetScriptOrModuleName() */ + /* XXX: 10 bits available */ uint8_t *byte_code_buf; /* (self pointer) */ int byte_code_len; JSAtom func_name; @@ -678,9 +679,11 @@ typedef enum JSIteratorKindEnum { typedef struct JSForInIterator { JSValue obj; - BOOL is_array; - uint32_t array_length; uint32_t idx; + uint32_t atom_count; + uint8_t in_prototype_chain; + uint8_t is_array; + JSPropertyEnum *tab_atom; /* is_array = FALSE */ } JSForInIterator; typedef struct JSRegExp { @@ -714,21 +717,16 @@ typedef struct JSTypedArray { } JSTypedArray; typedef struct JSAsyncFunctionState { - JSValue this_val; /* 'this' generator argument */ + JSGCObjectHeader header; + JSValue this_val; /* 'this' argument */ int argc; /* number of function arguments */ BOOL throw_flag; /* used to throw an exception in JS_CallInternal() */ + BOOL is_completed; /* TRUE if the function has returned. The stack + frame is no longer valid */ + JSValue resolving_funcs[2]; /* only used in JS async functions */ 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, @@ -810,6 +808,15 @@ typedef struct JSImportEntry { int req_module_idx; /* in req_module_entries */ } JSImportEntry; +typedef enum { + JS_MODULE_STATUS_UNLINKED, + JS_MODULE_STATUS_LINKING, + JS_MODULE_STATUS_LINKED, + JS_MODULE_STATUS_EVALUATING, + JS_MODULE_STATUS_EVALUATING_ASYNC, + JS_MODULE_STATUS_EVALUATED, +} JSModuleStatus; + struct JSModuleDef { JSRefCountHeader header; /* must come first, 32-bit */ JSAtom module_name; @@ -834,11 +841,24 @@ struct JSModuleDef { JSValue module_ns; JSValue func_obj; /* only used for JS modules */ JSModuleInitFunc *init_func; /* only used for C modules */ + BOOL has_tla : 8; /* true if func_obj contains await */ BOOL resolved : 8; BOOL func_created : 8; - BOOL instantiated : 8; - BOOL evaluated : 8; - BOOL eval_mark : 8; /* temporary use during js_evaluate_module() */ + JSModuleStatus status : 8; + /* temp use during js_module_link() & js_module_evaluate() */ + int dfs_index, dfs_ancestor_index; + JSModuleDef *stack_prev; + /* temp use during js_module_evaluate() */ + JSModuleDef **async_parent_modules; + int async_parent_modules_count; + int async_parent_modules_size; + int pending_async_dependencies; + BOOL async_evaluation; + int64_t async_evaluation_timestamp; + JSModuleDef *cycle_root; + JSValue promise; /* corresponds to spec field: capability */ + JSValue resolving_funcs[2]; /* corresponds to spec field: capability */ + /* true if evaluation yielded an exception. It is saved in eval_exception */ BOOL eval_has_exception : 8; @@ -907,8 +927,8 @@ struct JSObject { union { JSGCObjectHeader header; struct { - int _gc_ref_count; /* corresponds to header.ref_count */ - uint8_t _gc_mark; /* corresponds to header.mark/gc_obj_type */ + int __gc_ref_count; /* corresponds to header.ref_count */ + uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ uint8_t extensible : 1; uint8_t free_mark : 1; /* only used when freeing objects with cycles */ @@ -946,7 +966,7 @@ struct JSObject { 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 JSAsyncFunctionState *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 */ @@ -989,8 +1009,9 @@ struct JSObject { } u; /* byte sizes: 40/48/72 */ }; + enum { - JS_ATOM_NULL_ = JS_ATOM_NULL, + __JS_ATOM_NULL = JS_ATOM_NULL, #define DEF(name, str) JS_ATOM_ ## name, #include "quickjs-atom.h" #undef DEF @@ -1036,8 +1057,8 @@ enum OPCodeEnum { }; static int JS_InitAtoms(JSRuntime *rt); -static JSAtom JS_NewAtomInitImpl(JSRuntime *rt, const char *str, int len, - int atom_type); +static JSAtom __JS_NewAtomInit(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, @@ -1057,34 +1078,29 @@ static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_o int argc, JSValueConst *argv); static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, int argc, JSValueConst *argv); -static warn_unused int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, +static __exception 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, +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 __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_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_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_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); @@ -1128,6 +1144,12 @@ 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 + +#define HINT_STRING 0 +#define HINT_NUMBER 1 +#define HINT_NONE 2 +#define HINT_FORCE_ORDINARY (1 << 4) // don't try Symbol.toPrimitive +static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint); 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); @@ -1149,13 +1171,25 @@ typedef enum 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_strict_eq(JSContext *ctx, JSValueConst op1, JSValueConst 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); +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); #ifdef CONFIG_BIGNUM static void js_float_env_finalizer(JSRuntime *rt, JSValue val); static JSValue JS_NewBigFloat(JSContext *ctx); @@ -1170,18 +1204,6 @@ 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); @@ -1191,9 +1213,10 @@ 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_resolve_proxy(JSContext *ctx, JSValueConst *pval, int throw_exception); 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, @@ -1211,11 +1234,17 @@ static JSValue js_typed_array_constructor(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int classid); +static JSValue js_typed_array_constructor_ta(JSContext *ctx, + JSValueConst new_target, + JSValueConst src_obj, + 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 void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s); +static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s); static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, int argc, JSValueConst *argv, @@ -1235,12 +1264,14 @@ 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, +static __exception 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 JSValue js_promise_then(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv); static int js_string_compare(JSContext *ctx, const JSString *p1, const JSString *p2); static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val); @@ -1252,17 +1283,15 @@ 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, +static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, JSValueConst obj); -static warn_unused int js_get_length64(JSContext *ctx, int64_t *pres, +static __exception 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, @@ -1281,13 +1310,13 @@ 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 JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int is_map); static const JSClassExoticMethods js_arguments_exotic_methods; static const JSClassExoticMethods js_string_exotic_methods; @@ -1349,14 +1378,12 @@ void *js_mallocz_rt(JSRuntime *rt, size_t size) 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) @@ -1473,6 +1500,10 @@ static inline int is_digit(int c) { return c >= '0' && c <= '9'; } +static inline int string_get(const JSString *p, int idx) { + return p->is_wide_char ? p->u.str16[idx] : p->u.str8[idx]; +} + typedef struct JSClassShortDef { JSAtom class_name; JSClassFinalizer *finalizer; @@ -1507,15 +1538,13 @@ static JSClassShortDef const js_std_class_def[] = { { 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 */ +#ifdef CONFIG_BIGNUM { 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 */ @@ -1550,7 +1579,6 @@ static int init_class_range(JSRuntime *rt, JSClassShortDef const *tab, return 0; } -#ifdef CONFIG_BIGNUM static JSValue JS_ThrowUnsupportedOperation(JSContext *ctx) { return JS_ThrowTypeError(ctx, "unsupported operation"); @@ -1606,8 +1634,6 @@ static void set_dummy_numeric_ops(JSNumericOperations *ops) 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) @@ -1672,9 +1698,9 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque) 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); +#ifdef CONFIG_BIGNUM set_dummy_numeric_ops(&rt->bigfloat_ops); set_dummy_numeric_ops(&rt->bigdecimal_ops); #endif @@ -1729,19 +1755,19 @@ void JS_SetRuntimeOpaque(JSRuntime *rt, void *opaque) } /* default memory allocation functions with memory limitation */ -static inline size_t js_def_malloc_usable_size(void *ptr) +static size_t js_def_malloc_usable_size(const void *ptr) { #if defined(__APPLE__) return malloc_size(ptr); #elif defined(_WIN32) - return _msize(ptr); + return _msize((void *)ptr); #elif defined(EMSCRIPTEN) return 0; #elif defined(__linux__) - return malloc_usable_size(ptr); + return malloc_usable_size((void *)ptr); #else /* change this to `return 0;` if compilation fails */ - return malloc_usable_size(ptr); + return malloc_usable_size((void *)ptr); #endif } @@ -1805,18 +1831,7 @@ 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 + js_def_malloc_usable_size, }; JSRuntime *JS_NewRuntime(void) @@ -2047,9 +2062,7 @@ void JS_FreeRuntime(JSRuntime *rt) } 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 */ @@ -2187,8 +2200,8 @@ JSContext *JS_NewContextRaw(JSRuntime *rt) } ctx->rt = rt; list_add_tail(&ctx->link, &rt->context_list); -#ifdef CONFIG_BIGNUM ctx->bf_ctx = &rt->bf_ctx; +#ifdef CONFIG_BIGNUM ctx->fp_env.prec = 113; ctx->fp_env.flags = bf_set_exp_bits(15) | BF_RNDN | BF_FLAG_SUBNORMAL; #endif @@ -2221,9 +2234,7 @@ JSContext *JS_NewContext(JSRuntime *rt) JS_AddIntrinsicMapSet(ctx); JS_AddIntrinsicTypedArrays(ctx); JS_AddIntrinsicPromise(ctx); -#ifdef CONFIG_BIGNUM JS_AddIntrinsicBigInt(ctx); -#endif return ctx; } @@ -2264,7 +2275,6 @@ JSValue JS_GetClassProto(JSContext *ctx, JSClassID 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 */ @@ -2274,8 +2284,7 @@ static void js_free_modules(JSContext *ctx, JSFreeModuleEnum flag) 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)) { + (flag == JS_FREE_MODULE_NOT_RESOLVED && !m->resolved)) { js_free_module_def(ctx, m); } } @@ -2431,6 +2440,11 @@ static inline BOOL is_math_mode(JSContext *ctx) JSStackFrame *sf = ctx->rt->current_stack_frame; return (sf && (sf->js_mode & JS_MODE_MATH)); } +#else +static inline BOOL is_math_mode(JSContext *ctx) +{ + return FALSE; +} #endif /* JSAtom support */ @@ -2442,7 +2456,7 @@ static inline BOOL is_math_mode(JSContext *ctx) /* return the max count from the hash size */ #define JS_ATOM_COUNT_RESIZE(n) ((n) * 2) -static inline BOOL JS_AtomIsConst(JSAtom v) +static inline BOOL __JS_AtomIsConst(JSAtom v) { #if defined(DUMP_LEAKS) && DUMP_LEAKS > 1 return (int32_t)v <= 0; @@ -2451,17 +2465,17 @@ static inline BOOL JS_AtomIsConst(JSAtom v) #endif } -static inline BOOL JS_AtomIsTaggedInt(JSAtom v) +static inline BOOL __JS_AtomIsTaggedInt(JSAtom v) { return (v & JS_ATOM_TAG_INT) != 0; } -static inline JSAtom JS_AtomFromUInt32(uint32_t v) +static inline JSAtom __JS_AtomFromUInt32(uint32_t v) { return v | JS_ATOM_TAG_INT; } -static inline uint32_t JS_AtomToUInt32(JSAtom atom) +static inline uint32_t __JS_AtomToUInt32(JSAtom atom) { return atom & ~JS_ATOM_TAG_INT; } @@ -2481,10 +2495,7 @@ static inline BOOL is_num_string(uint32_t *pval, const JSString *p) 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]; + c = string_get(p, 0); if (is_num(c)) { if (c == '0') { if (len != 1) @@ -2493,10 +2504,7 @@ static inline BOOL is_num_string(uint32_t *pval, const JSString *p) } 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]; + c = string_get(p, i); if (!is_num(c)) return FALSE; n64 = (uint64_t)n * 10 + (c - '0'); @@ -2541,10 +2549,24 @@ static uint32_t hash_string(const JSString *str, uint32_t h) return h; } -static maybe_unused void JS_DumpString(JSRuntime *rt, - const JSString *p) +static __maybe_unused void JS_DumpChar(JSRuntime *rt, int c, int sep) +{ + 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); + } +} + +static __maybe_unused void JS_DumpString(JSRuntime *rt, const JSString *p) { - int i, c, sep; + int i, sep; if (p == NULL) { printf("<null>"); @@ -2554,26 +2576,12 @@ static maybe_unused void JS_DumpString(JSRuntime *rt, 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); - } + JS_DumpChar(rt, string_get(p, i), sep); } putchar(sep); } -static maybe_unused void JS_DumpAtoms(JSRuntime *rt) +static __maybe_unused void JS_DumpAtoms(JSRuntime *rt) { JSAtomStruct *p; int h, i; @@ -2660,7 +2668,7 @@ static int JS_InitAtoms(JSRuntime *rt) else atom_type = JS_ATOM_TYPE_STRING; len = strlen(p); - if (JS_NewAtomInitImpl(rt, p, len, atom_type) == JS_ATOM_NULL) + if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL) return -1; p = p + len + 1; } @@ -2671,7 +2679,7 @@ static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v) { JSAtomStruct *p; - if (!JS_AtomIsConst(v)) { + if (!__JS_AtomIsConst(v)) { p = rt->atom_array[v]; p->header.ref_count++; } @@ -2683,7 +2691,7 @@ JSAtom JS_DupAtom(JSContext *ctx, JSAtom v) JSRuntime *rt; JSAtomStruct *p; - if (!JS_AtomIsConst(v)) { + if (!__JS_AtomIsConst(v)) { rt = ctx->rt; p = rt->atom_array[v]; p->header.ref_count++; @@ -2697,7 +2705,7 @@ static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v) JSAtomStruct *p; rt = ctx->rt; - if (JS_AtomIsTaggedInt(v)) + if (__JS_AtomIsTaggedInt(v)) return JS_ATOM_KIND_STRING; p = rt->atom_array[v]; switch(p->atom_type) { @@ -2743,7 +2751,7 @@ static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p) /* string case (internal). Return JS_ATOM_NULL if error. 'str' is freed. */ -static JSAtom JS_NewAtomImpl(JSRuntime *rt, JSString *str, int atom_type) +static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) { uint32_t h, h1, i; JSAtomStruct *p; @@ -2758,7 +2766,7 @@ static JSAtom JS_NewAtomImpl(JSRuntime *rt, JSString *str, int atom_type) /* str is the atom, return its index */ i = js_get_atom_index(rt, str); /* reduce string refcount and increase atom's unless constant */ - if (JS_AtomIsConst(i)) + if (__JS_AtomIsConst(i)) str->header.ref_count--; return i; } @@ -2774,7 +2782,7 @@ static JSAtom JS_NewAtomImpl(JSRuntime *rt, JSString *str, int atom_type) p->atom_type == atom_type && p->len == len && js_string_memcmp(p, str, len) == 0) { - if (!JS_AtomIsConst(i)) + if (!__JS_AtomIsConst(i)) p->header.ref_count++; goto done; } @@ -2899,8 +2907,8 @@ static JSAtom JS_NewAtomImpl(JSRuntime *rt, JSString *str, int atom_type) } /* only works with zero terminated 8 bit strings */ -static JSAtom JS_NewAtomInitImpl(JSRuntime *rt, const char *str, int len, - int atom_type) +static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, + int atom_type) { JSString *p; p = js_alloc_string_rt(rt, len, 0); @@ -2908,10 +2916,11 @@ static JSAtom JS_NewAtomInitImpl(JSRuntime *rt, const char *str, int len, return JS_ATOM_NULL; memcpy(p->u.str8, str, len); p->u.str8[len] = '\0'; - return JS_NewAtomImpl(rt, p, atom_type); + return __JS_NewAtom(rt, p, atom_type); } -static JSAtom JS_FindAtom(JSRuntime *rt, const char *str, size_t len, +/* Warning: str must be ASCII only */ +static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, int atom_type) { uint32_t h, h1, i; @@ -2928,7 +2937,7 @@ static JSAtom JS_FindAtom(JSRuntime *rt, const char *str, size_t len, p->len == len && p->is_wide_char == 0 && memcmp(p->u.str8, str, len) == 0) { - if (!JS_AtomIsConst(i)) + if (!__JS_AtomIsConst(i)) p->header.ref_count++; return i; } @@ -2980,7 +2989,7 @@ static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p) assert(rt->atom_count >= 0); } -static void JS_FreeAtomImpl(JSRuntime *rt, uint32_t i) +static void __JS_FreeAtom(JSRuntime *rt, uint32_t i) { JSAtomStruct *p; @@ -2998,19 +3007,21 @@ static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p) if (is_num_string(&n, p)) { if (n <= JS_ATOM_MAX_INT) { js_free_string(rt, p); - return JS_AtomFromUInt32(n); + return __JS_AtomFromUInt32(n); } } /* XXX: should generate an exception */ - return JS_NewAtomImpl(rt, p, JS_ATOM_TYPE_STRING); + return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING); } +/* str is UTF-8 encoded */ 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); + // XXX: this will not work if UTF-8 encoded str contains non ASCII bytes + JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING); if (atom) return atom; } @@ -3028,7 +3039,7 @@ JSAtom JS_NewAtom(JSContext *ctx, const char *str) JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n) { if (n <= JS_ATOM_MAX_INT) { - return JS_AtomFromUInt32(n); + return __JS_AtomFromUInt32(n); } else { char buf[11]; JSValue val; @@ -3036,7 +3047,7 @@ JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n) val = JS_NewString(ctx, buf); if (JS_IsException(val)) return JS_ATOM_NULL; - return JS_NewAtomImpl(ctx->rt, JS_VALUE_GET_STRING(val), + return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), JS_ATOM_TYPE_STRING); } } @@ -3044,7 +3055,7 @@ JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n) static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n) { if ((uint64_t)n <= JS_ATOM_MAX_INT) { - return JS_AtomFromUInt32((uint32_t)n); + return __JS_AtomFromUInt32((uint32_t)n); } else { char buf[24]; JSValue val; @@ -3052,7 +3063,7 @@ static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n) val = JS_NewString(ctx, buf); if (JS_IsException(val)) return JS_ATOM_NULL; - return JS_NewAtomImpl(ctx->rt, JS_VALUE_GET_STRING(val), + return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), JS_ATOM_TYPE_STRING); } } @@ -3062,7 +3073,7 @@ static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type) { JSRuntime *rt = ctx->rt; JSAtom atom; - atom = JS_NewAtomImpl(rt, p, atom_type); + atom = __JS_NewAtom(rt, p, atom_type); if (atom == JS_ATOM_NULL) return JS_ThrowOutOfMemory(ctx); return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]); @@ -3075,7 +3086,7 @@ static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr, JSRuntime *rt = ctx->rt; JSString *p; - assert(!JS_AtomIsTaggedInt(descr)); + assert(!__JS_AtomIsTaggedInt(descr)); assert(descr < rt->atom_size); p = rt->atom_array[descr]; JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); @@ -3088,8 +3099,8 @@ static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr, static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, JSAtom atom) { - if (JS_AtomIsTaggedInt(atom)) { - snprintf(buf, buf_size, "%u", JS_AtomToUInt32(atom)); + if (__JS_AtomIsTaggedInt(atom)) { + snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom)); } else { JSAtomStruct *p; assert(atom < rt->atom_size); @@ -3115,10 +3126,7 @@ static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, 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]; + c = string_get(str, i); if ((q - buf) >= buf_size - UTF8_CHAR_LEN_MAX) break; if (c < 128) { @@ -3139,12 +3147,12 @@ static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom); } -static JSValue JS_AtomToValueImpl(JSContext *ctx, JSAtom atom, BOOL force_string) +static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string) { char buf[ATOM_GET_STR_BUF_SIZE]; - if (JS_AtomIsTaggedInt(atom)) { - snprintf(buf, sizeof(buf), "%u", JS_AtomToUInt32(atom)); + if (__JS_AtomIsTaggedInt(atom)) { + snprintf(buf, sizeof(buf), "%u", __JS_AtomToUInt32(atom)); return JS_NewString(ctx, buf); } else { JSRuntime *rt = ctx->rt; @@ -3168,20 +3176,20 @@ static JSValue JS_AtomToValueImpl(JSContext *ctx, JSAtom atom, BOOL force_string JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom) { - return JS_AtomToValueImpl(ctx, atom, FALSE); + return __JS_AtomToValue(ctx, atom, FALSE); } JSValue JS_AtomToString(JSContext *ctx, JSAtom atom) { - return JS_AtomToValueImpl(ctx, atom, TRUE); + return __JS_AtomToValue(ctx, atom, TRUE); } /* return TRUE if the atom is an array index (i.e. 0 <= index <= 2^32-2 and return its value */ static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom) { - if (JS_AtomIsTaggedInt(atom)) { - *pval = JS_AtomToUInt32(atom); + if (__JS_AtomIsTaggedInt(atom)) { + *pval = __JS_AtomToUInt32(atom); return TRUE; } else { JSRuntime *rt = ctx->rt; @@ -3212,8 +3220,8 @@ static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) int c, len, ret; JSValue num, str; - if (JS_AtomIsTaggedInt(atom)) - return JS_NewInt32(ctx, JS_AtomToUInt32(atom)); + if (__JS_AtomIsTaggedInt(atom)) + return JS_NewInt32(ctx, __JS_AtomToUInt32(atom)); assert(atom < rt->atom_size); p1 = rt->atom_array[atom]; if (p1->atom_type != JS_ATOM_TYPE_STRING) @@ -3255,7 +3263,7 @@ static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) /* -0 case is specific */ if (c == '0' && len == 2) { minus_zero: - return JS_NewFloat64Impl(ctx, -0.0); + return __JS_NewFloat64(ctx, -0.0); } } if (!is_num(c)) { @@ -3300,14 +3308,14 @@ static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom) void JS_FreeAtom(JSContext *ctx, JSAtom v) { - if (!JS_AtomIsConst(v)) - JS_FreeAtomImpl(ctx->rt, v); + if (!__JS_AtomIsConst(v)) + __JS_FreeAtom(ctx->rt, v); } void JS_FreeAtomRT(JSRuntime *rt, JSAtom v) { - if (!JS_AtomIsConst(v)) - JS_FreeAtomImpl(rt, v); + if (!__JS_AtomIsConst(v)) + __JS_FreeAtom(rt, v); } /* return TRUE if 'v' is a symbol with a string description */ @@ -3317,7 +3325,7 @@ static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v) JSAtomStruct *p; rt = ctx->rt; - if (JS_AtomIsTaggedInt(v)) + if (__JS_AtomIsTaggedInt(v)) return FALSE; p = rt->atom_array[v]; return (((p->atom_type == JS_ATOM_TYPE_SYMBOL && @@ -3326,7 +3334,7 @@ static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v) !(p->len == 0 && p->is_wide_char != 0)); } -static maybe_unused void print_atom(JSContext *ctx, JSAtom atom) +static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom) { char buf[ATOM_GET_STR_BUF_SIZE]; const char *p; @@ -3425,19 +3433,37 @@ static inline BOOL JS_IsEmptyString(JSValueConst v) /* JSClass support */ +#ifdef CONFIG_ATOMICS +static pthread_mutex_t js_class_id_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + /* a new class ID is allocated if *pclass_id != 0 */ JSClassID JS_NewClassID(JSClassID *pclass_id) { JSClassID class_id; - /* XXX: make it thread safe */ +#ifdef CONFIG_ATOMICS + pthread_mutex_lock(&js_class_id_mutex); +#endif class_id = *pclass_id; if (class_id == 0) { class_id = js_class_id_alloc++; *pclass_id = class_id; } +#ifdef CONFIG_ATOMICS + pthread_mutex_unlock(&js_class_id_mutex); +#endif return class_id; } +JSClassID JS_GetClassID(JSValue v) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT) + return JS_INVALID_CLASS_ID; + p = JS_VALUE_GET_OBJ(v); + return p->class_id; +} + BOOL JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id) { return (class_id < rt->class_count && @@ -3501,9 +3527,9 @@ int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def) JSAtom name; len = strlen(class_def->class_name); - name = JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); + name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); if (name == JS_ATOM_NULL) { - name = JS_NewAtomInitImpl(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); + name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); if (name == JS_ATOM_NULL) return -1; } @@ -3732,28 +3758,23 @@ 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)) + if (string_buffer_putc16(s, get_hi_surrogate(c))) return -1; - c = (c & 0x3ff) + 0xdc00; + c = get_lo_surrogate(c); } 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) { + if (is_hi_surrogate(c) && idx < p->len) { c1 = p->u.str16[idx]; - if (c1 >= 0xdc00 && c1 < 0xe000) { - c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000; + if (is_lo_surrogate(c1)) { + c = from_surrogate(c, c1); idx++; } } @@ -3951,9 +3972,8 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len) } else if (c <= 0x10FFFF) { p = p_next; /* surrogate pair */ - c -= 0x10000; - string_buffer_putc16(b, (c >> 10) + 0xd800); - c = (c & 0x3ff) + 0xdc00; + string_buffer_putc16(b, get_hi_surrogate(c)); + c = get_lo_surrogate(c); } else { /* invalid char */ c = 0xfffd; @@ -4091,13 +4111,12 @@ const char *JS_ToCStringLen2(JSContext *ctx, size_t *plen, JSValueConst val1, BO if (c < 0x80) { *q++ = c; } else { - if (c >= 0xd800 && c < 0xdc00) { + if (is_hi_surrogate(c)) { if (pos < len && !cesu8) { c1 = src[pos]; - if (c1 >= 0xdc00 && c1 < 0xe000) { + if (is_lo_surrogate(c1)) { pos++; - /* surrogate pair */ - c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000; + c = from_surrogate(c, c1); } else { /* Keep unmatched surrogate code points */ /* c = 0xfffd; */ /* error */ @@ -4130,7 +4149,7 @@ void JS_FreeCString(JSContext *ctx, const char *ptr) if (!ptr) return; /* purposely removing constness */ - p = (JSString *)(void *)(ptr - offsetof(JSString, u)); + p = container_of(ptr, JSString, u); JS_FreeValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); } @@ -4230,7 +4249,43 @@ static JSValue JS_ConcatString1(JSContext *ctx, return JS_MKPTR(JS_TAG_STRING, p); } -/* op1 and op2 are converted to strings. For convience, op1 or op2 = +static BOOL JS_ConcatStringInPlace(JSContext *ctx, JSString *p1, JSValueConst op2) { + if (JS_VALUE_GET_TAG(op2) == JS_TAG_STRING) { + JSString *p2 = JS_VALUE_GET_STRING(op2); + size_t size1; + + if (p2->len == 0) + return TRUE; + if (p1->header.ref_count != 1) + return FALSE; + size1 = js_malloc_usable_size(ctx, p1); + if (p1->is_wide_char) { + if (size1 >= sizeof(*p1) + ((p1->len + p2->len) << 1)) { + if (p2->is_wide_char) { + memcpy(p1->u.str16 + p1->len, p2->u.str16, p2->len << 1); + p1->len += p2->len; + return TRUE; + } else { + size_t i; + for (i = 0; i < p2->len; i++) { + p1->u.str16[p1->len++] = p2->u.str8[i]; + } + return TRUE; + } + } + } else if (!p2->is_wide_char) { + if (size1 >= sizeof(*p1) + p1->len + p2->len + 1) { + memcpy(p1->u.str8 + p1->len, p2->u.str8, p2->len); + p1->len += p2->len; + p1->u.str8[p1->len] = '\0'; + return TRUE; + } + } + } + return FALSE; +} + +/* op1 and op2 are converted to strings. For convenience, op1 or op2 = JS_EXCEPTION are accepted and return JS_EXCEPTION. */ static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) { @@ -4252,27 +4307,11 @@ static JSValue JS_ConcatString(JSContext *ctx, JSValue op1, JSValue op2) } } 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: + if (JS_ConcatStringInPlace(ctx, p1, op2)) { JS_FreeValue(ctx, op2); return op1; } + p2 = JS_VALUE_GET_STRING(op2); ret = JS_ConcatString1(ctx, p1, p2); JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -4509,6 +4548,7 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh, JSShapeProperty *pr; void *sh_alloc; intptr_t h; + JSShape *old_sh; sh = *psh; new_size = max_int(count, sh->prop_size * 3 / 2); @@ -4524,19 +4564,21 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh, new_hash_size = sh->prop_hash_mask + 1; while (new_hash_size < new_size) new_hash_size = 2 * new_hash_size; + /* resize the property shapes. Using js_realloc() is not possible in + case the GC runs during the allocation */ + 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 shape 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); + 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, @@ -4548,20 +4590,12 @@ static no_inline int resize_properties(JSContext *ctx, JSShape **psh, 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); + /* just copy the previous hash table */ + memcpy(prop_hash_end(sh) - new_hash_size, prop_hash_end(old_sh) - new_hash_size, + sizeof(prop_hash_end(sh)[0]) * new_hash_size); } + js_free(ctx, get_alloc_from_shape(old_sh)); *psh = sh; sh->prop_size = new_size; return 0; @@ -4670,7 +4704,7 @@ static int add_shape_property(JSContext *ctx, JSShape **psh, pr = &prop[sh->prop_count++]; pr->atom = JS_DupAtom(ctx, atom); pr->flags = prop_flags; - sh->has_small_array_index |= JS_AtomIsTaggedInt(atom); + sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom); /* add in hash table */ hash_mask = sh->prop_hash_mask; h = atom & hash_mask; @@ -4731,7 +4765,7 @@ static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh, return NULL; } -static maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh) +static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh) { char atom_buf[ATOM_GET_STR_BUF_SIZE]; int j; @@ -4747,7 +4781,7 @@ static maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh) printf("\n"); } -static maybe_unused void JS_DumpShapes(JSRuntime *rt) +static __maybe_unused void JS_DumpShapes(JSRuntime *rt) { int i; JSShape *sh; @@ -4838,10 +4872,8 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas 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; @@ -4858,8 +4890,8 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas case JS_CLASS_BOOLEAN: case JS_CLASS_SYMBOL: case JS_CLASS_DATE: -#ifdef CONFIG_BIGNUM case JS_CLASS_BIG_INT: +#ifdef CONFIG_BIGNUM case JS_CLASS_BIG_FLOAT: case JS_CLASS_BIG_DECIMAL: #endif @@ -4921,8 +4953,8 @@ static JSValue JS_GetObjectData(JSContext *ctx, JSValueConst obj) case JS_CLASS_BOOLEAN: case JS_CLASS_SYMBOL: case JS_CLASS_DATE: -#ifdef CONFIG_BIGNUM case JS_CLASS_BIG_INT: +#ifdef CONFIG_BIGNUM case JS_CLASS_BIG_FLOAT: case JS_CLASS_BIG_DECIMAL: #endif @@ -4945,8 +4977,8 @@ static int JS_SetObjectData(JSContext *ctx, JSValueConst obj, JSValue val) case JS_CLASS_BOOLEAN: case JS_CLASS_SYMBOL: case JS_CLASS_DATE: -#ifdef CONFIG_BIGNUM case JS_CLASS_BIG_INT: +#ifdef CONFIG_BIGNUM case JS_CLASS_BIG_FLOAT: case JS_CLASS_BIG_DECIMAL: #endif @@ -5293,10 +5325,12 @@ static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref) 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 */ + list_del(&var_ref->var_ref_link); /* still on the stack */ + if (var_ref->async_func) + async_func_free(rt, var_ref->async_func); } + remove_gc_object(&var_ref->header); js_free_rt(rt, var_ref); } } @@ -5394,7 +5428,7 @@ static void js_bytecode_function_mark(JSRuntime *rt, JSValueConst val, 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) { + if (var_ref) { mark_func(rt, &var_ref->header); } } @@ -5436,7 +5470,15 @@ 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; + int i; + JS_FreeValueRT(rt, it->obj); + if (!it->is_array) { + for(i = 0; i < it->atom_count; i++) { + JS_FreeAtomRT(rt, it->tab_atom[i].atom); + } + js_free_rt(rt, it->tab_atom); + } js_free_rt(rt, it); } @@ -5504,6 +5546,9 @@ static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp) case JS_GC_OBJ_TYPE_FUNCTION_BYTECODE: free_function_bytecode(rt, (JSFunctionBytecode *)gp); break; + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: + __async_func_free(rt, (JSAsyncFunctionState *)gp); + break; default: abort(); } @@ -5527,7 +5572,7 @@ static void free_zero_refcount(JSRuntime *rt) } /* called with the ref_count of 'v' reaches zero. */ -static void JS_FreeValueRTImpl(JSRuntime *rt, JSValue v) +static void __JS_FreeValueRT(JSRuntime *rt, JSValue v) { uint32_t tag = JS_VALUE_GET_TAG(v); @@ -5573,15 +5618,17 @@ static void JS_FreeValueRTImpl(JSRuntime *rt, JSValue v) case JS_TAG_MODULE: abort(); /* never freed here */ break; -#ifdef CONFIG_BIGNUM case JS_TAG_BIG_INT: +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: +#endif { JSBigFloat *bf = JS_VALUE_GET_PTR(v); bf_delete(&bf->num); js_free_rt(rt, bf); } break; +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_DECIMAL: { JSBigDecimal *bf = JS_VALUE_GET_PTR(v); @@ -5602,9 +5649,9 @@ static void JS_FreeValueRTImpl(JSRuntime *rt, JSValue v) } } -static void JS_FreeValueImpl(JSContext *ctx, JSValue v) +static void __JS_FreeValue(JSContext *ctx, JSValue v) { - JS_FreeValueRTImpl(ctx->rt, v); + __JS_FreeValueRT(ctx->rt, v); } /* garbage collection */ @@ -5660,11 +5707,9 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, 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); - } + /* 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); } @@ -5698,16 +5743,32 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp, 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); + if (var_ref->is_detached) { + JS_MarkValue(rt, *var_ref->pvalue, mark_func); + } else if (var_ref->async_func) { + mark_func(rt, &var_ref->async_func->header); + } } 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); + JSAsyncFunctionState *s = (JSAsyncFunctionState *)gp; + JSStackFrame *sf = &s->frame; + JSValue *sp; + + if (!s->is_completed) { + JS_MarkValue(rt, sf->cur_func, mark_func); + JS_MarkValue(rt, s->this_val, mark_func); + /* sf->cur_sp = NULL if the function is running */ + 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); + } + } JS_MarkValue(rt, s->resolving_funcs[0], mark_func); JS_MarkValue(rt, s->resolving_funcs[1], mark_func); } @@ -5815,12 +5876,13 @@ static void gc_free_cycles(JSRuntime *rt) 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. */ + /* Only need to free the GC object associated with JS values + or async functions. 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: + case JS_GC_OBJ_TYPE_ASYNC_FUNCTION: #ifdef DUMP_GC_FREE if (!header_done) { printf("Freeing cycles:\n"); @@ -5842,7 +5904,8 @@ static void gc_free_cycles(JSRuntime *rt) 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); + p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE || + p->gc_obj_type == JS_GC_OBJ_TYPE_ASYNC_FUNCTION); js_free_rt(rt, p); } @@ -5944,13 +6007,13 @@ static void compute_value_size(JSValueConst val, JSMemoryUsage_helper *hp) case JS_TAG_STRING: compute_jsstring_size(JS_VALUE_GET_STRING(val), hp); break; -#ifdef CONFIG_BIGNUM case JS_TAG_BIG_INT: +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: case JS_TAG_BIG_DECIMAL: +#endif /* should track JSBigFloat usage */ break; -#endif } } @@ -6074,8 +6137,8 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) 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 */ +#ifdef CONFIG_BIGNUM case JS_CLASS_BIG_FLOAT: /* u.object_data */ case JS_CLASS_BIG_DECIMAL: /* u.object_data */ #endif @@ -6167,10 +6230,8 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s) 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 */ @@ -6251,7 +6312,7 @@ void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) "BigNum " #endif CONFIG_VERSION " version, %d-bit, malloc limit: %"PRId64"\n\n", - (int)sizeof(void *) * 8, (int64_t)(ssize_t)s->malloc_limit); + (int)sizeof(void *) * 8, s->malloc_limit); #if 1 if (rt) { static const struct { @@ -6297,10 +6358,10 @@ void JS_DumpMemoryUsage(FILE *fp, const JSMemoryUsage *s, JSRuntime *rt) 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]) { + if (obj_classes[class_id] && class_id < rt->class_count) { 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)); + JS_AtomGetStrRT(rt, buf, sizeof(buf), rt->class_array[class_id].class_name)); } } if (obj_classes[JS_CLASS_INIT_COUNT]) @@ -6397,6 +6458,11 @@ JSValue JS_GetException(JSContext *ctx) return val; } +JS_BOOL JS_HasException(JSContext *ctx) +{ + return !JS_IsNull(ctx->rt->current_exception); +} + static void dbuf_put_leb128(DynBuf *s, uint32_t v) { uint32_t a; @@ -6526,8 +6592,8 @@ static const char *get_func_name(JSContext *ctx, JSValueConst func) /* if filename != NULL, an additional level is added with the filename and line number information (used for parse error). */ void build_backtrace(JSContext *ctx, JSValueConst error_obj, - const char *filename, int line_num, - int backtrace_flags) + const char *filename, int line_num, + int backtrace_flags) { JSStackFrame *sf; JSValue str; @@ -6698,7 +6764,7 @@ static int FORMAT_ATTR(3, 4) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, } /* never use it directly */ -static JSValue FORMAT_ATTR(3, 4) JS_ThrowTypeErrorAtomImpl(JSContext *ctx, JSAtom atom, const char *fmt, ...) +static JSValue FORMAT_ATTR(3, 4) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) { char buf[ATOM_GET_STR_BUF_SIZE]; return JS_ThrowTypeError(ctx, fmt, @@ -6706,7 +6772,7 @@ static JSValue FORMAT_ATTR(3, 4) JS_ThrowTypeErrorAtomImpl(JSContext *ctx, JSAto } /* never use it directly */ -static JSValue FORMAT_ATTR(3, 4) JS_ThrowSyntaxErrorAtomImpl(JSContext *ctx, JSAtom atom, const char *fmt, ...) +static JSValue FORMAT_ATTR(3, 4) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) { char buf[ATOM_GET_STR_BUF_SIZE]; return JS_ThrowSyntaxError(ctx, fmt, @@ -6715,8 +6781,8 @@ static JSValue FORMAT_ATTR(3, 4) JS_ThrowSyntaxErrorAtomImpl(JSContext *ctx, JSA /* %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, "") +#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "") +#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "") static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom) { @@ -6826,7 +6892,7 @@ static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id) return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name); } -static no_inline warn_unused int js_poll_interrupts_impl(JSContext *ctx) +static no_inline __exception int __js_poll_interrupts(JSContext *ctx) { JSRuntime *rt = ctx->rt; ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; @@ -6841,10 +6907,10 @@ static no_inline warn_unused int js_poll_interrupts_impl(JSContext *ctx) return 0; } -static inline warn_unused int js_poll_interrupts(JSContext *ctx) +static inline __exception int js_poll_interrupts(JSContext *ctx) { if (unlikely(--ctx->interrupt_counter <= 0)) { - return js_poll_interrupts_impl(ctx); + return __js_poll_interrupts(ctx); } else { return 0; } @@ -6931,10 +6997,10 @@ int JS_SetPrototype(JSContext *ctx, JSValueConst obj, JSValueConst proto_val) 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; +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: val = ctx->class_proto[JS_CLASS_BIG_FLOAT]; break; @@ -7148,9 +7214,9 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, case JS_TAG_STRING: { JSString *p1 = JS_VALUE_GET_STRING(obj); - if (JS_AtomIsTaggedInt(prop)) { + if (__JS_AtomIsTaggedInt(prop)) { uint32_t idx, ch; - idx = JS_AtomToUInt32(prop); + idx = __JS_AtomToUInt32(prop); if (idx < p1->len) { if (p1->is_wide_char) ch = p1->u.str16[idx]; @@ -7208,8 +7274,8 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, if (unlikely(p->is_exotic)) { /* exotic behaviors */ if (p->fast_array) { - if (JS_AtomIsTaggedInt(prop)) { - uint32_t idx = JS_AtomToUInt32(prop); + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); if (idx < p->u.array.count) { /* we avoid duplicating the code */ return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); @@ -7371,6 +7437,8 @@ static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj, return 0; } +/* add a private brand field to 'home_obj' if not already present and + if obj is != null add a private brand to it */ static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj) { JSObject *p, *p1; @@ -7386,10 +7454,10 @@ static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj) p = JS_VALUE_GET_OBJ(home_obj); prs = find_own_property(&pr, p, JS_ATOM_Private_brand); if (!prs) { + /* if the brand is not present, add it */ 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); @@ -7401,20 +7469,27 @@ static int JS_AddBrand(JSContext *ctx, JSValueConst obj, JSValueConst home_obj) } brand_atom = js_symbol_to_atom(ctx, brand); - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { - JS_ThrowTypeErrorNotAnObject(ctx); + if (JS_IsObject(obj)) { + p1 = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p1, brand_atom); + if (unlikely(prs)) { + JS_FreeAtom(ctx, brand_atom); + JS_ThrowTypeError(ctx, "private method is already present"); + return -1; + } + 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; + } else { 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; } +/* return a boolean telling if the brand of the home object of 'func' + is present on 'obj' or -1 in case of exception */ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func) { JSObject *p, *p1, *home_obj; @@ -7423,11 +7498,8 @@ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func) 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; - } + if (unlikely(JS_VALUE_GET_TAG(func) != JS_TAG_OBJECT)) + goto not_obj; p1 = JS_VALUE_GET_OBJ(func); if (!js_class_has_bytecode(p1->class_id)) goto not_obj; @@ -7445,15 +7517,14 @@ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func) 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"); + if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) { + not_obj: + JS_ThrowTypeErrorNotAnObject(ctx); return -1; } - return 0; + p = JS_VALUE_GET_OBJ(obj); + prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, brand)); + return (prs != NULL); } static uint32_t js_string_obj_get_length(JSContext *ctx, @@ -7503,7 +7574,7 @@ static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len) /* return < 0 in case if exception, 0 if OK. ptab and its atoms must be freed by the user. */ -static int warn_unused JS_GetOwnPropertyNamesInternal(JSContext *ctx, +static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, JSPropertyEnum **ptab, uint32_t *plen, JSObject *p, int flags) @@ -7653,7 +7724,7 @@ static int warn_unused JS_GetOwnPropertyNamesInternal(JSContext *ctx, len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); add_array_keys: for(i = 0; i < len; i++) { - tab_atom[num_index].atom = JS_AtomFromUInt32(i); + tab_atom[num_index].atom = __JS_AtomFromUInt32(i); if (tab_atom[num_index].atom == JS_ATOM_NULL) { js_free_prop_enum(ctx, tab_atom, num_index); return -1; @@ -7761,9 +7832,9 @@ retry: if (p->is_exotic) { if (p->fast_array) { /* specific case for fast arrays */ - if (JS_AtomIsTaggedInt(prop)) { + if (__JS_AtomIsTaggedInt(prop)) { uint32_t idx; - idx = JS_AtomToUInt32(prop); + idx = __JS_AtomToUInt32(prop); if (idx < p->u.array.count) { if (desc) { desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | @@ -7883,7 +7954,7 @@ JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val) if (tag == JS_TAG_INT && (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) { /* fast path for integer values */ - atom = JS_AtomFromUInt32(JS_VALUE_GET_INT(val)); + atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val)); } else if (tag == JS_TAG_SYMBOL) { JSAtomStruct *p = JS_VALUE_GET_PTR(val); atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p)); @@ -7910,40 +7981,46 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, 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; + uint32_t idx; /* 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: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return JS_DupValue(ctx, p->u.array.u.values[idx]); case JS_CLASS_INT8_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return JS_NewInt32(ctx, p->u.array.u.int8_ptr[idx]); case JS_CLASS_UINT8C_ARRAY: case JS_CLASS_UINT8_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return JS_NewInt32(ctx, p->u.array.u.uint8_ptr[idx]); case JS_CLASS_INT16_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return JS_NewInt32(ctx, p->u.array.u.int16_ptr[idx]); case JS_CLASS_UINT16_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return JS_NewInt32(ctx, p->u.array.u.uint16_ptr[idx]); case JS_CLASS_INT32_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return JS_NewInt32(ctx, p->u.array.u.int32_ptr[idx]); case JS_CLASS_UINT32_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return JS_NewUint32(ctx, p->u.array.u.uint32_ptr[idx]); -#ifdef CONFIG_BIGNUM case JS_CLASS_BIG_INT64_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; return JS_NewBigInt64(ctx, p->u.array.u.int64_ptr[idx]); case JS_CLASS_BIG_UINT64_ARRAY: + if (unlikely(idx >= p->u.array.count)) goto slow_path; 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]); + if (unlikely(idx >= p->u.array.count)) goto slow_path; + return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]); case JS_CLASS_FLOAT64_ARRAY: - return JS_NewFloat64Impl(ctx, p->u.array.u.double_ptr[idx]); + if (unlikely(idx >= p->u.array.count)) goto slow_path; + return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]); default: goto slow_path; } @@ -7978,7 +8055,7 @@ static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) { /* fast path */ - present = JS_HasProperty(ctx, obj, JS_AtomFromUInt32(idx)); + present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx)); if (present > 0) { val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); if (unlikely(JS_IsException(val))) @@ -8075,7 +8152,7 @@ static JSProperty *add_property(JSContext *ctx, /* 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, +static no_inline __exception int convert_fast_array_to_array(JSContext *ctx, JSObject *p) { JSProperty *pr; @@ -8097,8 +8174,8 @@ static no_inline warn_unused int convert_fast_array_to_array(JSContext *ctx, tab = p->u.array.u.values; for(i = 0; i < len; i++) { /* add_property cannot fail here but - JS_AtomFromUInt32(i) fails for i > INT32_MAX */ - pr = add_property(ctx, p, JS_AtomFromUInt32(i), JS_PROP_C_W_E); + __JS_AtomFromUInt32(i) fails for i > INT32_MAX */ + pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E); pr->u.value = *tab++; } js_free(ctx, p->u.array.u.values); @@ -8203,7 +8280,7 @@ static int call_setter(JSContext *ctx, JSObject *setter, func = JS_MKPTR(JS_TAG_OBJECT, setter); /* Note: the field could be removed in the setter */ func = JS_DupValue(ctx, func); - ret = JS_CallFree(ctx, func, this_obj, 1, &val); + ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst *)&val); JS_FreeValue(ctx, val); if (JS_IsException(ret)) return -1; @@ -8360,126 +8437,45 @@ static int add_fast_array_element(JSContext *ctx, JSObject *p, return TRUE; } -static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc) +/* Allocate a new fast array. Its 'length' property is set to zero. It + maximum size is 2^31-1 elements. For convenience, 'len' is a 64 bit + integer. WARNING: the content of the array is not initialized. */ +static JSValue js_allocate_fast_array(JSContext *ctx, int64_t len) { - 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; + JSValue arr; 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); - } + if (len > INT32_MAX) + return JS_ThrowRangeError(ctx, "invalid array length"); + arr = JS_NewArray(ctx); + if (JS_IsException(arr)) + return arr; + if (len > 0) { + p = JS_VALUE_GET_OBJ(arr); + if (expand_fast_array(ctx, p, len) < 0) { + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; } - ret = JS_DefineProperty(ctx, this_obj, prop, val, - JS_UNDEFINED, JS_UNDEFINED, - JS_PROP_HAS_VALUE); - JS_FreeValue(ctx, val); - return ret; + p->u.array.count = len; } + return arr; +} - 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; +static void js_free_desc(JSContext *ctx, JSPropertyDescriptor *desc) +{ + JS_FreeValue(ctx, desc->getter); + JS_FreeValue(ctx, desc->setter); + JS_FreeValue(ctx, desc->value); } /* 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) + the new property is not added and an error is raised. 'this_obj' is + the receiver. If obj != this_obj, then obj must be an object + (Reflect.set case). */ +int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, + JSAtom prop, JSValue val, JSValueConst this_obj, int flags) { JSObject *p, *p1; JSShapeProperty *prs; @@ -8492,25 +8488,37 @@ int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj, #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 */ + if (JS_VALUE_GET_TAG(obj) == JS_TAG_OBJECT) { p = NULL; - p1 = JS_VALUE_GET_OBJ(JS_GetPrototypePrimitive(ctx, this_obj)); + p1 = JS_VALUE_GET_OBJ(obj); goto prototype_lookup; + } else { + 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, obj)); + goto prototype_lookup; + } } + } else { + p = JS_VALUE_GET_OBJ(this_obj); + p1 = JS_VALUE_GET_OBJ(obj); + if (unlikely(p != p1)) + goto retry2; } - p = JS_VALUE_GET_OBJ(this_obj); -retry: - prs = find_own_property(&pr, p, prop); + + /* fast path if obj == this_obj */ + retry: + prs = find_own_property(&pr, p1, prop); if (prs) { if (likely((prs->flags & (JS_PROP_TMASK | JS_PROP_WRITABLE | JS_PROP_LENGTH)) == JS_PROP_WRITABLE)) { @@ -8543,12 +8551,11 @@ retry: } } - p1 = p; for(;;) { if (p1->is_exotic) { if (p1->fast_array) { - if (JS_AtomIsTaggedInt(prop)) { - uint32_t idx = JS_AtomToUInt32(prop); + if (__JS_AtomIsTaggedInt(prop)) { + uint32_t idx = __JS_AtomToUInt32(prop); if (idx < p1->u.array.count) { if (unlikely(p == p1)) return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags); @@ -8567,11 +8574,19 @@ retry: 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"); + /* must convert the argument even if out of bound access */ + if (p1->class_id == JS_CLASS_BIG_INT64_ARRAY || + p1->class_id == JS_CLASS_BIG_UINT64_ARRAY) { + int64_t v; + if (JS_ToBigInt64Free(ctx, &v, val)) + return -1; + } else { + val = JS_ToNumberFree(ctx, val); + JS_FreeValue(ctx, val); + if (JS_IsException(val)) + return -1; + } + return TRUE; } } } else { @@ -8643,9 +8658,7 @@ retry: return -1; goto retry2; } else if (!(prs->flags & JS_PROP_WRITABLE)) { - read_only_prop: - JS_FreeValue(ctx, val); - return JS_ThrowTypeErrorReadOnly(ctx, flags, prop); + goto read_only_prop; } } } @@ -8666,17 +8679,57 @@ retry: 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); + if (likely(p == JS_VALUE_GET_OBJ(obj))) { + 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 { goto generic_create_prop; } } else { + 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; + } + } else { + /* generic case: 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_prop: + 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; + } else { generic_create_prop: ret = JS_CreateProperty(ctx, p, prop, val, JS_UNDEFINED, JS_UNDEFINED, flags | @@ -8689,14 +8742,6 @@ retry: 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 */ @@ -8720,7 +8765,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, JSShape *sh1; /* fast path to add an element to the array */ - if (idx != p->u.array.count || + if (idx != (uint32_t)p->u.array.count || !p->fast_array || !p->extensible) goto slow_path; /* check if prototype chain has a numeric property */ @@ -8781,7 +8826,6 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, 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 */ @@ -8794,7 +8838,6 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, p->u.array.u.uint64_ptr[idx] = v; } break; -#endif case JS_CLASS_FLOAT32_ARRAY: if (JS_ToFloat64Free(ctx, &d, val)) return -1; @@ -8807,7 +8850,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, 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"); + return TRUE; } p->u.array.u.double_ptr[idx] = d; break; @@ -8825,7 +8868,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, JS_FreeValue(ctx, val); return -1; } - ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, flags); + ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, this_obj, flags); JS_FreeAtom(ctx, atom); return ret; } @@ -8865,7 +8908,7 @@ int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj, JSAtom atom; int ret; atom = JS_NewAtom(ctx, prop); - ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, JS_PROP_THROW); + ret = JS_SetPropertyInternal(ctx, this_obj, atom, val, this_obj, JS_PROP_THROW); JS_FreeAtom(ctx, atom); return ret; } @@ -8895,8 +8938,8 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p, uint32_t idx, len; if (p->fast_array) { - if (JS_AtomIsTaggedInt(prop)) { - idx = JS_AtomToUInt32(prop); + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); if (idx == p->u.array.count) { if (!p->extensible) goto not_extensible; @@ -9205,15 +9248,19 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, spaces. */ if (!js_same_value(ctx, val, *pr->u.var_ref->pvalue)) goto not_configurable; + } else { + /* update the reference */ + set_value(ctx, pr->u.var_ref->pvalue, + JS_DupValue(ctx, val)); } - /* 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 (p->class_id == JS_CLASS_MODULE_NS) { + return JS_ThrowTypeErrorOrFalse(ctx, flags, "module namespace properties have writable = false"); + } if (js_shape_prepare_update(ctx, p, &prs)) return -1; val1 = JS_DupValue(ctx, *pr->u.var_ref->pvalue); @@ -9272,8 +9319,8 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, uint32_t idx; uint32_t prop_flags; if (p->class_id == JS_CLASS_ARRAY) { - if (JS_AtomIsTaggedInt(prop)) { - idx = JS_AtomToUInt32(prop); + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); if (idx < p->u.array.count) { prop_flags = get_prop_flags(flags, JS_PROP_C_W_E); if (prop_flags != JS_PROP_C_W_E) @@ -9296,7 +9343,7 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, JSValue num; int ret; - if (!JS_AtomIsTaggedInt(prop)) { + if (!__JS_AtomIsTaggedInt(prop)) { /* slow path with to handle all numeric indexes */ num = JS_AtomIsNumericIndex1(ctx, prop); if (JS_IsUndefined(num)) @@ -9317,12 +9364,12 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, if (ret) { return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array"); } - if (!JS_AtomIsTaggedInt(prop)) + if (!__JS_AtomIsTaggedInt(prop)) goto typed_array_oob; } - idx = JS_AtomToUInt32(prop); + idx = __JS_AtomToUInt32(prop); /* if the typed array is detached, p->u.array.count = 0 */ - if (idx >= typed_array_get_length(ctx, p)) { + if (idx >= p->u.array.count) { typed_array_oob: return JS_ThrowTypeErrorOrFalse(ctx, flags, "out-of-bound index in typed array"); } @@ -9726,11 +9773,11 @@ static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, 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, result.scope, flags); } } - return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags); + return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, ctx->global_obj, flags); } /* return -1, FALSE or TRUE. return FALSE if not configurable or @@ -9765,7 +9812,7 @@ int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int fl if ((uint64_t)idx <= JS_ATOM_MAX_INT) { /* fast path for fast arrays */ - return JS_DeleteProperty(ctx, obj, JS_AtomFromUInt32(idx), flags); + return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags); } prop = JS_NewAtomInt64(ctx, idx); if (prop == JS_ATOM_NULL) @@ -9894,12 +9941,6 @@ void *JS_GetOpaque2(JSContext *ctx, JSValueConst obj, JSClassID 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; @@ -9933,7 +9974,7 @@ static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint) break; } arg = JS_AtomToString(ctx, atom); - ret = JS_CallFree(ctx, method, val, 1, &arg); + ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg); JS_FreeValue(ctx, arg); if (JS_IsException(ret)) goto exception; @@ -10015,9 +10056,10 @@ static int JS_ToBoolFree(JSContext *ctx, JSValue val) JS_FreeValue(ctx, val); return ret; } -#ifdef CONFIG_BIGNUM case JS_TAG_BIG_INT: +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: +#endif { JSBigFloat *p = JS_VALUE_GET_PTR(val); BOOL ret; @@ -10025,6 +10067,7 @@ static int JS_ToBoolFree(JSContext *ctx, JSValue val) JS_FreeValue(ctx, val); return ret; } +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_DECIMAL: { JSBigDecimal *p = JS_VALUE_GET_PTR(val); @@ -10094,12 +10137,13 @@ static inline int to_digit(int c) } /* XXX: remove */ -static double js_strtod(const char *p, int radix, BOOL is_float) +static double js_strtod(const char *str, int radix, BOOL is_float) { double d; int c; if (!is_float || radix != 10) { + const char *p = str; uint64_t n_max, n; int int_exp, is_neg; @@ -10126,6 +10170,8 @@ static double js_strtod(const char *p, int radix, BOOL is_float) if (n <= n_max) { n = n * radix + c; } else { + if (radix == 10) + goto strtod_case; int_exp++; } p++; @@ -10137,7 +10183,8 @@ static double js_strtod(const char *p, int radix, BOOL is_float) if (is_neg) d = -d; } else { - d = safe_strtod(p, NULL); + strtod_case: + d = safe_strtod(str, NULL); } return d; } @@ -10155,15 +10202,16 @@ static double js_strtod(const char *p, int radix, BOOL is_float) #define ATOD_TYPE_MASK (3 << 7) #define ATOD_TYPE_FLOAT64 (0 << 7) #define ATOD_TYPE_BIG_INT (1 << 7) +#ifdef CONFIG_BIGNUM #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) +#endif /* 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) { @@ -10179,10 +10227,15 @@ static JSValue js_string_to_bigint(JSContext *ctx, const char *buf, JS_FreeValue(ctx, val); return JS_ThrowOutOfMemory(ctx); } +#ifdef CONFIG_BIGNUM val = JS_CompactBigInt1(ctx, val, (flags & ATOD_MODE_BIGINT) != 0); +#else + val = JS_CompactBigInt1(ctx, val, FALSE); +#endif return val; } +#ifdef CONFIG_BIGNUM static JSValue js_string_to_bigfloat(JSContext *ctx, const char *buf, int radix, int flags, slimb_t *pexponent) { @@ -10228,7 +10281,6 @@ static JSValue js_string_to_bigdecimal(JSContext *ctx, const char *buf, } return val; } - #endif /* return an exception in case of memory error. Return JS_NAN if @@ -10303,8 +10355,11 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, } else { no_radix_prefix: if (!(flags & ATOD_INT_ONLY) && - (atod_type == ATOD_TYPE_FLOAT64 || - atod_type == ATOD_TYPE_BIG_FLOAT) && + (atod_type == ATOD_TYPE_FLOAT64 +#ifdef CONFIG_BIGNUM + || atod_type == ATOD_TYPE_BIG_FLOAT +#endif + ) && strstart(p, "Infinity", &p)) { #ifdef CONFIG_BIGNUM if (atod_type == ATOD_TYPE_BIG_FLOAT) { @@ -10350,8 +10405,11 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) { const char *p1 = p + 1; is_float = TRUE; - if (*p1 == '+' || *p1 == '-') + if (*p1 == '+') { + p1++; + } else if (*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]))) @@ -10381,36 +10439,40 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, } 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') { + } else +#ifdef CONFIG_BIGNUM + 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 (flags & ATOD_MODE_BIGINT) { + if (!is_float) + atod_type = ATOD_TYPE_BIG_INT; + if (has_legacy_octal) + goto fail; + } else +#endif + { + if (is_float && radix != 10) + goto fail; } } else { if (atod_type == ATOD_TYPE_FLOAT64) { +#ifdef CONFIG_BIGNUM if (flags & ATOD_MODE_BIGINT) { if (!is_float) atod_type = ATOD_TYPE_BIG_INT; if (has_legacy_octal) goto fail; - } else { + } else +#endif + { if (is_float && radix != 10) goto fail; } @@ -10431,6 +10493,7 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, goto fail; val = ctx->rt->bigint_ops.from_string(ctx, buf, radix, flags, NULL); break; +#ifdef CONFIG_BIGNUM case ATOD_TYPE_BIG_FLOAT: if (has_legacy_octal) goto fail; @@ -10442,19 +10505,10 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, goto fail; val = ctx->rt->bigdecimal_ops.from_string(ctx, buf, radix, flags, NULL); break; +#endif 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) @@ -10492,18 +10546,18 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, redo: tag = JS_VALUE_GET_NORM_TAG(val); switch(tag) { -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_DECIMAL: + case JS_TAG_BIG_INT: if (flag != TON_FLAG_NUMERIC) { JS_FreeValue(ctx, val); - return JS_ThrowTypeError(ctx, "cannot convert bigdecimal to number"); + return JS_ThrowTypeError(ctx, "cannot convert bigint to number"); } ret = val; break; - case JS_TAG_BIG_INT: +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_DECIMAL: if (flag != TON_FLAG_NUMERIC) { JS_FreeValue(ctx, val); - return JS_ThrowTypeError(ctx, "cannot convert bigint to number"); + return JS_ThrowTypeError(ctx, "cannot convert bigdecimal to number"); } ret = val; break; @@ -10586,7 +10640,7 @@ 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, +static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) { double d; @@ -10605,9 +10659,10 @@ static warn_unused int JS_ToFloat64FreeImpl(JSContext *ctx, double *pres, case JS_TAG_FLOAT64: d = JS_VALUE_GET_FLOAT64(val); break; -#ifdef CONFIG_BIGNUM case JS_TAG_BIG_INT: +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: +#endif { JSBigFloat *p = JS_VALUE_GET_PTR(val); /* XXX: there can be a double rounding issue with some @@ -10617,7 +10672,6 @@ static warn_unused int JS_ToFloat64FreeImpl(JSContext *ctx, double *pres, JS_FreeValue(ctx, val); } break; -#endif default: abort(); } @@ -10637,7 +10691,7 @@ static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) *pres = JS_VALUE_GET_FLOAT64(val); return 0; } else { - return JS_ToFloat64FreeImpl(ctx, pres, val); + return __JS_ToFloat64Free(ctx, pres, val); } } @@ -10652,7 +10706,7 @@ static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val) } /* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ -static maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) +static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) { uint32_t tag; JSValue ret; @@ -10685,6 +10739,10 @@ static maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) BOOL is_nan; a = JS_ToBigFloat(ctx, &a_s, val); + if (!a) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } if (!bf_is_finite(a)) { is_nan = bf_is_nan(a); if (is_nan) @@ -10815,7 +10873,7 @@ static int JS_ToInt64SatFree(JSContext *ctx, int64_t *pres, JSValue val) } else { if (d < INT64_MIN) *pres = INT64_MIN; - else if (d > INT64_MAX) + else if (d >= 0x1p63) /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */ *pres = INT64_MAX; else *pres = (int64_t)d; @@ -11067,7 +11125,7 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) return 0; } -static warn_unused int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, +static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, JSValue val, BOOL is_array_ctor) { uint32_t tag, len; @@ -11085,9 +11143,10 @@ static warn_unused int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, len = v; } break; -#ifdef CONFIG_BIGNUM case JS_TAG_BIG_INT: +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: +#endif { JSBigFloat *p = JS_VALUE_GET_PTR(val); bf_t a; @@ -11102,11 +11161,12 @@ static warn_unused int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, goto fail; } break; -#endif default: if (JS_TAG_IS_FLOAT64(tag)) { double d; d = JS_VALUE_GET_FLOAT64(val); + if (!(d >= 0 && d <= UINT32_MAX)) + goto fail; len = (uint32_t)d; if (len != d) goto fail; @@ -11169,7 +11229,7 @@ int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val) /* convert a value to a length between 0 and MAX_SAFE_INTEGER. return -1 for exception */ -static warn_unused int JS_ToLengthFree(JSContext *ctx, int64_t *plen, +static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen, JSValue val) { int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0); @@ -11206,13 +11266,13 @@ static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val) 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); } +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: { JSBigFloat *p = JS_VALUE_GET_PTR(val); @@ -11231,8 +11291,6 @@ static BOOL JS_NumberIsNegativeOrMinusZero(JSContext *ctx, JSValueConst val) } } -#ifdef CONFIG_BIGNUM - static JSValue js_bigint_to_string1(JSContext *ctx, JSValueConst val, int radix) { JSValue ret; @@ -11262,6 +11320,8 @@ static JSValue js_bigint_to_string(JSContext *ctx, JSValueConst val) return js_bigint_to_string1(ctx, val, 10); } +#ifdef CONFIG_BIGNUM + static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix, limb_t prec, bf_flags_t flags) { @@ -11274,6 +11334,10 @@ static JSValue js_ftoa(JSContext *ctx, JSValueConst val1, int radix, if (JS_IsException(val)) return val; a = JS_ToBigFloat(ctx, &a_s, val); + if (!a) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } saved_sign = a->sign; if (a->expn == BF_EXP_ZERO) a->sign = 0; @@ -11330,6 +11394,8 @@ static JSValue js_bigdecimal_to_string1(JSContext *ctx, JSValueConst val, int saved_sign; a = JS_ToBigDecimal(ctx, val); + if (!a) + return JS_EXCEPTION; saved_sign = a->sign; if (a->expn == BF_EXP_ZERO) a->sign = 0; @@ -11351,6 +11417,8 @@ static JSValue js_bigdecimal_to_string(JSContext *ctx, JSValueConst val) #endif /* CONFIG_BIGNUM */ /* 2 <= base <= 36 */ +static char const digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz"; + static char *i64toa(char *buf_end, int64_t n, unsigned int base) { char *q = buf_end; @@ -11362,15 +11430,20 @@ static char *i64toa(char *buf_end, int64_t n, unsigned int base) 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 (base == 10) { + /* division by known base uses multiplication */ + do { + digit = (uint64_t)n % 10; + n = (uint64_t)n / 10; + *--q = '0' + digit; + } while (n != 0); + } else { + do { + digit = (uint64_t)n % base; + n = (uint64_t)n / base; + *--q = digits[digit]; + } while (n != 0); + } if (is_neg) *--q = '-'; return q; @@ -11460,20 +11533,20 @@ static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf, return n_digits; } -static int js_fcvt1(char *buf, int buf_size, double d, int n_digits, +static int js_fcvt1(char (*buf)[JS_DTOA_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); + n = snprintf(*buf, sizeof(*buf), "%.*f", n_digits, d); if (rounding_mode != FE_TONEAREST) fesetround(FE_TONEAREST); - assert(n < buf_size); + assert(n < sizeof(*buf)); return n; } -static void js_fcvt(char *buf, int buf_size, double d, int n_digits) +static void js_fcvt(char (*buf)[JS_DTOA_BUF_SIZE], double d, int n_digits) { int rounding_mode; rounding_mode = FE_TONEAREST; @@ -11487,12 +11560,12 @@ static void js_fcvt(char *buf, int buf_size, double d, int n_digits) 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); + n1 = js_fcvt1(&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); + n1 = js_fcvt1(&buf1, d, n_digits + 1, FE_DOWNWARD); + n2 = js_fcvt1(&buf2, d, n_digits + 1, FE_UPWARD); if (n1 == n2 && memcmp(buf1, buf2, n1) == 0) { /* exact result: round away from zero */ if (buf1[0] == '-') @@ -11503,7 +11576,7 @@ static void js_fcvt(char *buf, int buf_size, double d, int n_digits) } } #endif /* CONFIG_PRINTF_RNDN */ - js_fcvt1(buf, buf_size, d, n_digits, rounding_mode); + js_fcvt1(buf, d, n_digits, rounding_mode); } /* radix != 10 is only supported with flags = JS_DTOA_VAR_FORMAT */ @@ -11519,33 +11592,35 @@ static void js_fcvt(char *buf, int buf_size, double d, int n_digits) /* 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) +static void js_dtoa1(char (*buf)[JS_DTOA_BUF_SIZE], double d, + int radix, int n_digits, int flags) { char *q; if (!isfinite(d)) { if (isnan(d)) { - strcpy(buf, "NaN"); + pstrcpy(*buf, sizeof(*buf), "NaN"); + } else if (d < 0) { + pstrcpy(*buf, sizeof(*buf), "-Infinity"); } else { - q = buf; - if (d < 0) - *q++ = '-'; - strcpy(q, "Infinity"); + pstrcpy(*buf, sizeof(*buf), "Infinity"); } } else if (flags == JS_DTOA_VAR_FORMAT) { int64_t i64; char buf1[70], *ptr; + if (d > (double)MAX_SAFE_INTEGER || d < (double)-MAX_SAFE_INTEGER) + goto generic_conv; i64 = (int64_t)d; - if (d != i64 || i64 > MAX_SAFE_INTEGER || i64 < -MAX_SAFE_INTEGER) + if (d != i64) goto generic_conv; /* fast path for integers */ ptr = i64toa(buf1 + sizeof(buf1), i64, radix); - strcpy(buf, ptr); + pstrcpy(*buf, sizeof(*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); + js_fcvt(buf, d, n_digits); } else { char buf1[JS_DTOA_BUF_SIZE]; int sign, decpt, k, n, i, p, n_max; @@ -11560,7 +11635,7 @@ static void js_dtoa1(char *buf, double d, int radix, int n_digits, int flags) /* 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; + q = *buf; if (sign) *q++ = '-'; if (flags & JS_DTOA_FORCE_EXP) @@ -11602,7 +11677,7 @@ static void js_dtoa1(char *buf, double d, int radix, int n_digits, int flags) p = n - 1; if (p >= 0) *q++ = '+'; - sprintf(q, "%d", p); + snprintf(q, *buf + sizeof(*buf) - q, "%d", p); } } } @@ -11612,10 +11687,84 @@ 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); + js_dtoa1(&buf, d, radix, n_digits, flags); return JS_NewString(ctx, buf); } +static JSValue js_dtoa_radix(JSContext *ctx, double d, int radix) +{ + char buf[2200], *ptr, *ptr2; + /* d is finite */ + int sign = d < 0; + int digit; + double frac, d0; + int64_t n0 = 0; + d = fabs(d); + d0 = trunc(d); + frac = d - d0; + ptr = buf + 1100; + *ptr = '\0'; + if (d0 <= MAX_SAFE_INTEGER) { + int64_t n = n0 = (int64_t)d0; + while (n >= radix) { + digit = n % radix; + n = n / radix; + *--ptr = digits[digit]; + } + *--ptr = digits[(int)n]; + } else { + /* no decimals */ + while (d0 >= radix) { + digit = fmod(d0, radix); + d0 = trunc(d0 / radix); + if (d0 >= MAX_SAFE_INTEGER) + digit = 0; + *--ptr = digits[digit]; + } + *--ptr = digits[(int)d0]; + goto done; + } + if (frac != 0) { + double log2_radix = log2(radix); + double prec = 1023 + 51; // handle subnormals + ptr2 = buf + 1100; + *ptr2++ = '.'; + while (frac != 0 && n0 <= MAX_SAFE_INTEGER/2 && prec > 0) { + frac *= radix; + digit = trunc(frac); + frac -= digit; + *ptr2++ = digits[digit]; + n0 = n0 * radix + digit; + prec -= log2_radix; + } + *ptr2 = '\0'; + if (frac * radix >= radix / 2) { + char nine = digits[radix - 1]; + // round to closest + while (ptr2[-1] == nine) + *--ptr2 = '\0'; + if (ptr2[-1] == '.') { + *--ptr2 = '\0'; + while (ptr2[-1] == nine) + *--ptr2 = '0'; + } + if (ptr2 - 1 == ptr) + *--ptr = '1'; + else + ptr2[-1] += 1; + } else { + while (ptr2[-1] == '0') + *--ptr2 = '\0'; + if (ptr2[-1] == '.') + *--ptr2 = '\0'; + } + } +done: + ptr[-1] = '-'; + ptr -= sign; + return JS_NewString(ctx, ptr); +} + JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToPropertyKey) { uint32_t tag; @@ -11662,9 +11811,9 @@ JSValue JS_ToStringInternal(JSContext *ctx, JSValueConst val, BOOL is_ToProperty 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); +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: return ctx->rt->bigfloat_ops.to_string(ctx, val); case JS_TAG_BIG_DECIMAL: @@ -11756,7 +11905,7 @@ static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1) goto fail; break; default: - if (c < 32 || (c >= 0xd800 && c < 0xe000)) { + if (c < 32 || is_surrogate(c)) { snprintf(buf, sizeof(buf), "\\u%04x", c); if (string_buffer_puts8(b, buf)) goto fail; @@ -11777,14 +11926,14 @@ static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1) return JS_EXCEPTION; } -static maybe_unused void JS_DumpObjectHeader(JSRuntime *rt) +static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt) { printf("%14s %4s %4s %14s %10s %s\n", "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS"); } /* for debug only: dump an object without side effect */ -static maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) +static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) { uint32_t i; char atom_buf[ATOM_GET_STR_BUF_SIZE]; @@ -11825,10 +11974,8 @@ static maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) 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: { @@ -11861,7 +12008,7 @@ static maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) printf("[autoinit %p %d %p]", (void *)js_autoinit_get_realm(pr), js_autoinit_get_id(pr), - pr->u.init.opaque); + (void *)pr->u.init.opaque); } else { JS_DumpValueShort(rt, pr->u.value); } @@ -11890,7 +12037,7 @@ static maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) printf("\n"); } -static maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) +static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) { if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { JS_DumpObject(rt, (JSObject *)p); @@ -11922,7 +12069,7 @@ static maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) } } -static maybe_unused void JS_DumpValueShort(JSRuntime *rt, +static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, JSValueConst val) { uint32_t tag = JS_VALUE_GET_NORM_TAG(val); @@ -11955,7 +12102,6 @@ static maybe_unused void JS_DumpValueShort(JSRuntime *rt, 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); @@ -11966,6 +12112,7 @@ static maybe_unused void JS_DumpValueShort(JSRuntime *rt, bf_realloc(&rt->bf_ctx, str, 0); } break; +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: { JSBigFloat *p = JS_VALUE_GET_PTR(val); @@ -12027,13 +12174,13 @@ static maybe_unused void JS_DumpValueShort(JSRuntime *rt, } } -static maybe_unused void JS_DumpValue(JSContext *ctx, +static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val) { JS_DumpValueShort(ctx->rt, val); } -static maybe_unused void JS_PrintValue(JSContext *ctx, +static __maybe_unused void JS_PrintValue(JSContext *ctx, const char *str, JSValueConst val) { @@ -12043,15 +12190,14 @@ static maybe_unused void JS_PrintValue(JSContext *ctx, } /* return -1 if exception (proxy case) or TRUE/FALSE */ +// TODO: should take flags to make proxy resolution and exceptions optional int JS_IsArray(JSContext *ctx, JSValueConst val) { - JSObject *p; + if (js_resolve_proxy(ctx, &val, TRUE)) + return -1; 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; + JSObject *p = JS_VALUE_GET_OBJ(val); + return p->class_id == JS_CLASS_ARRAY; } else { return FALSE; } @@ -12067,8 +12213,6 @@ static double js_pow(double a, double b) } } -#ifdef CONFIG_BIGNUM - JSValue JS_NewBigInt64_1(JSContext *ctx, int64_t v) { JSValue val; @@ -12113,70 +12257,6 @@ JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v) 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) { @@ -12194,8 +12274,10 @@ static JSValue JS_StringToBigInt(JSContext *ctx, JSValue val) val = JS_NewBigInt64(ctx, 0); } else { flags = ATOD_INT_ONLY | ATOD_ACCEPT_BIN_OCT | ATOD_TYPE_BIG_INT; +#ifdef CONFIG_BIGNUM if (is_math_mode(ctx)) flags |= ATOD_MODE_BIGINT; +#endif val = js_atof(ctx, p, &p, 0, flags); p += skip_spaces(p); if (!JS_IsException(val)) { @@ -12256,6 +12338,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) p = JS_VALUE_GET_PTR(val); r = &p->num; break; +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: if (!is_math_mode(ctx)) goto fail; @@ -12268,6 +12351,7 @@ static bf_t *JS_ToBigIntFree(JSContext *ctx, bf_t *buf, JSValue val) bf_rint(r, BF_RNDZ); JS_FreeValue(ctx, val); break; +#endif case JS_TAG_STRING: val = JS_StringToBigIntErr(ctx, val); if (JS_IsException(val)) @@ -12328,7 +12412,7 @@ static void JS_FreeBigInt(JSContext *ctx, bf_t *a, bf_t *buf) } else { JSBigFloat *p = (JSBigFloat *)((uint8_t *)a - offsetof(JSBigFloat, num)); - JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_FLOAT, p)); + JS_FreeValue(ctx, JS_MKPTR(JS_TAG_BIG_INT, p)); } } @@ -12363,28 +12447,6 @@ static JSBigFloat *js_new_bf(JSContext *ctx) 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; @@ -12426,6 +12488,110 @@ static JSValue JS_CompactBigInt(JSContext *ctx, JSValue val) return JS_CompactBigInt1(ctx, val, is_math_mode(ctx)); } +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); +} + +/* 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: +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_FLOAT: +#endif + 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; +} + +#ifdef CONFIG_BIGNUM +/* 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; +} + +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); +} + /* must be kept in sync with JSOverloadableOperatorEnum */ /* XXX: use atoms ? */ static const char js_overloadable_operator_names[JS_OVOP_COUNT][4] = { @@ -12516,7 +12682,7 @@ static JSObject *find_binary_op(JSBinaryOperatorDef *def, /* 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, +static __exception int js_call_binary_op_fallback(JSContext *ctx, JSValue *pret, JSValueConst op1, JSValueConst op2, @@ -12645,7 +12811,7 @@ static WARN_UNUSED int js_call_binary_op_fallback(JSContext *ctx, /* try to call the operation on the operatorSet field of 'obj'. Only used for "/" and "**" on the BigInt prototype in math mode */ -static WARN_UNUSED int js_call_binary_op_simple(JSContext *ctx, +static __exception int js_call_binary_op_simple(JSContext *ctx, JSValue *pret, JSValueConst obj, JSValueConst op1, @@ -12702,7 +12868,7 @@ static WARN_UNUSED int js_call_binary_op_simple(JSContext *ctx, /* 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, +static __exception int js_call_unary_op_fallback(JSContext *ctx, JSValue *pret, JSValueConst op1, OPCodeEnum op) @@ -12749,46 +12915,37 @@ static WARN_UNUSED int js_call_unary_op_fallback(JSContext *ctx, 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) +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, "bigint argument with unary +"); + JS_ThrowTypeError(ctx, "bigfloat argument with unary +"); JS_FreeValue(ctx, op1); return -1; } - res = JS_NewBigInt(ctx); + + res = JS_NewBigFloat(ctx); if (JS_IsException(res)) { JS_FreeValue(ctx, op1); return -1; } - r = JS_GetBigInt(res); - a = JS_ToBigInt(ctx, &a_s, op1); + r = JS_GetBigFloat(res); + a = JS_ToBigFloat(ctx, &a_s, op1); + if (!a) { + JS_FreeValue(ctx, res); + JS_FreeValue(ctx, op1); + return -1; + } 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); + ret = bf_add_si(r, a, v, ctx->fp_env.prec, ctx->fp_env.flags); break; case OP_plus: ret = bf_set(r, a); @@ -12797,66 +12954,65 @@ static int js_unary_arith_bigint(JSContext *ctx, 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); + if (a == &a_s) + bf_delete(a); JS_FreeValue(ctx, op1); - if (unlikely(ret)) { + if (unlikely(ret & BF_ST_MEM_ERROR)) { 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) +static int js_unary_arith_bigdecimal(JSContext *ctx, + JSValue *pres, OPCodeEnum op, JSValue op1) { - bf_t a_s, *r, *a; + bfdec_t *r, *a; int ret, v; JSValue res; if (op == OP_plus && !is_math_mode(ctx)) { - JS_ThrowTypeError(ctx, "bigfloat argument with unary +"); + JS_ThrowTypeError(ctx, "bigdecimal argument with unary +"); JS_FreeValue(ctx, op1); return -1; } - res = JS_NewBigFloat(ctx); + res = JS_NewBigDecimal(ctx); if (JS_IsException(res)) { JS_FreeValue(ctx, op1); return -1; } - r = JS_GetBigFloat(res); - a = JS_ToBigFloat(ctx, &a_s, op1); + r = JS_GetBigDecimal(res); + a = JS_ToBigDecimal(ctx, op1); + if (!a) { + JS_FreeValue(ctx, res); + JS_FreeValue(ctx, op1); + return -1; + } 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); + ret = bfdec_add_si(r, a, v, BF_PREC_INF, BF_RNDZ); break; case OP_plus: - ret = bf_set(r, a); + ret = bfdec_set(r, a); break; case OP_neg: - ret = bf_set(r, a); - bf_neg(r); + ret = bfdec_set(r, a); + bfdec_neg(r); break; default: abort(); } - if (a == &a_s) - bf_delete(a); JS_FreeValue(ctx, op1); - if (unlikely(ret & BF_ST_MEM_ERROR)) { + if (unlikely(ret)) { JS_FreeValue(ctx, res); throw_bf_exception(ctx, ret); return -1; @@ -12865,67 +13021,81 @@ static int js_unary_arith_bigfloat(JSContext *ctx, return 0; } -static int js_unary_arith_bigdecimal(JSContext *ctx, - JSValue *pres, OPCodeEnum op, JSValue op1) +#endif /* CONFIG_BIGNUM */ + +static int js_unary_arith_bigint(JSContext *ctx, + JSValue *pres, OPCodeEnum op, JSValue op1) { - bfdec_t *r, *a; + bf_t a_s, *r, *a; int ret, v; JSValue res; if (op == OP_plus && !is_math_mode(ctx)) { - JS_ThrowTypeError(ctx, "bigdecimal argument with unary +"); + JS_ThrowTypeError(ctx, "bigint argument with unary +"); JS_FreeValue(ctx, op1); return -1; } - - res = JS_NewBigDecimal(ctx); + res = JS_NewBigInt(ctx); if (JS_IsException(res)) { JS_FreeValue(ctx, op1); return -1; } - r = JS_GetBigDecimal(res); - a = JS_ToBigDecimal(ctx, op1); + r = JS_GetBigInt(res); + a = JS_ToBigInt(ctx, &a_s, op1); + if (!a) { + JS_FreeValue(ctx, res); + JS_FreeValue(ctx, op1); + return -1; + } 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); + ret = bf_add_si(r, a, v, BF_PREC_INF, BF_RNDZ); break; case OP_plus: - ret = bfdec_set(r, a); + ret = bf_set(r, a); break; case OP_neg: - ret = bfdec_set(r, a); - bfdec_neg(r); + 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 no_inline WARN_UNUSED int js_unary_arith_slow(JSContext *ctx, +static no_inline __exception int js_unary_arith_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { - JSValue op1, val; - int v, ret; + JSValue op1; + int v; uint32_t tag; op1 = sp[-1]; /* fast path for float64 */ if (JS_TAG_IS_FLOAT64(JS_VALUE_GET_TAG(op1))) goto handle_float64; +#ifdef CONFIG_BIGNUM if (JS_IsObject(op1)) { - ret = js_call_unary_op_fallback(ctx, &val, op1, op); + JSValue val; + int ret = js_call_unary_op_fallback(ctx, &val, op1, op); if (ret < 0) return -1; if (ret) { @@ -12934,7 +13104,7 @@ static no_inline WARN_UNUSED int js_unary_arith_slow(JSContext *ctx, return 0; } } - +#endif op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) goto exception; @@ -12954,7 +13124,7 @@ static no_inline WARN_UNUSED int js_unary_arith_slow(JSContext *ctx, break; case OP_neg: if (v64 == 0) { - sp[-1] = __JS_NewFloat64(ctx, -0.0); + sp[-1] = JS_NewFloat64(ctx, -0.0); return 0; } else { v64 = -v64; @@ -12971,6 +13141,7 @@ static no_inline WARN_UNUSED int js_unary_arith_slow(JSContext *ctx, if (ctx->rt->bigint_ops.unary_arith(ctx, sp - 1, op, op1)) goto exception; break; +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: if (ctx->rt->bigfloat_ops.unary_arith(ctx, sp - 1, op, op1)) goto exception; @@ -12979,6 +13150,7 @@ static no_inline WARN_UNUSED int js_unary_arith_slow(JSContext *ctx, if (ctx->rt->bigdecimal_ops.unary_arith(ctx, sp - 1, op, op1)) goto exception; break; +#endif default: handle_float64: { @@ -13000,7 +13172,7 @@ static no_inline WARN_UNUSED int js_unary_arith_slow(JSContext *ctx, default: abort(); } - sp[-1] = __JS_NewFloat64(ctx, d); + sp[-1] = JS_NewFloat64(ctx, d); } break; } @@ -13010,7 +13182,7 @@ static no_inline WARN_UNUSED int js_unary_arith_slow(JSContext *ctx, return -1; } -static WARN_UNUSED int js_post_inc_slow(JSContext *ctx, +static __exception int js_post_inc_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { JSValue op1; @@ -13029,12 +13201,13 @@ static WARN_UNUSED int js_post_inc_slow(JSContext *ctx, static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) { - JSValue op1, val; - int ret; + JSValue op1; op1 = sp[-1]; +#ifdef CONFIG_BIGNUM if (JS_IsObject(op1)) { - ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not); + JSValue val; + int ret = js_call_unary_op_fallback(ctx, &val, op1, OP_not); if (ret < 0) return -1; if (ret) { @@ -13043,7 +13216,7 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) return 0; } } - +#endif op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) goto exception; @@ -13062,67 +13235,6 @@ static no_inline int js_not_slow(JSContext *ctx, JSValue *sp) 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) { @@ -13164,11 +13276,13 @@ static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, goto math_mode_div_pow; } break; +#ifdef CONFIG_BIGNUM 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; +#endif case OP_mod: ret = bf_rem(r, a, b, BF_PREC_INF, BF_RNDZ, BF_RNDZ) & BF_ST_INVALID_OP; @@ -13179,6 +13293,7 @@ static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, ret = BF_ST_INVALID_OP; } else { math_mode_div_pow: +#ifdef CONFIG_BIGNUM 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) { @@ -13219,6 +13334,9 @@ static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, } *pres = res; return 0; +#else + abort(); +#endif } } else { ret = bf_pow(r, a, b, BF_PREC_INF, BF_RNDZ | BF_POW_JS_QUIRKS); @@ -13278,6 +13396,79 @@ static int js_binary_arith_bigint(JSContext *ctx, OPCodeEnum op, return -1; } +#ifdef CONFIG_BIGNUM +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)) + goto fail; + r = JS_GetBigFloat(res); + a = JS_ToBigFloat(ctx, &a_s, op1); + if (!a) { + JS_FreeValue(ctx, res); + goto fail; + } + b = JS_ToBigFloat(ctx, &b_s, op2); + if (!b) { + if (a == &a_s) + bf_delete(a); + JS_FreeValue(ctx, res); + goto fail; + } + 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; + fail: + 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) { @@ -13364,13 +13555,13 @@ static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op, JS_FreeValue(ctx, op2); return -1; } +#endif /* CONFIG_BIGNUM */ -static no_inline WARN_UNUSED int js_binary_arith_slow(JSContext *ctx, JSValue *sp, +static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { - JSValue op1, op2, res; + JSValue op1, op2; uint32_t tag1, tag2; - int ret; double d1, d2; op1 = sp[-2]; @@ -13384,12 +13575,14 @@ static no_inline WARN_UNUSED int js_binary_arith_slow(JSContext *ctx, JSValue *s goto handle_float64; } +#ifdef CONFIG_BIGNUM /* 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); + JSValue res; + int ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0); if (ret != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -13401,6 +13594,7 @@ static no_inline WARN_UNUSED int js_binary_arith_slow(JSContext *ctx, JSValue *s } } } +#endif op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) { @@ -13430,15 +13624,16 @@ static no_inline WARN_UNUSED int js_binary_arith_slow(JSContext *ctx, JSValue *s (v < -MAX_SAFE_INTEGER || v > MAX_SAFE_INTEGER)) goto handle_bigint; if (v == 0 && (v1 | v2) < 0) { - sp[-2] = __JS_NewFloat64(ctx, -0.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); + sp[-2] = JS_NewFloat64(ctx, (double)v1 / (double)v2); return 0; +#ifdef CONFIG_BIGNUM case OP_math_mod: if (unlikely(v2 == 0)) { throw_bf_exception(ctx, BF_ST_DIVIDE_ZERO); @@ -13452,6 +13647,7 @@ static no_inline WARN_UNUSED int js_binary_arith_slow(JSContext *ctx, JSValue *s v += v2; } break; +#endif case OP_mod: if (v1 < 0 || v2 <= 0) { sp[-2] = JS_NewFloat64(ctx, fmod(v1, v2)); @@ -13472,13 +13668,17 @@ static no_inline WARN_UNUSED int js_binary_arith_slow(JSContext *ctx, JSValue *s abort(); } sp[-2] = JS_NewInt64(ctx, v); - } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { + } else +#ifdef CONFIG_BIGNUM + 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) { + } else +#endif + 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; @@ -13507,6 +13707,7 @@ static no_inline WARN_UNUSED int js_binary_arith_slow(JSContext *ctx, JSValue *s case OP_mod: dr = fmod(d1, d2); break; +#ifdef CONFIG_BIGNUM case OP_math_mod: d2 = fabs(d2); dr = fmod(d1, d2); @@ -13514,13 +13715,14 @@ static no_inline WARN_UNUSED int js_binary_arith_slow(JSContext *ctx, JSValue *s if (dr < 0) dr += d2; break; +#endif case OP_pow: dr = js_pow(d1, d2); break; default: abort(); } - sp[-2] = __JS_NewFloat64(ctx, dr); + sp[-2] = JS_NewFloat64(ctx, dr); } return 0; exception: @@ -13529,11 +13731,10 @@ static no_inline WARN_UNUSED int js_binary_arith_slow(JSContext *ctx, JSValue *s return -1; } -static no_inline WARN_UNUSED int js_add_slow(JSContext *ctx, JSValue *sp) +static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) { - JSValue op1, op2, res; + JSValue op1, op2; uint32_t tag1, tag2; - int ret; op1 = sp[-2]; op2 = sp[-1]; @@ -13545,11 +13746,12 @@ static no_inline WARN_UNUSED int js_add_slow(JSContext *ctx, JSValue *sp) double d1, d2; d1 = JS_VALUE_GET_FLOAT64(op1); d2 = JS_VALUE_GET_FLOAT64(op2); - sp[-2] = __JS_NewFloat64(ctx, d1 + d2); + sp[-2] = JS_NewFloat64(ctx, d1 + d2); return 0; } if (tag1 == JS_TAG_OBJECT || tag2 == JS_TAG_OBJECT) { +#ifdef CONFIG_BIGNUM /* try to call an overloaded operator */ if ((tag1 == JS_TAG_OBJECT && (tag2 != JS_TAG_NULL && tag2 != JS_TAG_UNDEFINED && @@ -13557,8 +13759,9 @@ static no_inline WARN_UNUSED int js_add_slow(JSContext *ctx, JSValue *sp) (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); + JSValue res; + int 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); @@ -13570,7 +13773,7 @@ static no_inline WARN_UNUSED int js_add_slow(JSContext *ctx, JSValue *sp) } } } - +#endif op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); @@ -13613,13 +13816,17 @@ static no_inline WARN_UNUSED int js_add_slow(JSContext *ctx, JSValue *sp) 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) { + } else +#ifdef CONFIG_BIGNUM + 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) { + } else +#endif + 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; @@ -13634,7 +13841,7 @@ static no_inline WARN_UNUSED int js_add_slow(JSContext *ctx, JSValue *sp) 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); + sp[-2] = JS_NewFloat64(ctx, d1 + d2); } return 0; exception: @@ -13643,12 +13850,11 @@ static no_inline WARN_UNUSED int js_add_slow(JSContext *ctx, JSValue *sp) return -1; } -static no_inline WARN_UNUSED int js_binary_logic_slow(JSContext *ctx, +static no_inline __exception int js_binary_logic_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { - JSValue op1, op2, res; - int ret; + JSValue op1, op2; uint32_t tag1, tag2; uint32_t v1, v2, r; @@ -13657,12 +13863,14 @@ static no_inline WARN_UNUSED int js_binary_logic_slow(JSContext *ctx, tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); +#ifdef CONFIG_BIGNUM /* 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); + JSValue res; + int ret = js_call_binary_op_fallback(ctx, &res, op1, op2, op, TRUE, 0); if (ret != 0) { JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); @@ -13674,6 +13882,7 @@ static no_inline WARN_UNUSED int js_binary_logic_slow(JSContext *ctx, } } } +#endif op1 = JS_ToNumericFree(ctx, op1); if (JS_IsException(op1)) { @@ -13784,6 +13993,7 @@ static int js_compare_bigfloat(JSContext *ctx, OPCodeEnum op, return res; } +#ifdef CONFIG_BIGNUM static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op, JSValue op1, JSValue op2) { @@ -13803,8 +14013,8 @@ static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op, JS_FreeValue(ctx, op1); return -1; } - a = JS_ToBigDecimal(ctx, op1); - b = JS_ToBigDecimal(ctx, op2); + a = JS_ToBigDecimal(ctx, op1); /* cannot fail */ + b = JS_ToBigDecimal(ctx, op2); /* cannot fail */ switch(op) { case OP_lt: @@ -13829,11 +14039,12 @@ static int js_compare_bigdecimal(JSContext *ctx, OPCodeEnum op, JS_FreeValue(ctx, op2); return res; } +#endif /* !CONFIG_BIGNUM */ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, OPCodeEnum op) { - JSValue op1, op2, ret; + JSValue op1, op2; int res; uint32_t tag1, tag2; @@ -13841,11 +14052,13 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, op2 = sp[-1]; tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); +#ifdef CONFIG_BIGNUM /* 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))) { + JSValue ret; res = js_call_binary_op_fallback(ctx, &ret, op1, op2, op, FALSE, HINT_NUMBER); if (res != 0) { @@ -13859,6 +14072,7 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, } } } +#endif op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NUMBER); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); @@ -13933,6 +14147,7 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, tag1 = JS_VALUE_GET_NORM_TAG(op1); tag2 = JS_VALUE_GET_NORM_TAG(op2); +#ifdef CONFIG_BIGNUM if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { res = ctx->rt->bigdecimal_ops.compare(ctx, op, op1, op2); if (res < 0) @@ -13941,7 +14156,9 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, 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) { + } else +#endif + 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; @@ -13989,14 +14206,20 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, 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); + tag == JS_TAG_FLOAT64 +#ifdef CONFIG_BIGNUM + || tag == JS_TAG_BIG_FLOAT || tag == JS_TAG_BIG_DECIMAL +#endif + ); } -static no_inline WARN_UNUSED int js_eq_slow(JSContext *ctx, JSValue *sp, +static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, BOOL is_neq) { - JSValue op1, op2, ret; + JSValue op1, op2; +#ifdef CONFIG_BIGNUM + JSValue ret; +#endif int res; uint32_t tag1, tag2; @@ -14024,7 +14247,9 @@ static no_inline WARN_UNUSED int js_eq_slow(JSContext *ctx, JSValue *sp, d2 = JS_VALUE_GET_INT(op2); } res = (d1 == d2); - } else if (tag1 == JS_TAG_BIG_DECIMAL || tag2 == JS_TAG_BIG_DECIMAL) { + } else +#ifdef CONFIG_BIGNUM + 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; @@ -14032,12 +14257,15 @@ static no_inline WARN_UNUSED int js_eq_slow(JSContext *ctx, JSValue *sp, res = ctx->rt->bigfloat_ops.compare(ctx, OP_eq, op1, op2); if (res < 0) goto exception; - } else { + } else +#endif + { res = ctx->rt->bigint_ops.compare(ctx, OP_eq, op1, op2); if (res < 0) goto exception; } } else if (tag1 == tag2) { +#ifdef CONFIG_BIGNUM if (tag1 == JS_TAG_OBJECT) { /* try the fallback operator */ res = js_call_binary_op_fallback(ctx, &ret, op1, op2, @@ -14054,6 +14282,7 @@ static no_inline WARN_UNUSED int js_eq_slow(JSContext *ctx, JSValue *sp, } } } +#endif 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)) { @@ -14090,7 +14319,7 @@ static no_inline WARN_UNUSED int js_eq_slow(JSContext *ctx, JSValue *sp, goto exception; } } - res = js_strict_eq(ctx, op1, op2); + res = js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); } else if (tag1 == JS_TAG_BOOL) { op1 = JS_NewInt32(ctx, JS_VALUE_GET_INT(op1)); goto redo; @@ -14101,7 +14330,7 @@ static no_inline WARN_UNUSED int js_eq_slow(JSContext *ctx, JSValue *sp, (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))) { - +#ifdef CONFIG_BIGNUM /* try the fallback operator */ res = js_call_binary_op_fallback(ctx, &ret, op1, op2, is_neq ? OP_neq : OP_eq, @@ -14116,7 +14345,7 @@ static no_inline WARN_UNUSED int js_eq_slow(JSContext *ctx, JSValue *sp, return 0; } } - +#endif op1 = JS_ToPrimitiveFree(ctx, op1, HINT_NONE); if (JS_IsException(op1)) { JS_FreeValue(ctx, op2); @@ -14188,6 +14417,7 @@ static no_inline int js_shr_slow(JSContext *ctx, JSValue *sp) return -1; } +#ifdef CONFIG_BIGNUM static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, int64_t exponent) { @@ -14205,7 +14435,7 @@ static JSValue js_mul_pow10_to_float64(JSContext *ctx, const bf_t *a, if (ret & BF_ST_MEM_ERROR) return JS_ThrowOutOfMemory(ctx); else - return __JS_NewFloat64(ctx, d); + return JS_NewFloat64(ctx, d); } static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp) @@ -14222,8 +14452,10 @@ static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp) op1 = sp[-2]; op2 = sp[-1]; a = JS_ToBigFloat(ctx, &a_s, op1); - if (!a) + if (!a) { + JS_FreeValue(ctx, res); return -1; + } if (JS_IsBigInt(ctx, op2)) { ret = JS_ToBigInt64(ctx, &e, op2); } else { @@ -14244,395 +14476,7 @@ static no_inline int js_mul_pow10(JSContext *ctx, JSValue *sp) 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 */ +#endif /* XXX: Should take JSValueConst arguments */ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, @@ -14726,7 +14570,6 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, 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; @@ -14734,8 +14577,8 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, res = FALSE; break; } - a = JS_ToBigFloat(ctx, &a_s, op1); - b = JS_ToBigFloat(ctx, &b_s, op2); + a = JS_ToBigFloat(ctx, &a_s, op1); /* cannot fail */ + b = JS_ToBigFloat(ctx, &b_s, op2); /* cannot fail */ res = bf_cmp_eq(a, b); if (a == &a_s) bf_delete(a); @@ -14743,6 +14586,7 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, bf_delete(b); } break; +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: { JSBigFloat *p1, *p2; @@ -14793,9 +14637,16 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2, return res; } -static BOOL js_strict_eq(JSContext *ctx, JSValue op1, JSValue op2) +static BOOL js_strict_eq(JSContext *ctx, JSValueConst op1, JSValueConst op2) +{ + return js_strict_eq2(ctx, + JS_DupValue(ctx, op1), JS_DupValue(ctx, op2), + JS_EQ_STRICT); +} + +BOOL JS_StrictEq(JSContext *ctx, JSValueConst op1, JSValueConst op2) { - return js_strict_eq2(ctx, op1, op2, JS_EQ_STRICT); + return js_strict_eq(ctx, op1, op2); } static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2) @@ -14805,6 +14656,11 @@ static BOOL js_same_value(JSContext *ctx, JSValueConst op1, JSValueConst op2) JS_EQ_SAME_VALUE); } +BOOL JS_SameValue(JSContext *ctx, JSValueConst op1, JSValueConst op2) +{ + return js_same_value(ctx, op1, op2); +} + static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op2) { return js_strict_eq2(ctx, @@ -14812,16 +14668,21 @@ static BOOL js_same_value_zero(JSContext *ctx, JSValueConst op1, JSValueConst op JS_EQ_SAME_VALUE_ZERO); } +BOOL JS_SameValueZero(JSContext *ctx, JSValueConst op1, JSValueConst op2) +{ + return js_same_value_zero(ctx, op1, op2); +} + 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]); + res = js_strict_eq2(ctx, sp[-2], sp[-1], JS_EQ_STRICT); sp[-2] = JS_NewBool(ctx, res ^ is_neq); return 0; } -static warn_unused int js_operator_in(JSContext *ctx, JSValue *sp) +static __exception int js_operator_in(JSContext *ctx, JSValue *sp) { JSValue op1, op2; JSAtom atom; @@ -14847,7 +14708,44 @@ static warn_unused int js_operator_in(JSContext *ctx, JSValue *sp) return 0; } -static warn_unused int js_has_unscopable(JSContext *ctx, JSValueConst obj, +static __exception int js_operator_private_in(JSContext *ctx, JSValue *sp) +{ + JSValue op1, op2; + int ret; + + op1 = sp[-2]; /* object */ + op2 = sp[-1]; /* field name or method function */ + + if (JS_VALUE_GET_TAG(op1) != JS_TAG_OBJECT) { + JS_ThrowTypeError(ctx, "invalid 'in' operand"); + return -1; + } + if (JS_IsObject(op2)) { + /* method: use the brand */ + ret = JS_CheckBrand(ctx, op1, op2); + if (ret < 0) + return -1; + } else { + JSAtom atom; + JSObject *p; + JSShapeProperty *prs; + JSProperty *pr; + /* field */ + atom = JS_ValueToAtom(ctx, op2); + if (unlikely(atom == JS_ATOM_NULL)) + return -1; + p = JS_VALUE_GET_OBJ(op1); + prs = find_own_property(&pr, p, atom); + JS_FreeAtom(ctx, atom); + ret = (prs != NULL); + } + JS_FreeValue(ctx, op1); + JS_FreeValue(ctx, op2); + sp[-2] = JS_NewBool(ctx, ret); + return 0; +} + +static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj, JSAtom atom) { JSValue arr, val; @@ -14865,7 +14763,7 @@ static warn_unused int js_has_unscopable(JSContext *ctx, JSValueConst obj, return ret; } -static warn_unused int js_operator_instanceof(JSContext *ctx, JSValue *sp) +static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp) { JSValue op1, op2; BOOL ret; @@ -14881,17 +14779,17 @@ static warn_unused int js_operator_instanceof(JSContext *ctx, JSValue *sp) return 0; } -static warn_unused int js_operator_typeof(JSContext *ctx, JSValueConst op1) +static __exception 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; +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: atom = JS_ATOM_bigfloat; break; @@ -14938,7 +14836,7 @@ static warn_unused int js_operator_typeof(JSContext *ctx, JSValueConst op1) return atom; } -static warn_unused int js_operator_delete(JSContext *ctx, JSValue *sp) +static __exception int js_operator_delete(JSContext *ctx, JSValue *sp) { JSValue op1, op2; JSAtom atom; @@ -15093,7 +14991,7 @@ static JSValue js_build_mapped_arguments(JSContext *ctx, int argc, var_ref = get_var_ref(ctx, sf, i, TRUE); if (!var_ref) goto fail; - pr = add_property(ctx, p, JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF); + pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF); if (!pr) { free_var_ref(ctx->rt, var_ref); goto fail; @@ -15145,10 +15043,10 @@ static JSValue js_build_rest(JSContext *ctx, int first, int argc, JSValueConst * static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) { - JSObject *p; + JSObject *p, *p1; JSPropertyEnum *tab_atom; int i; - JSValue enum_obj, obj1; + JSValue enum_obj; JSForInIterator *it; uint32_t tag, tab_atom_count; @@ -15171,40 +15069,16 @@ static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) it->is_array = FALSE; it->obj = obj; it->idx = 0; - p = JS_VALUE_GET_OBJ(enum_obj); - p->u.for_in_iterator = it; + it->tab_atom = NULL; + it->atom_count = 0; + it->in_prototype_chain = FALSE; + p1 = JS_VALUE_GET_OBJ(enum_obj); + p1->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; @@ -15216,70 +15090,101 @@ static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) } /* for fast arrays, we only store the number of elements */ it->is_array = TRUE; - it->array_length = p->u.array.count; + it->atom_count = 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_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { + JS_FreeValue(ctx, enum_obj); + return JS_EXCEPTION; } - js_free_prop_enum(ctx, tab_atom, tab_atom_count); + it->tab_atom = tab_atom; + it->atom_count = tab_atom_count; } return enum_obj; +} - slow_path: - /* non enumerable properties hide the enumerables ones in the - prototype chain */ - obj1 = JS_DupValue(ctx, obj); +/* obj -> enum_obj */ +static __exception 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; +} + +/* return -1 if exception, 0 if slow case, 1 if the enumeration is finished */ +static __exception int js_for_in_prepare_prototype_chain_enum(JSContext *ctx, + JSValueConst enum_obj) +{ + JSObject *p; + JSForInIterator *it; + JSPropertyEnum *tab_atom; + uint32_t tab_atom_count, i; + JSValue obj1; + + p = JS_VALUE_GET_OBJ(enum_obj); + it = p->u.for_in_iterator; + + /* check if there are enumerable properties in the prototype chain (fast path) */ + obj1 = JS_DupValue(ctx, it->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_SET_ENUM)) { + JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY)) { 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; + 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; } } - return enum_obj; + JS_FreeValue(ctx, obj1); + return 1; - fail: - JS_FreeValue(ctx, enum_obj); - return JS_EXCEPTION; -} + slow_path: + /* add the visited properties, even if they are not enumerable */ + if (it->is_array) { + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, + JS_VALUE_GET_OBJ(it->obj), + JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { + goto fail; + } + it->is_array = FALSE; + it->tab_atom = tab_atom; + it->atom_count = tab_atom_count; + } -/* 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; + for(i = 0; i < it->atom_count; i++) { + if (JS_DefinePropertyValue(ctx, enum_obj, it->tab_atom[i].atom, JS_NULL, JS_PROP_ENUMERABLE) < 0) + goto fail; + } return 0; + fail: + return -1; } /* enum_obj -> enum_obj value done */ -static warn_unused int js_for_in_next(JSContext *ctx, JSValue *sp) +static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) { JSValueConst enum_obj; JSObject *p; JSAtom prop; JSForInIterator *it; + JSPropertyEnum *tab_atom; + uint32_t tab_atom_count; int ret; enum_obj = sp[-1]; @@ -15292,28 +15197,68 @@ static warn_unused int js_for_in_next(JSContext *ctx, JSValue *sp) 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++; + if (it->idx >= it->atom_count) { + if (JS_IsNull(it->obj) || JS_IsUndefined(it->obj)) + goto done; /* not an object */ + /* no more property in the current object: look in the prototype */ + if (!it->in_prototype_chain) { + ret = js_for_in_prepare_prototype_chain_enum(ctx, enum_obj); + if (ret < 0) + return -1; + if (ret) + goto done; + it->in_prototype_chain = TRUE; + } + it->obj = JS_GetPrototypeFree(ctx, it->obj); + if (JS_IsException(it->obj)) + return -1; + if (JS_IsNull(it->obj)) + goto done; /* no more prototype */ + + /* must check for timeout to avoid infinite loop */ + if (js_poll_interrupts(ctx)) + return -1; + + if (JS_GetOwnPropertyNamesInternal(ctx, &tab_atom, &tab_atom_count, + JS_VALUE_GET_OBJ(it->obj), + JS_GPN_STRING_MASK | JS_GPN_SET_ENUM)) { + return -1; + } + js_free_prop_enum(ctx, it->tab_atom, it->atom_count); + it->tab_atom = tab_atom; + it->atom_count = tab_atom_count; + it->idx = 0; } 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; + if (it->is_array) { + prop = __JS_AtomFromUInt32(it->idx); + it->idx++; + } else { + BOOL is_enumerable; + prop = it->tab_atom[it->idx].atom; + is_enumerable = it->tab_atom[it->idx].is_enumerable; + it->idx++; + if (it->in_prototype_chain) { + /* slow case: we are in the prototype chain */ + ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(enum_obj), prop); + if (ret < 0) + return ret; + if (ret) + continue; /* already visited */ + /* add to the visited property list */ + if (JS_DefinePropertyValue(ctx, enum_obj, prop, JS_NULL, + JS_PROP_ENUMERABLE) < 0) + return -1; + } + if (!is_enumerable) + continue; + } + /* check if the property was deleted */ + ret = JS_GetOwnPropertyInternal(ctx, NULL, JS_VALUE_GET_OBJ(it->obj), prop); + if (ret < 0) + return ret; + if (ret) + break; } - /* 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); @@ -15488,7 +15433,7 @@ static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj, } /* obj -> enum_rec (3 slots) */ -static warn_unused int js_for_of_start(JSContext *ctx, JSValue *sp, +static __exception int js_for_of_start(JSContext *ctx, JSValue *sp, BOOL is_async) { JSValue op1, obj, method; @@ -15509,7 +15454,7 @@ static warn_unused int js_for_of_start(JSContext *ctx, JSValue *sp, objs. If 'done' is true or in case of exception, 'enum_rec' is set to undefined. If 'done' is true, 'value' is always set to undefined. */ -static warn_unused int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) +static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) { JSValue value = JS_UNDEFINED; int done = 1; @@ -15555,7 +15500,7 @@ static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj, return JS_EXCEPTION; } -static warn_unused int js_iterator_get_value_done(JSContext *ctx, JSValue *sp) +static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp) { JSValue obj, value; BOOL done; @@ -15631,7 +15576,7 @@ static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, return FALSE; } -static warn_unused int js_append_enumerate(JSContext *ctx, JSValue *sp) +static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) { JSValue iterator, enumobj, method, value; int is_array_iterator; @@ -15711,7 +15656,7 @@ exception: return -1; } -static warn_unused int JS_CopyDataProperties(JSContext *ctx, +static __exception int JS_CopyDataProperties(JSContext *ctx, JSValueConst target, JSValueConst source, JSValueConst excluded, @@ -15799,7 +15744,7 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, struct list_head *el; list_for_each(el, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, header.link); + var_ref = list_entry(el, JSVarRef, var_ref_link); if (var_ref->var_idx == var_idx && var_ref->is_arg == is_arg) { var_ref->header.ref_count++; return var_ref; @@ -15810,15 +15755,29 @@ static JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf, if (!var_ref) return NULL; var_ref->header.ref_count = 1; + add_gc_object(ctx->rt, &var_ref->header, JS_GC_OBJ_TYPE_VAR_REF); 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); + list_add_tail(&var_ref->var_ref_link, &sf->var_ref_list); + if (sf->js_mode & JS_MODE_ASYNC) { + /* The stack frame is detached and may be destroyed at any + time so its reference count must be increased. Calling + close_var_refs() when destroying the stack frame is not + possible because it would change the graph between the GC + objects. Another solution could be to temporarily detach + the JSVarRef of async functions during the GC. It would + have the advantage of allowing the release of unused stack + frames in a cycle. */ + var_ref->async_func = container_of(sf, JSAsyncFunctionState, frame); + var_ref->async_func->header.ref_count++; + } else { + var_ref->async_func = NULL; + } 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; } @@ -16049,7 +16008,10 @@ static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) int var_idx; list_for_each_safe(el, el1, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, header.link); + var_ref = list_entry(el, JSVarRef, var_ref_link); + /* no need to unlink var_ref->var_ref_link as the list is never used afterwards */ + if (var_ref->async_func) + async_func_free(rt, var_ref->async_func); var_idx = var_ref->var_idx; if (var_ref->is_arg) var_ref->value = JS_DupValueRT(rt, sf->arg_buf[var_idx]); @@ -16058,7 +16020,6 @@ static void close_var_refs(JSRuntime *rt, JSStackFrame *sf) 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); } } @@ -16069,14 +16030,15 @@ static void close_lexical_var(JSContext *ctx, JSStackFrame *sf, int idx, int is_ int var_idx = idx; list_for_each_safe(el, el1, &sf->var_ref_list) { - var_ref = list_entry(el, JSVarRef, header.link); + var_ref = list_entry(el, JSVarRef, var_ref_link); if (var_idx == var_ref->var_idx && var_ref->is_arg == is_arg) { + list_del(&var_ref->var_ref_link); + if (var_ref->async_func) + async_func_free(ctx->rt, var_ref->async_func); 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); } } } @@ -16133,7 +16095,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, arg_buf[i] = JS_UNDEFINED; sf->arg_count = arg_count; } - sf->arg_buf = arg_buf; + sf->arg_buf = (JSValue*)arg_buf; func = p->u.cfunc.c_function; switch(cproto) { @@ -16267,9 +16229,10 @@ typedef enum { OP_SPECIAL_OBJECT_IMPORT_META, } OPSpecialObjectEnum; -#define FUNC_RET_AWAIT 0 -#define FUNC_RET_YIELD 1 -#define FUNC_RET_YIELD_STAR 2 +#define FUNC_RET_AWAIT 0 +#define FUNC_RET_YIELD 1 +#define FUNC_RET_YIELD_STAR 2 +#define FUNC_RET_INITIAL_YIELD 3 /* argv[] is modified if (flags & JS_CALL_FLAG_COPY_ARGV) = 0. */ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, @@ -16303,7 +16266,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, #include "quickjs-opcode.h" [ OP_COUNT ... 255 ] = &&case_default }; -#define SWITCH(pc) goto *dispatch_table[opcode = *(pc)++]; +#define SWITCH(pc) goto *dispatch_table[opcode = *pc++]; #define CASE(op) case_ ## op #define DEFAULT case_default #define BREAK SWITCH(pc) @@ -16346,7 +16309,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, return JS_ThrowTypeError(caller_ctx, "not a function"); } return call_func(caller_ctx, func_obj, this_obj, argc, - argv, flags); + (JSValueConst *)argv, flags); } b = p->u.func.function_bytecode; @@ -16500,12 +16463,12 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, int arg = *pc++; switch(arg) { case OP_SPECIAL_OBJECT_ARGUMENTS: - *sp++ = js_build_arguments(ctx, argc, argv); + *sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv); if (unlikely(JS_IsException(sp[-1]))) goto exception; break; case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS: - *sp++ = js_build_mapped_arguments(ctx, argc, argv, + *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv, sf, min_int(argc, b->arg_count)); if (unlikely(JS_IsException(sp[-1]))) goto exception; @@ -16545,7 +16508,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, { int first = get_u16(pc); pc += 2; - *sp++ = js_build_rest(ctx, first, argc, argv); + *sp++ = js_build_rest(ctx, first, argc, (JSValueConst *)argv); if (unlikely(JS_IsException(sp[-1]))) goto exception; } @@ -16778,7 +16741,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, goto exception; call_argv = sp - call_argc; for(i = 0; i < call_argc; i++) { - ret = JS_DefinePropertyValue(ctx, ret_val, JS_AtomFromUInt32(i), call_argv[i], + ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i], JS_PROP_C_W_E | JS_PROP_THROW); call_argv[i] = JS_UNDEFINED; if (ret < 0) { @@ -16797,7 +16760,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, magic = get_u16(pc); pc += 2; - ret_val = js_function_apply(ctx, sp[-3], 2, &sp[-2], magic); + ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic); if (unlikely(JS_IsException(ret_val))) goto exception; JS_FreeValue(ctx, sp[-3]); @@ -16834,8 +16797,15 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, } BREAK; CASE(OP_check_brand): - if (JS_CheckBrand(ctx, sp[-2], sp[-1]) < 0) - goto exception; + { + int ret = JS_CheckBrand(ctx, sp[-2], sp[-1]); + if (ret < 0) + goto exception; + if (!ret) { + JS_ThrowTypeError(ctx, "invalid brand on object"); + goto exception; + } + } BREAK; CASE(OP_add_brand): if (JS_AddBrand(ctx, sp[-2], sp[-1]) < 0) @@ -16930,7 +16900,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, JS_EVAL_TYPE_DIRECT, scope_idx); } else { ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len, - tab); + (JSValueConst *)tab); } free_arg_list(ctx, tab, len); if (unlikely(JS_IsException(ret_val))) @@ -17257,6 +17227,19 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, sp++; } BREAK; + CASE(OP_get_loc_checkthis): + { + int idx; + idx = get_u16(pc); + pc += 2; + if (unlikely(JS_IsUninitialized(var_buf[idx]))) { + JS_ThrowReferenceErrorUninitialized2(caller_ctx, b, idx, FALSE); + goto exception; + } + sp[0] = JS_DupValue(ctx, var_buf[idx]); + sp++; + } + BREAK; CASE(OP_put_loc_check): { int idx; @@ -17380,6 +17363,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, op1 = sp[-1]; pc += 4; + /* quick and dirty test for JS_TAG_INT, JS_TAG_BOOL, JS_TAG_NULL and JS_TAG_UNDEFINED */ if ((uint32_t)JS_VALUE_GET_TAG(op1) <= JS_TAG_UNDEFINED) { res = JS_VALUE_GET_INT(op1); } else { @@ -17526,26 +17510,21 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, } sp--; BREAK; - CASE(OP_iterator_close_return): + CASE(OP_nip_catch): { JSValue ret_val; - /* iter_obj next catch_offset ... ret_val -> - ret_eval iter_obj next catch_offset */ + /* catch_offset ... ret_val -> ret_eval */ 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"); + if (unlikely(sp == stack_buf)) { + JS_ThrowInternalError(ctx, "nip_catch"); JS_FreeValue(ctx, ret_val); goto exception; } - sp[0] = sp[-1]; - sp[-1] = sp[-2]; - sp[-2] = sp[-3]; - sp[-3] = ret_val; - sp++; + sp[-1] = ret_val; } BREAK; @@ -17554,7 +17533,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, { JSValue ret; ret = JS_Call(ctx, sp[-3], sp[-4], - 1, (sp - 1)); + 1, (JSValueConst *)(sp - 1)); if (JS_IsException(ret)) goto exception; JS_FreeValue(ctx, sp[-1]); @@ -17582,7 +17561,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, 0, NULL); } else { ret = JS_CallFree(ctx, method, sp[-4], - 1, (sp - 1)); + 1, (JSValueConst *)(sp - 1)); } if (JS_IsException(ret)) goto exception; @@ -17646,7 +17625,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, atom = get_u32(pc); pc += 4; - ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1], + ret = JS_SetPropertyInternal(ctx, sp[-2], atom, sp[-1], sp[-2], JS_PROP_THROW_STRICT); JS_FreeValue(ctx, sp[-2]); sp -= 2; @@ -17945,8 +17924,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, 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); + ret = JS_SetPropertyInternal(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]); @@ -18005,9 +17984,14 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, sp[-2] = JS_NewInt32(ctx, r); sp--; } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { - sp[-2] = JS_NewFloat64Impl(ctx, JS_VALUE_GET_FLOAT64(op1) + + sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) + JS_VALUE_GET_FLOAT64(op2)); sp--; + } else if (JS_IsString(op1) && JS_IsString(op2)) { + sp[-2] = JS_ConcatString(ctx, op1, op2); + sp--; + if (JS_IsException(sp[-1])) + goto exception; } else { add_slow: if (js_add_slow(ctx, sp)) @@ -18018,38 +18002,45 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, BREAK; CASE(OP_add_loc): { + JSValue op2; JSValue *pv; int idx; idx = *pc; pc += 1; + op2 = sp[-1]; pv = &var_buf[idx]; - if (likely(JS_VALUE_IS_BOTH_INT(*pv, sp[-1]))) { + if (likely(JS_VALUE_IS_BOTH_INT(*pv, op2))) { int64_t r; - r = (int64_t)JS_VALUE_GET_INT(*pv) + - JS_VALUE_GET_INT(sp[-1]); + r = (int64_t)JS_VALUE_GET_INT(*pv) + JS_VALUE_GET_INT(op2); if (unlikely((int)r != r)) goto add_loc_slow; *pv = JS_NewInt32(ctx, r); sp--; + } else if (JS_VALUE_IS_BOTH_FLOAT(*pv, op2)) { + *pv = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(*pv) + + JS_VALUE_GET_FLOAT64(op2)); + 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)) + op2 = JS_ToPrimitiveFree(ctx, op2, HINT_NONE); + if (JS_IsException(op2)) goto exception; - set_value(ctx, pv, op1); + if (JS_ConcatStringInPlace(ctx, JS_VALUE_GET_STRING(*pv), op2)) { + JS_FreeValue(ctx, op2); + } else { + op2 = JS_ConcatString(ctx, JS_DupValue(ctx, *pv), op2); + if (JS_IsException(op2)) + goto exception; + set_value(ctx, pv, op2); + } } 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]; + ops[1] = op2; sp--; if (js_add_slow(ctx, ops + 2)) goto exception; @@ -18070,8 +18061,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, sp[-2] = JS_NewInt32(ctx, r); sp--; } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { - sp[-2] = JS_NewFloat64Impl(ctx, JS_VALUE_GET_FLOAT64(op1) - - JS_VALUE_GET_FLOAT64(op2)); + sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) - + JS_VALUE_GET_FLOAT64(op2)); sp--; } else { goto binary_arith_slow; @@ -18113,7 +18104,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, #endif d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); mul_fp_res: - sp[-2] = JS_NewFloat64Impl(ctx, d); + sp[-2] = __JS_NewFloat64(ctx, d); sp--; } else { goto binary_arith_slow; @@ -18205,7 +18196,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, } else if (JS_TAG_IS_FLOAT64(tag)) { d = -JS_VALUE_GET_FLOAT64(op1); neg_fp_res: - sp[-1] = JS_NewFloat64Impl(ctx, d); + sp[-1] = __JS_NewFloat64(ctx, d); } else { if (js_unary_arith_slow(ctx, sp, opcode)) goto exception; @@ -18495,6 +18486,11 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, goto exception; sp--; BREAK; + CASE(OP_private_in): + if (js_operator_private_in(ctx, sp)) + goto exception; + sp--; + BREAK; CASE(OP_instanceof): if (js_operator_instanceof(ctx, sp)) goto exception; @@ -18625,7 +18621,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, break; case OP_with_put_var: /* XXX: check if strict mode */ - ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], + ret = JS_SetPropertyInternal(ctx, obj, atom, sp[-2], obj, JS_PROP_THROW_STRICT); JS_FreeValue(ctx, sp[-1]); sp -= 2; @@ -18681,9 +18677,11 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, 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_initial_yield): + ret_val = JS_NewInt32(ctx, FUNC_RET_INITIAL_YIELD); + goto done_generator; CASE(OP_nop): BREAK; @@ -18802,14 +18800,14 @@ JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, int argc, JSValueConst *argv) { return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, - argc, argv, JS_CALL_FLAG_COPY_ARGV); + argc, (JSValue *)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); + argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV); JS_FreeValue(ctx, func_obj); return res; } @@ -18914,7 +18912,7 @@ static JSValue JS_CallConstructorInternal(JSContext *ctx, return JS_ThrowTypeError(ctx, "not a function"); } return call_func(ctx, func_obj, new_target, argc, - argv, flags); + (JSValueConst *)argv, flags); } b = p->u.func.function_bytecode; @@ -18943,7 +18941,7 @@ JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, int argc, JSValueConst *argv) { return JS_CallConstructorInternal(ctx, func_obj, new_target, - argc, argv, + argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV); } @@ -18951,7 +18949,7 @@ JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, int argc, JSValueConst *argv) { return JS_CallConstructorInternal(ctx, func_obj, func_obj, - argc, argv, + argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV); } @@ -18974,26 +18972,35 @@ static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, } /* 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) +static JSAsyncFunctionState *async_func_init(JSContext *ctx, + JSValueConst func_obj, JSValueConst this_obj, + int argc, JSValueConst *argv) { + JSAsyncFunctionState *s; JSObject *p; JSFunctionBytecode *b; JSStackFrame *sf; int local_count, i, arg_buf_len, n; + s = js_mallocz(ctx, sizeof(*s)); + if (!s) + return NULL; + s->header.ref_count = 1; + add_gc_object(ctx->rt, &s->header, JS_GC_OBJ_TYPE_ASYNC_FUNCTION); + 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->js_mode = b->js_mode | JS_MODE_ASYNC; 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; + if (!sf->arg_buf) { + js_free(ctx, s); + return NULL; + } sf->cur_func = JS_DupValue(ctx, func_obj); s->this_val = JS_DupValue(ctx, this_obj); s->argc = argc; @@ -19005,38 +19012,17 @@ static warn_unused int async_func_init(JSContext *ctx, JSAsyncFunctionState *s, 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); - } + s->resolving_funcs[0] = JS_UNDEFINED; + s->resolving_funcs[1] = JS_UNDEFINED; + s->is_completed = FALSE; + return s; } -static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) +static void async_func_free_frame(JSRuntime *rt, JSAsyncFunctionState *s) { - JSStackFrame *sf; + JSStackFrame *sf = &s->frame; 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); @@ -19044,6 +19030,7 @@ static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) JS_FreeValueRT(rt, *sp); } js_free_rt(rt, sf->arg_buf); + sf->arg_buf = NULL; } JS_FreeValueRT(rt, sf->cur_func); JS_FreeValueRT(rt, s->this_val); @@ -19051,17 +19038,66 @@ static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) static JSValue async_func_resume(JSContext *ctx, JSAsyncFunctionState *s) { - JSValue func_obj; + JSRuntime *rt = ctx->rt; + JSStackFrame *sf = &s->frame; + JSValue func_obj, ret; - if (js_check_stack_overflow(ctx->rt, 0)) - return JS_ThrowStackOverflow(ctx); + assert(!s->is_completed); + if (js_check_stack_overflow(ctx->rt, 0)) { + ret = JS_ThrowStackOverflow(ctx); + } else { + /* the tag does not matter provided it is not an object */ + func_obj = JS_MKPTR(JS_TAG_INT, s); + ret = JS_CallInternal(ctx, func_obj, s->this_val, JS_UNDEFINED, + s->argc, sf->arg_buf, JS_CALL_FLAG_GENERATOR); + } + if (JS_IsException(ret) || JS_IsUndefined(ret)) { + if (JS_IsUndefined(ret)) { + ret = sf->cur_sp[-1]; + sf->cur_sp[-1] = JS_UNDEFINED; + } + /* end of execution */ + s->is_completed = TRUE; + + /* close the closure variables. */ + close_var_refs(rt, sf); - /* 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); + async_func_free_frame(rt, s); + } + return ret; } +static void __async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) +{ + /* cannot close the closure variables here because it would + potentially modify the object graph */ + if (!s->is_completed) { + async_func_free_frame(rt, s); + } + + JS_FreeValueRT(rt, s->resolving_funcs[0]); + JS_FreeValueRT(rt, s->resolving_funcs[1]); + + remove_gc_object(&s->header); + if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES && s->header.ref_count != 0) { + list_add_tail(&s->header.link, &rt->gc_zero_ref_count_list); + } else { + js_free_rt(rt, s); + } +} + +static void async_func_free(JSRuntime *rt, JSAsyncFunctionState *s) +{ + if (--s->header.ref_count == 0) { + if (rt->gc_phase != JS_GC_PHASE_REMOVE_CYCLES) { + list_del(&s->header.link); + list_add(&s->header.link, &rt->gc_zero_ref_count_list); + if (rt->gc_phase == JS_GC_PHASE_NONE) { + free_zero_refcount(rt); + } + } + } +} /* Generators */ @@ -19075,14 +19111,17 @@ typedef enum JSGeneratorStateEnum { typedef struct JSGeneratorData { JSGeneratorStateEnum state; - JSAsyncFunctionState func_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); + if (s->func_state) { + async_func_free(rt, s->func_state); + s->func_state = NULL; + } s->state = JS_GENERATOR_STATE_COMPLETED; } @@ -19107,9 +19146,9 @@ static void js_generator_mark(JSRuntime *rt, JSValueConst val, JSObject *p = JS_VALUE_GET_OBJ(val); JSGeneratorData *s = p->u.generator_data; - if (!s || s->state == JS_GENERATOR_STATE_COMPLETED) + if (!s || !s->func_state) return; - async_func_mark(rt, &s->func_state, mark_func); + mark_func(rt, &s->func_state->header); } /* XXX: use enum */ @@ -19128,10 +19167,10 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, *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: + sf = &s->func_state->frame; if (magic == GEN_MAGIC_NEXT) { goto exec_no_arg; } else { @@ -19141,28 +19180,29 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, break; case JS_GENERATOR_STATE_SUSPENDED_YIELD_STAR: case JS_GENERATOR_STATE_SUSPENDED_YIELD: + sf = &s->func_state->frame; /* 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; + 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->func_state->throw_flag = FALSE; } s->state = JS_GENERATOR_STATE_EXECUTING; - func_ret = async_func_resume(ctx, &s->func_state); + 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 */ + if (s->func_state->is_completed) { + /* finalize the execution in case of exception or normal return */ free_generator_stack(ctx, s); return func_ret; - } - if (JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT) { + } else { + assert(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; @@ -19173,12 +19213,6 @@ static JSValue js_generator_next(JSContext *ctx, JSValueConst this_val, } 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: @@ -19216,13 +19250,14 @@ static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, 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->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv); + if (!s->func_state) { 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); + func_ret = async_func_resume(ctx, s->func_state); if (JS_IsException(func_ret)) goto fail; JS_FreeValue(ctx, func_ret); @@ -19240,36 +19275,12 @@ static JSValue js_generator_function_call(JSContext *ctx, JSValueConst func_obj, /* 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; + JSAsyncFunctionState *s = p->u.async_function_data; if (s) { - js_async_function_free(rt, s); + async_func_free(rt, s); } } @@ -19277,14 +19288,14 @@ 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; + JSAsyncFunctionState *s = p->u.async_function_data; if (s) { mark_func(rt, &s->header); } } static int js_async_function_resolve_create(JSContext *ctx, - JSAsyncFunctionData *s, + JSAsyncFunctionState *s, JSValue *resolving_funcs) { int i; @@ -19306,60 +19317,58 @@ static int js_async_function_resolve_create(JSContext *ctx, return 0; } -static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) +static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionState *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); + func_ret = async_func_resume(ctx, s); + if (s->is_completed) { + if (JS_IsException(func_ret)) { + JSValue error; + fail: + error = JS_GetException(ctx); + ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, + 1, (JSValueConst *)&error); + JS_FreeValue(ctx, error); 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; + /* normal return */ + ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED, + 1, (JSValueConst *)&func_ret); + JS_FreeValue(ctx, func_ret); + JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ + } + } else { + JSValue value, 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; - } + value = s->frame.cur_sp[-1]; + s->frame.cur_sp[-1] = JS_UNDEFINED; - /* 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); + /* await */ + JS_FreeValue(ctx, func_ret); /* not used */ + promise = js_promise_resolve(ctx, ctx->promise_ctor, + 1, (JSValueConst *)&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); - for(i = 0; i < 2; i++) - JS_FreeValue(ctx, resolving_funcs[i]); - if (res) - goto fail; + 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; } } @@ -19370,7 +19379,7 @@ static JSValue js_async_function_resolve_call(JSContext *ctx, int flags) { JSObject *p = JS_VALUE_GET_OBJ(func_obj); - JSAsyncFunctionData *s = p->u.async_function_data; + JSAsyncFunctionState *s = p->u.async_function_data; BOOL is_reject = p->class_id - JS_CLASS_ASYNC_FUNCTION_RESOLVE; JSValueConst arg; @@ -19378,12 +19387,12 @@ static JSValue js_async_function_resolve_call(JSContext *ctx, arg = argv[0]; else arg = JS_UNDEFINED; - s->func_state.throw_flag = is_reject; + s->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); + s->frame.cur_sp[-1] = JS_DupValue(ctx, arg); } js_async_function_resume(ctx, s); return JS_UNDEFINED; @@ -19394,32 +19403,21 @@ static JSValue js_async_function_call(JSContext *ctx, JSValueConst func_obj, int argc, JSValueConst *argv, int flags) { JSValue promise; - JSAsyncFunctionData *s; + JSAsyncFunctionState *s; - s = js_mallocz(ctx, sizeof(*s)); + s = async_func_init(ctx, func_obj, this_obj, argc, argv); 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); + if (JS_IsException(promise)) { + async_func_free(ctx->rt, s); return JS_EXCEPTION; } - s->is_active = TRUE; js_async_function_resume(ctx, s); - js_async_function_free(ctx->rt, s); + async_func_free(ctx->rt, s); return promise; } @@ -19448,7 +19446,8 @@ typedef struct JSAsyncGeneratorRequest { typedef struct JSAsyncGeneratorData { JSObject *generator; /* back pointer to the object (const) */ JSAsyncGeneratorStateEnum state; - JSAsyncFunctionState func_state; + /* func_state is NULL is state AWAITING_RETURN and COMPLETED */ + JSAsyncFunctionState *func_state; struct list_head queue; /* list of JSAsyncGeneratorRequest.link */ } JSAsyncGeneratorData; @@ -19466,10 +19465,8 @@ static void js_async_generator_free(JSRuntime *rt, 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); - } + if (s->func_state) + async_func_free(rt, s->func_state); js_free_rt(rt, s); } @@ -19496,9 +19493,8 @@ static void js_async_generator_mark(JSRuntime *rt, JSValueConst val, 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); + if (s->func_state) { + mark_func(rt, &s->func_state->header); } } } @@ -19608,7 +19604,8 @@ static void js_async_generator_complete(JSContext *ctx, { if (s->state != JS_ASYNC_GENERATOR_STATE_COMPLETED) { s->state = JS_ASYNC_GENERATOR_STATE_COMPLETED; - async_func_free(ctx->rt, &s->func_state); + async_func_free(ctx->rt, s->func_state); + s->func_state = NULL; } } @@ -19619,10 +19616,19 @@ static int js_async_generator_completed_return(JSContext *ctx, 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; + // Can fail looking up JS_ATOM_constructor when is_reject==0. + promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, &value, + /*is_reject*/0); + // A poisoned .constructor property is observable and the resulting + // exception should be delivered to the catch handler. + if (JS_IsException(promise)) { + JSValue err = JS_GetException(ctx); + promise = js_promise_resolve(ctx, ctx->promise_ctor, 1, (JSValueConst *)&err, + /*is_reject*/1); + JS_FreeValue(ctx, err); + if (JS_IsException(promise)) + return -1; + } if (js_async_generator_resolve_function_create(ctx, JS_MKPTR(JS_TAG_OBJECT, s->generator), resolving_funcs1, @@ -19670,7 +19676,6 @@ static void js_async_generator_resume_next(JSContext *ctx, } 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); } @@ -19681,30 +19686,38 @@ static void js_async_generator_resume_next(JSContext *ctx, 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; + 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] = + 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++; + s->func_state->frame.cur_sp++; exec_no_arg: - s->func_state.throw_flag = FALSE; + 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 = async_func_resume(ctx, s->func_state); + if (s->func_state->is_completed) { + 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 { + /* end of function */ + js_async_generator_complete(ctx, s); + js_async_generator_resolve(ctx, s, func_ret, TRUE); + JS_FreeValue(ctx, func_ret); + } + } else { + int func_ret_code, ret; + assert(JS_VALUE_GET_TAG(func_ret) == JS_TAG_INT); func_ret_code = JS_VALUE_GET_INT(func_ret); + value = s->func_state->frame.cur_sp[-1]; + s->func_state->frame.cur_sp[-1] = JS_UNDEFINED; switch(func_ret_code) { case FUNC_RET_YIELD: case FUNC_RET_YIELD_STAR: @@ -19716,20 +19729,17 @@ static void js_async_generator_resume_next(JSContext *ctx, JS_FreeValue(ctx, value); break; case FUNC_RET_AWAIT: - js_async_generator_await(ctx, s, value); + ret = js_async_generator_await(ctx, s, value); JS_FreeValue(ctx, value); + if (ret < 0) { + /* exception: throw it */ + s->func_state->throw_flag = TRUE; + goto resume_exec; + } 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: @@ -19763,12 +19773,12 @@ static JSValue js_async_generator_resolve_function(JSContext *ctx, } else { /* restart function execution after await() */ assert(s->state == JS_ASYNC_GENERATOR_STATE_EXECUTING); - s->func_state.throw_flag = is_reject; + 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); + s->func_state->frame.cur_sp[-1] = JS_DupValue(ctx, arg); } js_async_generator_resume_next(ctx, s); } @@ -19792,7 +19802,7 @@ static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val, JS_ThrowTypeError(ctx, "not an AsyncGenerator object"); err = JS_GetException(ctx); res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, - 1, &err); + 1, (JSValueConst *)&err); JS_FreeValue(ctx, err); JS_FreeValue(ctx, res2); JS_FreeValue(ctx, resolving_funcs[0]); @@ -19832,14 +19842,12 @@ static JSValue js_async_generator_function_call(JSContext *ctx, JSValueConst fun 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; + s->func_state = async_func_init(ctx, func_obj, this_obj, argc, argv); + if (!s->func_state) 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); + func_ret = async_func_resume(ctx, s->func_state); if (JS_IsException(func_ret)) goto fail; JS_FreeValue(ctx, func_ret); @@ -20025,6 +20033,7 @@ typedef enum JSParseFunctionEnum { JS_PARSE_FUNC_GETTER, JS_PARSE_FUNC_SETTER, JS_PARSE_FUNC_METHOD, + JS_PARSE_FUNC_CLASS_STATIC_INIT, JS_PARSE_FUNC_CLASS_CONSTRUCTOR, JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR, } JSParseFunctionEnum; @@ -20144,6 +20153,7 @@ typedef struct JSFunctionDef { int source_len; JSModuleDef *module; /* != NULL when parsing a module */ + BOOL has_await; /* TRUE if await is used (used in module eval) */ } JSFunctionDef; typedef struct JSToken { @@ -20227,16 +20237,14 @@ static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = { #define short_opcode_info(op) opcode_info[op] #endif -static warn_unused int next_token(JSParseState *s); +static __exception 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); @@ -20258,7 +20266,7 @@ static void free_token(JSParseState *s, JSToken *token) } } -static void maybe_unused dump_token(JSParseState *s, +static void __maybe_unused dump_token(JSParseState *s, const JSToken *token) { switch(token->val) { @@ -20365,7 +20373,7 @@ static int js_parse_error_reserved_identifier(JSParseState *s) s->token.u.ident.atom)); } -static warn_unused int js_parse_template_part(JSParseState *s, const uint8_t *p) +static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p) { uint32_t c; StringBuffer b_s, *b = &b_s; @@ -20426,7 +20434,7 @@ static warn_unused int js_parse_template_part(JSParseState *s, const uint8_t *p) return -1; } -static warn_unused int js_parse_string(JSParseState *s, int sep, +static __exception int js_parse_string(JSParseState *s, int sep, BOOL do_throw, const uint8_t *p, JSToken *token, const uint8_t **pp) { @@ -20571,7 +20579,7 @@ static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) { !s->token.u.ident.has_escape; } -static warn_unused int js_parse_regexp(JSParseState *s) +static __exception int js_parse_regexp(JSParseState *s) { const uint8_t *p; BOOL in_class; @@ -20669,7 +20677,7 @@ static warn_unused int js_parse_regexp(JSParseState *s) return -1; } -static warn_unused int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, +static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, const char *static_buf) { char *buf, *new_buf; @@ -20696,6 +20704,48 @@ static warn_unused int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, return 0; } +/* convert a TOK_IDENT to a keyword when needed */ +static void update_token_ident(JSParseState *s) +{ + 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_CLASS_STATIC_INIT || + (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) || + s->cur_func->parent->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))))) { + if (s->token.u.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; + } + } +} + +/* if the current token is an identifier or keyword, reparse it + according to the current function type */ +static void reparse_ident_token(JSParseState *s) +{ + if (s->token.val == TOK_IDENT || + (s->token.val >= TOK_FIRST_KEYWORD && + s->token.val <= TOK_LAST_KEYWORD)) { + s->token.val = TOK_IDENT; + s->token.u.ident.is_reserved = FALSE; + update_token_ident(s); + } +} + /* '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) @@ -20745,7 +20795,7 @@ static JSAtom parse_ident(JSParseState *s, const uint8_t **pp, } -static warn_unused int next_token(JSParseState *s) +static __exception int next_token(JSParseState *s) { const uint8_t *p; int c; @@ -20902,30 +20952,8 @@ static warn_unused int next_token(JSParseState *s) 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; - } + s->token.val = TOK_IDENT; + update_token_ident(s); break; case '#': /* private name */ @@ -20982,8 +21010,8 @@ static warn_unused int next_token(JSParseState *s) int flags, radix; flags = ATOD_ACCEPT_BIN_OCT | ATOD_ACCEPT_LEGACY_OCTAL | ATOD_ACCEPT_UNDERSCORES; -#ifdef CONFIG_BIGNUM flags |= ATOD_ACCEPT_SUFFIX; +#ifdef CONFIG_BIGNUM if (s->cur_func->js_mode & JS_MODE_MATH) { flags |= ATOD_MODE_BIGINT; if (s->cur_func->js_mode & JS_MODE_MATH) @@ -21274,8 +21302,7 @@ static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c) for(;;) { buf[ident_pos++] = c; c = *p; - if (c >= 128 || - !((lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1)) + if (c >= 128 || !lre_is_id_continue_byte(c)) break; p++; if (unlikely(ident_pos >= ident_size - UTF8_CHAR_LEN_MAX)) { @@ -21293,7 +21320,7 @@ static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c) return atom; } -static warn_unused int json_next_token(JSParseState *s) +static __exception int json_next_token(JSParseState *s) { const uint8_t *p; int c; @@ -21487,9 +21514,29 @@ static warn_unused int json_next_token(JSParseState *s) return -1; } -/* only used for ':' and '=>', 'let' or 'function' look-ahead. *pp is - only set if TOK_IMPORT is returned */ -/* XXX: handle all unicode cases */ +static int match_identifier(const uint8_t *p, const char *s) { + uint32_t c; + while (*s) { + if ((uint8_t)*s++ != *p++) + return 0; + } + c = *p; + if (c >= 128) + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); + return !lre_js_is_ident_next(c); +} + +/* simple_next_token() is used to check for the next token in simple cases. + It is only used for ':' and '=>', 'let' or 'function' look-ahead. + (*pp) is only set if TOK_IMPORT is returned for JS_DetectModule() + Whitespace and comments are skipped correctly. + Then the next token is analyzed, only for specific words. + Return values: + - '\n' if !no_line_terminator + - TOK_ARROW, TOK_IN, TOK_IMPORT, TOK_OF, TOK_EXPORT, TOK_FUNCTION + - TOK_IDENT is returned for other identifiers and keywords + - otherwise the next character or unicode codepoint is returned. + */ static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator) { const uint8_t *p; @@ -21533,33 +21580,42 @@ static int simple_next_token(const uint8_t **pp, BOOL no_line_terminator) if (*p == '>') return TOK_ARROW; break; + case 'i': + if (match_identifier(p, "n")) + return TOK_IN; + if (match_identifier(p, "mport")) { + *pp = p + 5; + return TOK_IMPORT; + } + return TOK_IDENT; + case 'o': + if (match_identifier(p, "f")) + return TOK_OF; + return TOK_IDENT; + case 'e': + if (match_identifier(p, "xport")) + return TOK_EXPORT; + return TOK_IDENT; + case 'f': + if (match_identifier(p, "unction")) + return TOK_FUNCTION; + return TOK_IDENT; + case '\\': + if (*p == 'u') { + if (lre_js_is_ident_first(lre_parse_escape(&p, TRUE))) + return TOK_IDENT; + } + break; default: - if (lre_js_is_ident_first(c)) { - if (c == 'i') { - if (p[0] == 'n' && !lre_js_is_ident_next(p[1])) { - return TOK_IN; - } - if (p[0] == 'm' && p[1] == 'p' && p[2] == 'o' && - p[3] == 'r' && p[4] == 't' && - !lre_js_is_ident_next(p[5])) { - *pp = p + 5; - return TOK_IMPORT; - } - } else if (c == 'o' && *p == 'f' && !lre_js_is_ident_next(p[1])) { - return TOK_OF; - } else if (c == 'e' && - p[0] == 'x' && p[1] == 'p' && p[2] == 'o' && - p[3] == 'r' && p[4] == 't' && - !lre_js_is_ident_next(p[5])) { - *pp = p + 5; - return TOK_EXPORT; - } else if (c == 'f' && p[0] == 'u' && p[1] == 'n' && - p[2] == 'c' && p[3] == 't' && p[4] == 'i' && - p[5] == 'o' && p[6] == 'n' && !lre_js_is_ident_next(p[7])) { - return TOK_FUNCTION; - } - return TOK_IDENT; + if (c >= 128) { + c = unicode_from_utf8(p - 1, UTF8_CHAR_LEN_MAX, &p); + if (no_line_terminator && (c == CP_PS || c == CP_LS)) + return '\n'; } + if (lre_is_space(c)) + continue; + if (lre_js_is_ident_first(c)) + return TOK_IDENT; break; } return c; @@ -21572,6 +21628,31 @@ static int peek_token(JSParseState *s, BOOL no_line_terminator) return simple_next_token(&p, no_line_terminator); } +static void skip_shebang(const uint8_t **pp, const uint8_t *buf_end) +{ + const uint8_t *p = *pp; + int c; + + if (p[0] == '#' && p[1] == '!') { + p += 2; + while (p < buf_end) { + if (*p == '\n' || *p == '\r') { + break; + } else if (*p >= 0x80) { + c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); + if (c == CP_LS || c == CP_PS) { + break; + } else if (c == -1) { + p++; /* skip invalid UTF-8 */ + } + } else { + p++; + } + } + *pp = p; + } +} + /* return true if 'input' contains the source of a module (heuristic). 'input' must be a zero terminated. @@ -21582,6 +21663,8 @@ BOOL JS_DetectModule(const char *input, size_t input_len) { const uint8_t *p = (const uint8_t *)input; int tok; + + skip_shebang(&p, p + input_len); switch(simple_next_token(&p, FALSE)) { case TOK_IMPORT: tok = simple_next_token(&p, FALSE); @@ -21694,6 +21777,14 @@ static int new_label(JSParseState *s) return new_label_fd(s->cur_func, -1); } +/* don't update the last opcode and don't emit line number info */ +static void emit_label_raw(JSParseState *s, int label) +{ + emit_u8(s, OP_label); + emit_u32(s, label); + s->cur_func->label_slots[label].pos = s->cur_func->byte_code.size; +} + /* return the label ID offset */ static int emit_label(JSParseState *s, int label) { @@ -21733,7 +21824,7 @@ static int cpool_add(JSParseState *s, JSValue val) return fd->cpool_count - 1; } -static warn_unused int emit_push_const(JSParseState *s, JSValueConst val, +static __exception int emit_push_const(JSParseState *s, JSValueConst val, BOOL as_atom) { int idx; @@ -21743,7 +21834,7 @@ static warn_unused int emit_push_const(JSParseState *s, JSValueConst val, /* warning: JS_NewAtomStr frees the string value */ JS_DupValue(s->ctx, val); atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val)); - if (atom != JS_ATOM_NULL && !JS_AtomIsTaggedInt(atom)) { + if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) { emit_op(s, OP_push_atom_value); emit_u32(s, atom); return 0; @@ -22192,7 +22283,7 @@ static int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, /* add a private field variable in the current scope */ static int add_private_class_field(JSParseState *s, JSFunctionDef *fd, - JSAtom name, JSVarKindEnum var_kind) + JSAtom name, JSVarKindEnum var_kind, BOOL is_static) { JSContext *ctx = s->ctx; JSVarDef *vd; @@ -22204,17 +22295,18 @@ static int add_private_class_field(JSParseState *s, JSFunctionDef *fd, vd = &fd->vars[idx]; vd->is_lexical = 1; vd->is_const = 1; + vd->is_static_private = is_static; return idx; } -static warn_unused int js_parse_expr(JSParseState *s); -static warn_unused int js_parse_function_decl(JSParseState *s, +static __exception int js_parse_expr(JSParseState *s); +static __exception int js_parse_function_decl(JSParseState *s, JSParseFunctionEnum func_type, JSFunctionKindEnum func_kind, JSAtom func_name, const uint8_t *ptr, int start_line); static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s); -static warn_unused int js_parse_function_decl2(JSParseState *s, +static __exception int js_parse_function_decl2(JSParseState *s, JSParseFunctionEnum func_type, JSFunctionKindEnum func_kind, JSAtom func_name, @@ -22222,9 +22314,9 @@ static warn_unused int js_parse_function_decl2(JSParseState *s, int function_line_num, JSParseExportEnum export_flag, JSFunctionDef **pfd); -static warn_unused int js_parse_assign_expr2(JSParseState *s, int parse_flags); -static warn_unused int js_parse_assign_expr(JSParseState *s); -static warn_unused int js_parse_unary(JSParseState *s, int parse_flags); +static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags); +static __exception int js_parse_assign_expr(JSParseState *s); +static __exception int js_parse_unary(JSParseState *s, int parse_flags); static void push_break_entry(JSFunctionDef *fd, BlockEnv *be, JSAtom label_name, int label_break, int label_cont, @@ -22251,7 +22343,7 @@ static int seal_template_obj(JSContext *ctx, JSValueConst obj) return 0; } -static warn_unused int js_parse_template(JSParseState *s, int call, int *argc) +static __exception int js_parse_template(JSParseState *s, int call, int *argc) { JSContext *ctx = s->ctx; JSValue raw_array, template_object; @@ -22382,7 +22474,7 @@ static BOOL token_is_ident(int tok) } /* if the property is an expression, name = JS_ATOM_NULL */ -static int warn_unused js_parse_property_name(JSParseState *s, +static int __exception js_parse_property_name(JSParseState *s, JSAtom *pname, BOOL allow_method, BOOL allow_var, BOOL allow_private) @@ -22401,7 +22493,8 @@ static int warn_unused js_parse_property_name(JSParseState *s, if (next_token(s)) goto fail1; if (s->token.val == ':' || s->token.val == ',' || - s->token.val == '}' || s->token.val == '(') { + s->token.val == '}' || s->token.val == '(' || + s->token.val == '=') { is_non_reserved_ident = TRUE; goto ident_found; } @@ -22417,7 +22510,8 @@ static int warn_unused js_parse_property_name(JSParseState *s, if (next_token(s)) goto fail1; if (s->token.val == ':' || s->token.val == ',' || - s->token.val == '}' || s->token.val == '(') { + s->token.val == '}' || s->token.val == '(' || + s->token.val == '=') { is_non_reserved_ident = TRUE; goto ident_found; } @@ -22524,7 +22618,7 @@ static int js_parse_get_pos(JSParseState *s, JSParsePos *sp) return 0; } -static warn_unused int js_parse_seek_token(JSParseState *s, const JSParsePos *sp) +static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp) { s->token.line_num = sp->last_line_num; s->line_num = sp->line_num; @@ -22730,7 +22824,7 @@ static void set_object_name_computed(JSParseState *s) } } -static warn_unused int js_parse_object_literal(JSParseState *s) +static __exception int js_parse_object_literal(JSParseState *s) { JSAtom name = JS_ATOM_NULL; const uint8_t *start_ptr; @@ -22848,22 +22942,20 @@ static warn_unused int js_parse_object_literal(JSParseState *s) #define PF_IN_ACCEPTED (1 << 0) /* allow function calls parsing in js_parse_postfix_expr() */ #define PF_POSTFIX_CALL (1 << 1) -/* allow arrow functions parsing in js_parse_postfix_expr() */ -#define PF_ARROW_FUNC (1 << 2) /* allow the exponentiation operator in js_parse_unary() */ -#define PF_POW_ALLOWED (1 << 3) +#define PF_POW_ALLOWED (1 << 2) /* forbid the exponentiation operator in js_parse_unary() */ -#define PF_POW_FORBIDDEN (1 << 4) +#define PF_POW_FORBIDDEN (1 << 3) -static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags); +static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags); -static warn_unused int js_parse_left_hand_side_expr(JSParseState *s) +static __exception int js_parse_left_hand_side_expr(JSParseState *s) { return js_parse_postfix_expr(s, PF_POSTFIX_CALL); } /* XXX: could generate specific bytecode */ -static warn_unused int js_parse_class_default_ctor(JSParseState *s, +static __exception int js_parse_class_default_ctor(JSParseState *s, BOOL has_super, JSFunctionDef **pfd) { @@ -22950,11 +23042,12 @@ static JSAtom get_private_setter_name(JSContext *ctx, JSAtom name) typedef struct { JSFunctionDef *fields_init_fd; int computed_fields_count; - BOOL has_brand; + BOOL need_brand; int brand_push_pos; + BOOL is_static; } ClassFieldsDef; -static warn_unused int emit_class_init_start(JSParseState *s, +static __exception int emit_class_init_start(JSParseState *s, ClassFieldsDef *cf) { int label_add_brand; @@ -22965,41 +23058,27 @@ static warn_unused int emit_class_init_start(JSParseState *s, s->cur_func = cf->fields_init_fd; - /* XXX: would be better to add the code only if needed, maybe in a - later pass */ - emit_op(s, OP_push_false); /* will be patched later */ - cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos; - label_add_brand = emit_goto(s, OP_if_false, -1); - - emit_op(s, OP_scope_get_var); - emit_atom(s, JS_ATOM_this); - emit_u16(s, 0); + if (!cf->is_static) { + /* add the brand to the newly created instance */ + /* XXX: would be better to add the code only if needed, maybe in a + later pass */ + emit_op(s, OP_push_false); /* will be patched later */ + cf->brand_push_pos = cf->fields_init_fd->last_opcode_pos; + label_add_brand = emit_goto(s, OP_if_false, -1); - emit_op(s, OP_scope_get_var); - emit_atom(s, JS_ATOM_home_object); - emit_u16(s, 0); - - emit_op(s, OP_add_brand); - - emit_label(s, label_add_brand); + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); - s->cur_func = s->cur_func->parent; - return 0; -} + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_home_object); + emit_u16(s, 0); -static warn_unused int add_brand(JSParseState *s, ClassFieldsDef *cf) -{ - if (!cf->has_brand) { - /* define the brand field in 'this' of the initializer */ - if (!cf->fields_init_fd) { - if (emit_class_init_start(s, cf)) - return -1; - } - /* patch the start of the function to enable the OP_add_brand code */ - cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true; + emit_op(s, OP_add_brand); - cf->has_brand = TRUE; + emit_label(s, label_add_brand); } + s->cur_func = s->cur_func->parent; return 0; } @@ -23019,7 +23098,7 @@ static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf) } -static warn_unused int js_parse_class(JSParseState *s, BOOL is_class_expr, +static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, JSParseExportEnum export_flag) { JSContext *ctx = s->ctx; @@ -23106,7 +23185,8 @@ static warn_unused int js_parse_class(JSParseState *s, BOOL is_class_expr, ClassFieldsDef *cf = &class_fields[i]; cf->fields_init_fd = NULL; cf->computed_fields_count = 0; - cf->has_brand = FALSE; + cf->need_brand = FALSE; + cf->is_static = i; } ctor_fd = NULL; @@ -23116,11 +23196,51 @@ static warn_unused int js_parse_class(JSParseState *s, BOOL is_class_expr, goto fail; continue; } - is_static = (s->token.val == TOK_STATIC); + is_static = FALSE; + if (s->token.val == TOK_STATIC) { + int next = peek_token(s, TRUE); + if (!(next == ';' || next == '}' || next == '(' || next == '=')) + is_static = TRUE; + } prop_type = -1; if (is_static) { if (next_token(s)) goto fail; + if (s->token.val == '{') { + ClassFieldsDef *cf = &class_fields[is_static]; + JSFunctionDef *init; + if (!cf->fields_init_fd) { + if (emit_class_init_start(s, cf)) + goto fail; + } + s->cur_func = cf->fields_init_fd; + /* XXX: could try to avoid creating a new function and + reuse 'fields_init_fd' with a specific 'var' + scope */ + // stack is now: <empty> + if (js_parse_function_decl2(s, JS_PARSE_FUNC_CLASS_STATIC_INIT, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, s->token.line_num, + JS_PARSE_EXPORT_NONE, &init) < 0) { + goto fail; + } + // stack is now: fclosure + push_scope(s); + emit_op(s, OP_scope_get_var); + emit_atom(s, JS_ATOM_this); + emit_u16(s, 0); + // stack is now: fclosure this + emit_op(s, OP_swap); + // stack is now: this fclosure + emit_op(s, OP_call_method); + emit_u16(s, 0); + // stack is now: returnvalue + emit_op(s, OP_drop); + // stack is now: <empty> + pop_scope(s); + s->cur_func = s->cur_func->parent; + continue; + } /* allow "static" field name */ if (s->token.val == ';' || s->token.val == '=') { is_static = FALSE; @@ -23151,24 +23271,26 @@ static warn_unused int js_parse_class(JSParseState *s, BOOL is_class_expr, JSFunctionDef *method_fd; if (is_private) { - int idx, var_kind; + int idx, var_kind, is_static1; idx = find_private_class_field(ctx, fd, name, fd->scope_level); if (idx >= 0) { var_kind = fd->vars[idx].var_kind; + is_static1 = fd->vars[idx].is_static_private; if (var_kind == JS_VAR_PRIVATE_FIELD || var_kind == JS_VAR_PRIVATE_METHOD || var_kind == JS_VAR_PRIVATE_GETTER_SETTER || - var_kind == (JS_VAR_PRIVATE_GETTER + is_set)) { + var_kind == (JS_VAR_PRIVATE_GETTER + is_set) || + (var_kind == (JS_VAR_PRIVATE_GETTER + 1 - is_set) && + is_static != is_static1)) { goto private_field_already_defined; } fd->vars[idx].var_kind = JS_VAR_PRIVATE_GETTER_SETTER; } else { if (add_private_class_field(s, fd, name, - JS_VAR_PRIVATE_GETTER + is_set) < 0) + JS_VAR_PRIVATE_GETTER + is_set, is_static) < 0) goto fail; } - if (add_brand(s, &class_fields[is_static]) < 0) - goto fail; + class_fields[is_static].need_brand = TRUE; } if (js_parse_function_decl2(s, JS_PARSE_FUNC_GETTER + is_set, @@ -23190,7 +23312,7 @@ static warn_unused int js_parse_class(JSParseState *s, BOOL is_class_expr, goto fail; emit_atom(s, setter_name); ret = add_private_class_field(s, fd, setter_name, - JS_VAR_PRIVATE_SETTER); + JS_VAR_PRIVATE_SETTER, is_static); JS_FreeAtom(ctx, setter_name); if (ret < 0) goto fail; @@ -23225,7 +23347,7 @@ static warn_unused int js_parse_class(JSParseState *s, BOOL is_class_expr, goto private_field_already_defined; } if (add_private_class_field(s, fd, name, - JS_VAR_PRIVATE_FIELD) < 0) + JS_VAR_PRIVATE_FIELD, is_static) < 0) goto fail; emit_op(s, OP_private_symbol); emit_atom(s, name); @@ -23315,8 +23437,7 @@ static warn_unused int js_parse_class(JSParseState *s, BOOL is_class_expr, func_type = JS_PARSE_FUNC_CLASS_CONSTRUCTOR; } if (is_private) { - if (add_brand(s, &class_fields[is_static]) < 0) - goto fail; + class_fields[is_static].need_brand = TRUE; } if (js_parse_function_decl2(s, func_type, func_kind, JS_ATOM_NULL, start_ptr, s->token.line_num, JS_PARSE_EXPORT_NONE, &method_fd)) goto fail; @@ -23332,7 +23453,7 @@ static warn_unused int js_parse_class(JSParseState *s, BOOL is_class_expr, goto fail; } if (add_private_class_field(s, fd, name, - JS_VAR_PRIVATE_METHOD) < 0) + JS_VAR_PRIVATE_METHOD, is_static) < 0) goto fail; emit_op(s, OP_set_home_object); emit_op(s, OP_set_name); @@ -23382,12 +23503,29 @@ static warn_unused int js_parse_class(JSParseState *s, BOOL is_class_expr, if (next_token(s)) goto fail; - /* store the function to initialize the fields to that it can be - referenced by the constructor */ { ClassFieldsDef *cf = &class_fields[0]; int var_idx; + if (cf->need_brand) { + /* add a private brand to the prototype */ + emit_op(s, OP_dup); + emit_op(s, OP_null); + emit_op(s, OP_swap); + emit_op(s, OP_add_brand); + + /* define the brand field in 'this' of the initializer */ + if (!cf->fields_init_fd) { + if (emit_class_init_start(s, cf)) + goto fail; + } + /* patch the start of the function to enable the + OP_add_brand_instance code */ + cf->fields_init_fd->byte_code.buf[cf->brand_push_pos] = OP_push_true; + } + + /* store the function to initialize the fields to that it can be + referenced by the constructor */ var_idx = define_var(s, fd, JS_ATOM_class_fields_init, JS_VAR_DEF_CONST); if (var_idx < 0) @@ -23405,14 +23543,11 @@ static warn_unused int js_parse_class(JSParseState *s, BOOL is_class_expr, /* drop the prototype */ emit_op(s, OP_drop); - /* initialize the static fields */ - if (class_fields[1].fields_init_fd != NULL) { - ClassFieldsDef *cf = &class_fields[1]; + if (class_fields[1].need_brand) { + /* add a private brand to the class */ emit_op(s, OP_dup); - emit_class_init_end(s, cf); - emit_op(s, OP_call_method); - emit_u16(s, 0); - emit_op(s, OP_drop); + emit_op(s, OP_dup); + emit_op(s, OP_add_brand); } if (class_name != JS_ATOM_NULL) { @@ -23424,6 +23559,17 @@ static warn_unused int js_parse_class(JSParseState *s, BOOL is_class_expr, emit_atom(s, class_name); emit_u16(s, fd->scope_level); } + + /* initialize the static fields */ + if (class_fields[1].fields_init_fd != NULL) { + ClassFieldsDef *cf = &class_fields[1]; + emit_op(s, OP_dup); + emit_class_init_end(s, cf); + emit_op(s, OP_call_method); + emit_u16(s, 0); + emit_op(s, OP_drop); + } + pop_scope(s); pop_scope(s); @@ -23464,7 +23610,7 @@ static warn_unused int js_parse_class(JSParseState *s, BOOL is_class_expr, return -1; } -static warn_unused int js_parse_array_literal(JSParseState *s) +static __exception int js_parse_array_literal(JSParseState *s) { uint32_t idx; BOOL need_length; @@ -23500,7 +23646,7 @@ static warn_unused int js_parse_array_literal(JSParseState *s) if (js_parse_assign_expr(s)) return -1; emit_op(s, OP_define_field); - emit_u32(s, JS_AtomFromUInt32(idx)); + emit_u32(s, __JS_AtomFromUInt32(idx)); need_length = FALSE; } idx++; @@ -23610,7 +23756,7 @@ static BOOL has_with_scope(JSFunctionDef *s, int scope_level) return FALSE; } -static warn_unused int get_lvalue(JSParseState *s, int *popcode, int *pscope, +static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, JSAtom *pname, int *plabel, int *pdepth, BOOL keep, int tok) { @@ -23850,7 +23996,7 @@ static void put_lvalue(JSParseState *s, int opcode, int scope, } } -static warn_unused int js_parse_expr_paren(JSParseState *s) +static __exception int js_parse_expr_paren(JSParseState *s) { if (js_parse_expect(s, '(')) return -1; @@ -23868,7 +24014,7 @@ static int js_unsupported_keyword(JSParseState *s, JSAtom atom) JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom)); } -static warn_unused int js_define_var(JSParseState *s, JSAtom name, int tok) +static __exception int js_define_var(JSParseState *s, JSAtom name, int tok) { JSFunctionDef *fd = s->cur_func; JSVarDefEnum var_def_type; @@ -24416,8 +24562,8 @@ static void optional_chain_test(JSParseState *s, int *poptional_chaining_label, emit_label(s, label_next); } -/* allowed parse_flags: PF_POSTFIX_CALL, PF_ARROW_FUNC */ -static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags) +/* allowed parse_flags: PF_POSTFIX_CALL */ +static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) { FuncCallType call_type; int optional_chaining_label; @@ -24519,16 +24665,8 @@ static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags) } break; case '(': - if ((parse_flags & PF_ARROW_FUNC) && - js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) { - if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, - JS_FUNC_NORMAL, JS_ATOM_NULL, - s->token.ptr, s->token.line_num)) - return -1; - } else { - if (js_parse_expr_paren(s)) - return -1; - } + if (js_parse_expr_paren(s)) + return -1; break; case TOK_FUNCTION: if (js_parse_function_decl(s, JS_PARSE_FUNC_EXPR, @@ -24568,14 +24706,8 @@ static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags) if (s->token.u.ident.is_reserved) { return js_parse_error_reserved_identifier(s); } - if ((parse_flags & PF_ARROW_FUNC) && - peek_token(s, TRUE) == TOK_ARROW) { - if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, - JS_FUNC_NORMAL, JS_ATOM_NULL, - s->token.ptr, s->token.line_num)) - return -1; - } else if (token_is_pseudo_keyword(s, JS_ATOM_async) && - peek_token(s, TRUE) != '\n') { + if (token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, TRUE) != '\n') { const uint8_t *source_ptr; int source_line_num; @@ -24588,15 +24720,6 @@ static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags) JS_FUNC_ASYNC, JS_ATOM_NULL, source_ptr, source_line_num)) return -1; - } else if ((parse_flags & PF_ARROW_FUNC) && - ((s->token.val == '(' && - js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) || - (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved && - peek_token(s, TRUE) == TOK_ARROW))) { - if (js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, - JS_FUNC_ASYNC, JS_ATOM_NULL, - source_ptr, source_line_num)) - return -1; } else { name = JS_DupAtom(s->ctx, JS_ATOM_async); goto do_get_var; @@ -24608,8 +24731,10 @@ static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags) return -1; } name = JS_DupAtom(s->ctx, s->token.u.ident.atom); - if (next_token(s)) /* update line number before emitting code */ + if (next_token(s)) { /* update line number before emitting code */ + JS_FreeAtom(s->ctx, name); return -1; + } do_get_var: emit_op(s, OP_scope_get_var); emit_u32(s, name); @@ -24756,6 +24881,25 @@ static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags) fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2; drop_count = 2; break; + case OP_get_field_opt_chain: + { + int opt_chain_label, next_label; + opt_chain_label = get_u32(fd->byte_code.buf + + fd->last_opcode_pos + 1 + 4 + 1); + /* keep the object on the stack */ + fd->byte_code.buf[fd->last_opcode_pos] = OP_get_field2; + fd->byte_code.size = fd->last_opcode_pos + 1 + 4; + next_label = emit_goto(s, OP_goto, -1); + emit_label(s, opt_chain_label); + /* need an additional undefined value for the + case where the optional field does not + exists */ + emit_op(s, OP_undefined); + emit_label(s, next_label); + drop_count = 2; + opcode = OP_get_field; + } + break; case OP_scope_get_private_field: /* keep the object on the stack */ fd->byte_code.buf[fd->last_opcode_pos] = OP_scope_get_private_field2; @@ -24766,6 +24910,25 @@ static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags) fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2; drop_count = 2; break; + case OP_get_array_el_opt_chain: + { + int opt_chain_label, next_label; + opt_chain_label = get_u32(fd->byte_code.buf + + fd->last_opcode_pos + 1 + 1); + /* keep the object on the stack */ + fd->byte_code.buf[fd->last_opcode_pos] = OP_get_array_el2; + fd->byte_code.size = fd->last_opcode_pos + 1; + next_label = emit_goto(s, OP_goto, -1); + emit_label(s, opt_chain_label); + /* need an additional undefined value for the + case where the optional field does not + exists */ + emit_op(s, OP_undefined); + emit_label(s, next_label); + drop_count = 2; + opcode = OP_get_array_el; + } + break; case OP_scope_get_var: { JSAtom name; @@ -25048,43 +25211,89 @@ static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags) break; } } - if (optional_chaining_label >= 0) - emit_label(s, optional_chaining_label); + if (optional_chaining_label >= 0) { + JSFunctionDef *fd = s->cur_func; + int opcode; + emit_label_raw(s, optional_chaining_label); + /* modify the last opcode so that it is an indicator of an + optional chain */ + opcode = get_prev_opcode(fd); + if (opcode == OP_get_field || opcode == OP_get_array_el) { + if (opcode == OP_get_field) + opcode = OP_get_field_opt_chain; + else + opcode = OP_get_array_el_opt_chain; + fd->byte_code.buf[fd->last_opcode_pos] = opcode; + } else { + fd->last_opcode_pos = -1; + } + } return 0; } -static warn_unused int js_parse_delete(JSParseState *s) +static __exception int js_parse_delete(JSParseState *s) { JSFunctionDef *fd = s->cur_func; JSAtom name; + int opcode; if (next_token(s)) return -1; if (js_parse_unary(s, PF_POW_FORBIDDEN)) return -1; - switch (get_prev_opcode(fd)) { + switch(opcode = get_prev_opcode(fd)) { case OP_get_field: + case OP_get_field_opt_chain: { JSValue val; - int ret; - + int ret, opt_chain_label, next_label; + if (opcode == OP_get_field_opt_chain) { + opt_chain_label = get_u32(fd->byte_code.buf + + fd->last_opcode_pos + 1 + 4 + 1); + } else { + opt_chain_label = -1; + } name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); fd->byte_code.size = fd->last_opcode_pos; - fd->last_opcode_pos = -1; val = JS_AtomToValue(s->ctx, name); ret = emit_push_const(s, val, 1); JS_FreeValue(s->ctx, val); JS_FreeAtom(s->ctx, name); if (ret) return ret; + emit_op(s, OP_delete); + if (opt_chain_label >= 0) { + next_label = emit_goto(s, OP_goto, -1); + emit_label(s, opt_chain_label); + /* if the optional chain is not taken, return 'true' */ + emit_op(s, OP_drop); + emit_op(s, OP_push_true); + emit_label(s, next_label); + } + fd->last_opcode_pos = -1; } - goto do_delete; + break; case OP_get_array_el: fd->byte_code.size = fd->last_opcode_pos; fd->last_opcode_pos = -1; - do_delete: emit_op(s, OP_delete); break; + case OP_get_array_el_opt_chain: + { + int opt_chain_label, next_label; + opt_chain_label = get_u32(fd->byte_code.buf + + fd->last_opcode_pos + 1 + 1); + fd->byte_code.size = fd->last_opcode_pos; + emit_op(s, OP_delete); + next_label = emit_goto(s, OP_goto, -1); + emit_label(s, opt_chain_label); + /* if the optional chain is not taken, return 'true' */ + emit_op(s, OP_drop); + emit_op(s, OP_push_true); + emit_label(s, next_label); + fd->last_opcode_pos = -1; + } + break; case OP_scope_get_var: /* 'delete this': this is not a reference */ name = get_u32(fd->byte_code.buf + fd->last_opcode_pos + 1); @@ -25099,6 +25308,8 @@ static warn_unused int js_parse_delete(JSParseState *s) case OP_scope_get_private_field: return js_parse_error(s, "cannot delete a private class field"); case OP_get_super_value: + fd->byte_code.size = fd->last_opcode_pos; + fd->last_opcode_pos = -1; emit_op(s, OP_throw_error); emit_atom(s, JS_ATOM_NULL); emit_u8(s, JS_THROW_ERROR_DELETE_SUPER); @@ -25112,8 +25323,8 @@ static warn_unused int js_parse_delete(JSParseState *s) return 0; } -/* allowed parse_flags: PF_ARROW_FUNC, PF_POW_ALLOWED, PF_POW_FORBIDDEN */ -static warn_unused int js_parse_unary(JSParseState *s, int parse_flags) +/* allowed parse_flags: PF_POW_ALLOWED, PF_POW_FORBIDDEN */ +static __exception int js_parse_unary(JSParseState *s, int parse_flags) { int op; @@ -25198,12 +25409,12 @@ static warn_unused int js_parse_unary(JSParseState *s, int parse_flags) return -1; if (js_parse_unary(s, PF_POW_FORBIDDEN)) return -1; + s->cur_func->has_await = TRUE; emit_op(s, OP_await); parse_flags = 0; break; default: - if (js_parse_postfix_expr(s, (parse_flags & PF_ARROW_FUNC) | - PF_POSTFIX_CALL)) + if (js_parse_postfix_expr(s, PF_POSTFIX_CALL)) return -1; if (!s->got_lf && (s->token.val == TOK_DEC || s->token.val == TOK_INC)) { @@ -25260,18 +25471,40 @@ static warn_unused int js_parse_unary(JSParseState *s, int parse_flags) return 0; } -/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ -static warn_unused int js_parse_expr_binary(JSParseState *s, int level, +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_expr_binary(JSParseState *s, int level, int parse_flags) { int op, opcode; if (level == 0) { - return js_parse_unary(s, (parse_flags & PF_ARROW_FUNC) | - PF_POW_ALLOWED); + return js_parse_unary(s, PF_POW_ALLOWED); + } else if (s->token.val == TOK_PRIVATE_NAME && + (parse_flags & PF_IN_ACCEPTED) && level == 4 && + peek_token(s, FALSE) == TOK_IN) { + JSAtom atom; + + atom = JS_DupAtom(s->ctx, s->token.u.ident.atom); + if (next_token(s)) + goto fail_private_in; + if (s->token.val != TOK_IN) + goto fail_private_in; + if (next_token(s)) + goto fail_private_in; + if (js_parse_expr_binary(s, level - 1, parse_flags)) { + fail_private_in: + JS_FreeAtom(s->ctx, atom); + return -1; + } + emit_op(s, OP_scope_in_private_field); + emit_atom(s, atom); + emit_u16(s, s->cur_func->scope_level); + JS_FreeAtom(s->ctx, atom); + return 0; + } else { + if (js_parse_expr_binary(s, level - 1, parse_flags)) + return -1; } - if (js_parse_expr_binary(s, level - 1, parse_flags)) - return -1; for(;;) { op = s->token.val; switch(level) { @@ -25400,15 +25633,15 @@ static warn_unused int js_parse_expr_binary(JSParseState *s, int level, } if (next_token(s)) return -1; - if (js_parse_expr_binary(s, level - 1, parse_flags & ~PF_ARROW_FUNC)) + if (js_parse_expr_binary(s, level - 1, parse_flags)) return -1; emit_op(s, opcode); } return 0; } -/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ -static warn_unused int js_parse_logical_and_or(JSParseState *s, int op, +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_logical_and_or(JSParseState *s, int op, int parse_flags) { int label1; @@ -25431,11 +25664,11 @@ static warn_unused int js_parse_logical_and_or(JSParseState *s, int op, emit_op(s, OP_drop); if (op == TOK_LAND) { - if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC)) + if (js_parse_expr_binary(s, 8, parse_flags)) return -1; } else { if (js_parse_logical_and_or(s, TOK_LAND, - parse_flags & ~PF_ARROW_FUNC)) + parse_flags)) return -1; } if (s->token.val != op) { @@ -25450,7 +25683,7 @@ static warn_unused int js_parse_logical_and_or(JSParseState *s, int op, return 0; } -static warn_unused int js_parse_coalesce_expr(JSParseState *s, int parse_flags) +static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags) { int label1; @@ -25467,7 +25700,7 @@ static warn_unused int js_parse_coalesce_expr(JSParseState *s, int parse_flags) emit_goto(s, OP_if_false, label1); emit_op(s, OP_drop); - if (js_parse_expr_binary(s, 8, parse_flags & ~PF_ARROW_FUNC)) + if (js_parse_expr_binary(s, 8, parse_flags)) return -1; if (s->token.val != TOK_DOUBLE_QUESTION_MARK) break; @@ -25477,8 +25710,8 @@ static warn_unused int js_parse_coalesce_expr(JSParseState *s, int parse_flags) return 0; } -/* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ -static warn_unused int js_parse_cond_expr(JSParseState *s, int parse_flags) +/* allowed parse_flags: PF_IN_ACCEPTED */ +static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags) { int label1, label2; @@ -25509,7 +25742,7 @@ static warn_unused int js_parse_cond_expr(JSParseState *s, int parse_flags) static void emit_return(JSParseState *s, BOOL hasval); /* allowed parse_flags: PF_IN_ACCEPTED */ -static warn_unused int js_parse_assign_expr2(JSParseState *s, int parse_flags) +static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) { int opcode, op, scope; JSAtom name0 = JS_ATOM_NULL; @@ -25571,7 +25804,6 @@ static warn_unused int js_parse_assign_expr2(JSParseState *s, int parse_flags) /* OP_async_yield_star takes the value as parameter */ emit_op(s, OP_get_field); emit_atom(s, JS_ATOM_value); - emit_op(s, OP_await); emit_op(s, OP_async_yield_star); } else { /* OP_yield_star takes (value, done) as parameter */ @@ -25653,12 +25885,50 @@ static warn_unused int js_parse_assign_expr2(JSParseState *s, int parse_flags) emit_label(s, label_next); } return 0; + } else if (s->token.val == '(' && + js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) { + return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, s->token.line_num); + } else if (token_is_pseudo_keyword(s, JS_ATOM_async)) { + const uint8_t *source_ptr; + int source_line_num, tok; + JSParsePos pos; + + /* fast test */ + tok = peek_token(s, TRUE); + if (tok == TOK_FUNCTION || tok == '\n') + goto next; + + source_ptr = s->token.ptr; + source_line_num = s->token.line_num; + js_parse_get_pos(s, &pos); + if (next_token(s)) + return -1; + if ((s->token.val == '(' && + js_parse_skip_parens_token(s, NULL, TRUE) == TOK_ARROW) || + (s->token.val == TOK_IDENT && !s->token.u.ident.is_reserved && + peek_token(s, TRUE) == TOK_ARROW)) { + return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, + JS_FUNC_ASYNC, JS_ATOM_NULL, + source_ptr, source_line_num); + } else { + /* undo the token parsing */ + if (js_parse_seek_token(s, &pos)) + return -1; + } + } else if (s->token.val == TOK_IDENT && + peek_token(s, TRUE) == TOK_ARROW) { + return js_parse_function_decl(s, JS_PARSE_FUNC_ARROW, + JS_FUNC_NORMAL, JS_ATOM_NULL, + s->token.ptr, s->token.line_num); } + next: if (s->token.val == TOK_IDENT) { /* name0 is used to check for OP_set_name pattern, not duplicated */ name0 = s->token.u.ident.atom; } - if (js_parse_cond_expr(s, parse_flags | PF_ARROW_FUNC)) + if (js_parse_cond_expr(s, parse_flags)) return -1; op = s->token.val; @@ -25755,13 +26025,13 @@ static warn_unused int js_parse_assign_expr2(JSParseState *s, int parse_flags) return 0; } -static warn_unused int js_parse_assign_expr(JSParseState *s) +static __exception int js_parse_assign_expr(JSParseState *s) { return js_parse_assign_expr2(s, PF_IN_ACCEPTED); } /* allowed parse_flags: PF_IN_ACCEPTED */ -static warn_unused int js_parse_expr2(JSParseState *s, int parse_flags) +static __exception int js_parse_expr2(JSParseState *s, int parse_flags) { BOOL comma = FALSE; for(;;) { @@ -25785,7 +26055,7 @@ static warn_unused int js_parse_expr2(JSParseState *s, int parse_flags) return 0; } -static warn_unused int js_parse_expr(JSParseState *s) +static __exception int js_parse_expr(JSParseState *s) { return js_parse_expr2(s, PF_IN_ACCEPTED); } @@ -25813,7 +26083,7 @@ static void pop_break_entry(JSFunctionDef *fd) fd->top_break = be->prev; } -static warn_unused int emit_break(JSParseState *s, JSAtom name, int is_cont) +static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont) { BlockEnv *top; int i, scope_level; @@ -25865,61 +26135,61 @@ static warn_unused int emit_break(JSParseState *s, JSAtom name, int is_cont) static void emit_return(JSParseState *s, BOOL hasval) { BlockEnv *top; - int drop_count; - drop_count = 0; + if (s->cur_func->func_kind != JS_FUNC_NORMAL) { + if (!hasval) { + /* no value: direct return in case of async generator */ + emit_op(s, OP_undefined); + hasval = TRUE; + } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { + /* the await must be done before handling the "finally" in + case it raises an exception */ + emit_op(s, OP_await); + } + } + top = s->cur_func->top_break; while (top != NULL) { - /* XXX: emit the appropriate OP_leave_scope opcodes? Probably not - required as all local variables will be closed upon returning - from JS_CallInternal, but not in the same order. */ - if (top->has_iterator) { - /* with 'yield', the exact number of OP_drop to emit is - unknown, so we use a specific operation to look for - the catch offset */ + if (top->has_iterator || top->label_finally != -1) { if (!hasval) { emit_op(s, OP_undefined); hasval = TRUE; } - emit_op(s, OP_iterator_close_return); - if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { - int label_next, label_next2; - - emit_op(s, OP_drop); /* catch offset */ - emit_op(s, OP_drop); /* next */ - emit_op(s, OP_get_field2); - emit_atom(s, JS_ATOM_return); - /* stack: iter_obj return_func */ - emit_op(s, OP_dup); - emit_op(s, OP_is_undefined_or_null); - label_next = emit_goto(s, OP_if_true, -1); - emit_op(s, OP_call_method); - emit_u16(s, 0); - emit_op(s, OP_iterator_check_object); - emit_op(s, OP_await); - label_next2 = emit_goto(s, OP_goto, -1); - emit_label(s, label_next); - emit_op(s, OP_drop); - emit_label(s, label_next2); - emit_op(s, OP_drop); + /* Remove the stack elements up to and including the catch + offset. When 'yield' is used in an expression we have + no easy way to count them, so we use this specific + instruction instead. */ + emit_op(s, OP_nip_catch); + /* stack: iter_obj next ret_val */ + if (top->has_iterator) { + if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { + int label_next, label_next2; + emit_op(s, OP_nip); /* next */ + emit_op(s, OP_swap); + emit_op(s, OP_get_field2); + emit_atom(s, JS_ATOM_return); + /* stack: iter_obj return_func */ + emit_op(s, OP_dup); + emit_op(s, OP_is_undefined_or_null); + label_next = emit_goto(s, OP_if_true, -1); + emit_op(s, OP_call_method); + emit_u16(s, 0); + emit_op(s, OP_iterator_check_object); + emit_op(s, OP_await); + label_next2 = emit_goto(s, OP_goto, -1); + emit_label(s, label_next); + emit_op(s, OP_drop); + emit_label(s, label_next2); + emit_op(s, OP_drop); + } else { + emit_op(s, OP_rot3r); + emit_op(s, OP_undefined); /* dummy catch offset */ + emit_op(s, OP_iterator_close); + } } else { - emit_op(s, OP_iterator_close); - } - drop_count = -3; - } - drop_count += top->drop_count; - if (top->label_finally != -1) { - while(drop_count) { - /* must keep the stack top if hasval */ - emit_op(s, hasval ? OP_nip : OP_drop); - drop_count--; + /* execute the "finally" block */ + emit_goto(s, OP_gosub, top->label_finally); } - if (!hasval) { - /* must push return value to keep same stack size */ - emit_op(s, OP_undefined); - hasval = TRUE; - } - emit_goto(s, OP_gosub, top->label_finally); } top = top->prev; } @@ -25936,20 +26206,15 @@ static void emit_return(JSParseState *s, BOOL hasval) label_return = -1; } - /* XXX: if this is not initialized, should throw the - ReferenceError in the caller realm */ - emit_op(s, OP_scope_get_var); + /* The error should be raised in the caller context, so we use + a specific opcode */ + emit_op(s, OP_scope_get_var_checkthis); emit_atom(s, JS_ATOM_this); emit_u16(s, 0); emit_label(s, label_return); emit_op(s, OP_return); } else if (s->cur_func->func_kind != JS_FUNC_NORMAL) { - if (!hasval) { - emit_op(s, OP_undefined); - } else if (s->cur_func->func_kind == JS_FUNC_ASYNC_GENERATOR) { - emit_op(s, OP_await); - } emit_op(s, OP_return_async); } else { emit_op(s, hasval ? OP_return : OP_return_undef); @@ -25962,15 +26227,15 @@ static void emit_return(JSParseState *s, BOOL hasval) #define DECL_MASK_OTHER (1 << 2) /* all other declarations */ #define DECL_MASK_ALL (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER) -static warn_unused int js_parse_statement_or_decl(JSParseState *s, +static __exception int js_parse_statement_or_decl(JSParseState *s, int decl_mask); -static warn_unused int js_parse_statement(JSParseState *s) +static __exception int js_parse_statement(JSParseState *s) { return js_parse_statement_or_decl(s, 0); } -static warn_unused int js_parse_block(JSParseState *s) +static __exception int js_parse_block(JSParseState *s) { if (js_parse_expect(s, '{')) return -1; @@ -25990,7 +26255,7 @@ static warn_unused int js_parse_block(JSParseState *s) } /* allowed parse_flags: PF_IN_ACCEPTED */ -static warn_unused int js_parse_var(JSParseState *s, int parse_flags, int tok, +static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok, BOOL export_flag) { JSContext *ctx = s->ctx; @@ -26096,7 +26361,6 @@ static int is_let(JSParseState *s, int decl_mask) int res = FALSE; if (token_is_pseudo_keyword(s, JS_ATOM_let)) { -#if 1 JSParsePos pos; js_parse_get_pos(s, &pos); for (;;) { @@ -26129,19 +26393,13 @@ static int is_let(JSParseState *s, int decl_mask) if (js_parse_seek_token(s, &pos)) { res = -1; } -#else - int tok = peek_token(s, TRUE); - if (tok == '{' || tok == TOK_IDENT || peek_token(s, FALSE) == '[') { - res = TRUE; - } -#endif } return res; } /* XXX: handle IteratorClose when exiting the loop before the enumeration is done */ -static warn_unused int js_parse_for_in_of(JSParseState *s, int label_name, +static __exception int js_parse_for_in_of(JSParseState *s, int label_name, BOOL is_async) { JSContext *ctx = s->ctx; @@ -26216,6 +26474,9 @@ static warn_unused int js_parse_for_in_of(JSParseState *s, int label_name, emit_atom(s, var_name); emit_u16(s, fd->scope_level); } + } else if (!is_async && token_is_pseudo_keyword(s, JS_ATOM_async) && + peek_token(s, FALSE) == TOK_OF) { + return js_parse_error(s, "'for of' expression cannot start with 'async'"); } else { int skip_bits; if ((s->token.val == '[' || s->token.val == '{') @@ -26372,7 +26633,7 @@ static void set_eval_ret_undefined(JSParseState *s) } } -static warn_unused int js_parse_statement_or_decl(JSParseState *s, +static __exception int js_parse_statement_or_decl(JSParseState *s, int decl_mask) { JSContext *ctx = s->ctx; @@ -26432,6 +26693,10 @@ static warn_unused int js_parse_statement_or_decl(JSParseState *s, js_parse_error(s, "return not in a function"); goto fail; } + if (s->cur_func->func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) { + js_parse_error(s, "return in a static initializer block"); + goto fail; + } if (next_token(s)) goto fail; if (s->token.val != ';' && s->token.val != '}' && !s->got_lf) { @@ -26600,6 +26865,7 @@ static warn_unused int js_parse_statement_or_decl(JSParseState *s, is_async = TRUE; if (next_token(s)) goto fail; + s->cur_func->has_await = TRUE; } if (js_parse_expect(s, '(')) goto fail; @@ -27130,6 +27396,9 @@ static JSModuleDef *js_new_module_def(JSContext *ctx, JSAtom name) m->func_obj = JS_UNDEFINED; m->eval_exception = JS_UNDEFINED; m->meta_obj = JS_UNDEFINED; + m->promise = JS_UNDEFINED; + m->resolving_funcs[0] = JS_UNDEFINED; + m->resolving_funcs[1] = JS_UNDEFINED; list_add_tail(&m->link, &ctx->loaded_modules); return m; } @@ -27151,6 +27420,9 @@ static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, JS_MarkValue(rt, m->func_obj, mark_func); JS_MarkValue(rt, m->eval_exception, mark_func); JS_MarkValue(rt, m->meta_obj, mark_func); + JS_MarkValue(rt, m->promise, mark_func); + JS_MarkValue(rt, m->resolving_funcs[0], mark_func); + JS_MarkValue(rt, m->resolving_funcs[1], mark_func); } static void js_free_module_def(JSContext *ctx, JSModuleDef *m) @@ -27181,11 +27453,15 @@ static void js_free_module_def(JSContext *ctx, JSModuleDef *m) JS_FreeAtom(ctx, mi->import_name); } js_free(ctx, m->import_entries); + js_free(ctx, m->async_parent_modules); JS_FreeValue(ctx, m->module_ns); JS_FreeValue(ctx, m->func_obj); JS_FreeValue(ctx, m->eval_exception); JS_FreeValue(ctx, m->meta_obj); + JS_FreeValue(ctx, m->promise); + JS_FreeValue(ctx, m->resolving_funcs[0]); + JS_FreeValue(ctx, m->resolving_funcs[1]); list_del(&m->link); js_free(ctx, m); } @@ -27346,6 +27622,7 @@ static char *js_default_module_normalize_name(JSContext *ctx, { char *filename, *p; const char *r; + int cap; int len; if (name[0] != '.') { @@ -27359,7 +27636,8 @@ static char *js_default_module_normalize_name(JSContext *ctx, else len = 0; - filename = js_malloc(ctx, len + strlen(name) + 1 + 1); + cap = len + strlen(name) + 1 + 1; + filename = js_malloc(ctx, cap); if (!filename) return NULL; memcpy(filename, base_name, len); @@ -27391,8 +27669,8 @@ static char *js_default_module_normalize_name(JSContext *ctx, } } if (filename[0] != '\0') - strcat(filename, "/"); - strcat(filename, r); + pstrcat(filename, cap, "/"); + pstrcat(filename, cap, r); // printf("normalize: %s %s -> %s\n", base_name, name, filename); return filename; } @@ -27692,7 +27970,7 @@ static int find_exported_name(GetExportNamesState *s, JSAtom name) return -1; } -static warn_unused int get_exported_names(JSContext *ctx, +static __exception int get_exported_names(JSContext *ctx, GetExportNamesState *s, JSModuleDef *m, BOOL from_star) { @@ -27774,13 +28052,11 @@ static int exported_names_cmp(const void *p1, const void *p2, void *opaque) return ret; } -static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m); - static JSValue js_module_ns_autoinit(JSContext *ctx, JSObject *p, JSAtom atom, void *opaque) { JSModuleDef *m = opaque; - return js_get_module_ns(ctx, m); + return JS_GetModuleNamespace(ctx, m); } static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) @@ -27885,7 +28161,7 @@ static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) return JS_EXCEPTION; } -static JSValue js_get_module_ns(JSContext *ctx, JSModuleDef *m) +JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m) { if (JS_IsUndefined(m->module_ns)) { JSValue val; @@ -28040,7 +28316,8 @@ static int js_create_module_function(JSContext *ctx, JSModuleDef *m) /* Prepare a module to be executed by resolving all the imported variables. */ -static int js_link_module(JSContext *ctx, JSModuleDef *m) +static int js_inner_module_linking(JSContext *ctx, JSModuleDef *m, + JSModuleDef **pstack_top, int index) { int i; JSImportEntry *mi; @@ -28050,21 +28327,47 @@ static int js_link_module(JSContext *ctx, JSModuleDef *m) BOOL is_c_module; JSValue ret_val; - if (m->instantiated) - return 0; - m->instantiated = TRUE; + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + return -1; + } #ifdef DUMP_MODULE_RESOLVE { char buf1[ATOM_GET_STR_BUF_SIZE]; - printf("start instantiating module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); + printf("js_inner_module_linking '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); } #endif + if (m->status == JS_MODULE_STATUS_LINKING || + m->status == JS_MODULE_STATUS_LINKED || + m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED) + return index; + + assert(m->status == JS_MODULE_STATUS_UNLINKED); + m->status = JS_MODULE_STATUS_LINKING; + m->dfs_index = index; + m->dfs_ancestor_index = index; + index++; + /* push 'm' on stack */ + m->stack_prev = *pstack_top; + *pstack_top = m; + for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i]; - if (js_link_module(ctx, rme->module) < 0) + m1 = rme->module; + index = js_inner_module_linking(ctx, m1, pstack_top, index); + if (index < 0) goto fail; + assert(m1->status == JS_MODULE_STATUS_LINKING || + m1->status == JS_MODULE_STATUS_LINKED || + m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m1->status == JS_MODULE_STATUS_EVALUATED); + if (m1->status == JS_MODULE_STATUS_LINKING) { + m->dfs_ancestor_index = min_int(m->dfs_ancestor_index, + m1->dfs_ancestor_index); + } } #ifdef DUMP_MODULE_RESOLVE @@ -28119,7 +28422,7 @@ static int js_link_module(JSContext *ctx, JSModuleDef *m) if (mi->import_name == JS_ATOM__star_) { JSValue val; /* name space import */ - val = js_get_module_ns(ctx, m1); + val = JS_GetModuleNamespace(ctx, m1); if (JS_IsException(val)) goto fail; set_value(ctx, &var_refs[mi->var_idx]->value, val); @@ -28143,7 +28446,7 @@ static int js_link_module(JSContext *ctx, JSModuleDef *m) JSModuleDef *m2; /* name space import from */ m2 = res_m->req_module_entries[res_me->u.req_module_idx].module; - val = js_get_module_ns(ctx, m2); + val = JS_GetModuleNamespace(ctx, m2); if (JS_IsException(val)) goto fail; var_ref = js_create_module_var(ctx, TRUE); @@ -28190,14 +28493,59 @@ static int js_link_module(JSContext *ctx, JSModuleDef *m) JS_FreeValue(ctx, ret_val); } + assert(m->dfs_ancestor_index <= m->dfs_index); + if (m->dfs_index == m->dfs_ancestor_index) { + for(;;) { + /* pop m1 from stack */ + m1 = *pstack_top; + *pstack_top = m1->stack_prev; + m1->status = JS_MODULE_STATUS_LINKED; + if (m1 == m) + break; + } + } + #ifdef DUMP_MODULE_RESOLVE - printf("done instantiate\n"); + printf("js_inner_module_linking done\n"); #endif - return 0; + return index; fail: return -1; } +/* Prepare a module to be executed by resolving all the imported + variables. */ +static int js_link_module(JSContext *ctx, JSModuleDef *m) +{ + JSModuleDef *stack_top, *m1; + +#ifdef DUMP_MODULE_RESOLVE + { + char buf1[ATOM_GET_STR_BUF_SIZE]; + printf("js_link_module '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); + } +#endif + assert(m->status == JS_MODULE_STATUS_UNLINKED || + m->status == JS_MODULE_STATUS_LINKED || + m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED); + stack_top = NULL; + if (js_inner_module_linking(ctx, m, &stack_top, 0) < 0) { + while (stack_top != NULL) { + m1 = stack_top; + assert(m1->status == JS_MODULE_STATUS_LINKING); + m1->status = JS_MODULE_STATUS_UNLINKED; + stack_top = m1->stack_prev; + } + return -1; + } + assert(stack_top == NULL); + assert(m->status == JS_MODULE_STATUS_LINKED || + m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED); + return 0; +} + /* return JS_ATOM_NULL if the name cannot be found. Only works with not striped bytecode functions. */ JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels) @@ -28206,8 +28554,8 @@ JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels) JSFunctionBytecode *b; JSObject *p; /* XXX: currently we just use the filename of the englobing - function. It does not work for eval(). Need to add a - ScriptOrModule info in JSFunctionBytecode */ + function from the debug info. May need to add a ScriptOrModule + info in JSFunctionBytecode. */ sf = ctx->rt->current_stack_frame; if (!sf) return JS_ATOM_NULL; @@ -28216,15 +28564,23 @@ JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels) if (!sf) return JS_ATOM_NULL; } - if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT) - return JS_ATOM_NULL; - p = JS_VALUE_GET_OBJ(sf->cur_func); - if (!js_class_has_bytecode(p->class_id)) - return JS_ATOM_NULL; - b = p->u.func.function_bytecode; - if (!b->has_debug) - return JS_ATOM_NULL; - return JS_DupAtom(ctx, b->debug.filename); + for(;;) { + if (JS_VALUE_GET_TAG(sf->cur_func) != JS_TAG_OBJECT) + return JS_ATOM_NULL; + p = JS_VALUE_GET_OBJ(sf->cur_func); + if (!js_class_has_bytecode(p->class_id)) + return JS_ATOM_NULL; + b = p->u.func.function_bytecode; + if (!b->is_direct_or_indirect_eval) { + if (!b->has_debug) + return JS_ATOM_NULL; + return JS_DupAtom(ctx, b->debug.filename); + } else { + sf = sf->prev_frame; + if (!sf) + return JS_ATOM_NULL; + } + } } JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m) @@ -28267,29 +28623,110 @@ static JSValue js_import_meta(JSContext *ctx) return JS_GetImportMeta(ctx, m); } -/* used by os.Worker() and import() */ -JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename, - const char *filename) +static JSValue JS_NewModuleValue(JSContext *ctx, JSModuleDef *m) +{ + return JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); +} + +static JSValue js_load_module_rejected(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic, JSValue *func_data) { + JSValueConst *resolving_funcs = (JSValueConst *)func_data; + JSValueConst error; + JSValue ret; + + /* XXX: check if the test is necessary */ + if (argc >= 1) + error = argv[0]; + else + error = JS_UNDEFINED; + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, + 1, &error); + JS_FreeValue(ctx, ret); + return JS_UNDEFINED; +} + +static JSValue js_load_module_fulfilled(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic, JSValue *func_data) +{ + JSValueConst *resolving_funcs = (JSValueConst *)func_data; + JSModuleDef *m = JS_VALUE_GET_PTR(func_data[2]); + JSValue ret, ns; + + /* return the module namespace */ + ns = JS_GetModuleNamespace(ctx, m); + if (JS_IsException(ns)) { + JSValue err = JS_GetException(ctx); + js_load_module_rejected(ctx, JS_UNDEFINED, 1, (JSValueConst *)&err, 0, func_data); + return JS_UNDEFINED; + } + ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, + 1, (JSValueConst *)&ns); + JS_FreeValue(ctx, ret); + JS_FreeValue(ctx, ns); + return JS_UNDEFINED; +} + +static void JS_LoadModuleInternal(JSContext *ctx, const char *basename, + const char *filename, + JSValueConst *resolving_funcs) +{ + JSValue evaluate_promise; JSModuleDef *m; - JSValue ret, func_obj; + JSValue ret, err, func_obj, evaluate_resolving_funcs[2]; + JSValueConst func_data[3]; m = js_host_resolve_imported_module(ctx, basename, filename); if (!m) - return NULL; + goto fail; if (js_resolve_module(ctx, m) < 0) { js_free_modules(ctx, JS_FREE_MODULE_NOT_RESOLVED); - return NULL; + goto fail; } /* Evaluate the module code */ - func_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); - ret = JS_EvalFunction(ctx, func_obj); - if (JS_IsException(ret)) - return NULL; + func_obj = JS_NewModuleValue(ctx, m); + evaluate_promise = JS_EvalFunction(ctx, func_obj); + if (JS_IsException(evaluate_promise)) { + fail: + err = JS_GetException(ctx); + ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, + 1, (JSValueConst *)&err); + JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ + JS_FreeValue(ctx, err); + return; + } + + func_obj = JS_NewModuleValue(ctx, m); + func_data[0] = resolving_funcs[0]; + func_data[1] = resolving_funcs[1]; + func_data[2] = func_obj; + evaluate_resolving_funcs[0] = JS_NewCFunctionData(ctx, js_load_module_fulfilled, 0, 0, 3, func_data); + evaluate_resolving_funcs[1] = JS_NewCFunctionData(ctx, js_load_module_rejected, 0, 0, 3, func_data); + JS_FreeValue(ctx, func_obj); + ret = js_promise_then(ctx, evaluate_promise, 2, (JSValueConst *)evaluate_resolving_funcs); JS_FreeValue(ctx, ret); - return m; + JS_FreeValue(ctx, evaluate_resolving_funcs[0]); + JS_FreeValue(ctx, evaluate_resolving_funcs[1]); + JS_FreeValue(ctx, evaluate_promise); +} + +/* Return a promise or an exception in case of memory error. Used by + os.Worker() */ +JSValue JS_LoadModule(JSContext *ctx, const char *basename, + const char *filename) +{ + JSValue promise, resolving_funcs[2]; + + promise = JS_NewPromiseCapability(ctx, resolving_funcs); + if (JS_IsException(promise)) + return JS_EXCEPTION; + JS_LoadModuleInternal(ctx, basename, filename, + (JSValueConst *)resolving_funcs); + JS_FreeValue(ctx, resolving_funcs[0]); + JS_FreeValue(ctx, resolving_funcs[1]); + return promise; } static JSValue js_dynamic_import_job(JSContext *ctx, @@ -28298,9 +28735,8 @@ static JSValue js_dynamic_import_job(JSContext *ctx, JSValueConst *resolving_funcs = argv; JSValueConst basename_val = argv[2]; JSValueConst specifier = argv[3]; - JSModuleDef *m; const char *basename = NULL, *filename; - JSValue ret, err, ns; + JSValue ret, err; if (!JS_IsString(basename_val)) { JS_ThrowTypeError(ctx, "no function filename for import()"); @@ -28314,27 +28750,15 @@ static JSValue js_dynamic_import_job(JSContext *ctx, if (!filename) goto exception; - m = JS_RunModule(ctx, basename, filename); + JS_LoadModuleInternal(ctx, basename, filename, + resolving_funcs); JS_FreeCString(ctx, filename); - if (!m) - goto exception; - - /* return the module namespace */ - ns = js_get_module_ns(ctx, m); - if (JS_IsException(ns)) - goto exception; - - ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, - 1, &ns); - JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ - JS_FreeValue(ctx, ns); JS_FreeCString(ctx, basename); return JS_UNDEFINED; exception: - err = JS_GetException(ctx); ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, - 1, &err); + 1, (JSValueConst *)&err); JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ JS_FreeValue(ctx, err); JS_FreeCString(ctx, basename); @@ -28367,6 +28791,8 @@ static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier) args[2] = basename_val; args[3] = specifier; + /* cannot run JS_LoadModuleInternal synchronously because it would + cause an unexpected recursion in js_evaluate_module() */ JS_EnqueueJob(ctx, js_dynamic_import_job, 4, args); JS_FreeValue(ctx, basename_val); @@ -28375,63 +28801,400 @@ static JSValue js_dynamic_import(JSContext *ctx, JSValueConst specifier) return promise; } -/* Run the <eval> function of the module and of all its requested - modules. */ -static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m) +static void js_set_module_evaluated(JSContext *ctx, JSModuleDef *m) +{ + m->status = JS_MODULE_STATUS_EVALUATED; + if (!JS_IsUndefined(m->promise)) { + JSValue value, ret_val; + assert(m->cycle_root == m); + value = JS_UNDEFINED; + ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED, + 1, (JSValueConst *)&value); + JS_FreeValue(ctx, ret_val); + } +} + +typedef struct { + JSModuleDef **tab; + int count; + int size; +} ExecModuleList; + +/* XXX: slow. Could use a linked list instead of ExecModuleList */ +static BOOL find_in_exec_module_list(ExecModuleList *exec_list, JSModuleDef *m) +{ + int i; + for(i = 0; i < exec_list->count; i++) { + if (exec_list->tab[i] == m) + return TRUE; + } + return FALSE; +} + +static int gather_available_ancestors(JSContext *ctx, JSModuleDef *module, + ExecModuleList *exec_list) +{ + int i; + + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + return -1; + } + for(i = 0; i < module->async_parent_modules_count; i++) { + JSModuleDef *m = module->async_parent_modules[i]; + if (!find_in_exec_module_list(exec_list, m) && + !m->cycle_root->eval_has_exception) { + assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC); + assert(!m->eval_has_exception); + assert(m->async_evaluation); + assert(m->pending_async_dependencies > 0); + m->pending_async_dependencies--; + if (m->pending_async_dependencies == 0) { + if (js_resize_array(ctx, (void **)&exec_list->tab, sizeof(exec_list->tab[0]), &exec_list->size, exec_list->count + 1)) { + return -1; + } + exec_list->tab[exec_list->count++] = m; + if (!m->has_tla) { + if (gather_available_ancestors(ctx, m, exec_list)) + return -1; + } + } + } + } + return 0; +} + +static int exec_module_list_cmp(const void *p1, const void *p2, void *opaque) +{ + JSModuleDef *m1 = *(JSModuleDef **)p1; + JSModuleDef *m2 = *(JSModuleDef **)p2; + return (m1->async_evaluation_timestamp > m2->async_evaluation_timestamp) - + (m1->async_evaluation_timestamp < m2->async_evaluation_timestamp); +} + +static int js_execute_async_module(JSContext *ctx, JSModuleDef *m); +static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m, + JSValue *pvalue); + +static JSValue js_async_module_execution_rejected(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic, JSValue *func_data) +{ + JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]); + JSValueConst error = argv[0]; + int i; + + if (js_check_stack_overflow(ctx->rt, 0)) + return JS_ThrowStackOverflow(ctx); + + if (module->status == JS_MODULE_STATUS_EVALUATED) { + assert(module->eval_has_exception); + return JS_UNDEFINED; + } + + assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC); + assert(!module->eval_has_exception); + assert(module->async_evaluation); + + module->eval_has_exception = TRUE; + module->eval_exception = JS_DupValue(ctx, error); + module->status = JS_MODULE_STATUS_EVALUATED; + + for(i = 0; i < module->async_parent_modules_count; i++) { + JSModuleDef *m = module->async_parent_modules[i]; + JSValue m_obj = JS_NewModuleValue(ctx, m); + js_async_module_execution_rejected(ctx, JS_UNDEFINED, 1, &error, 0, + &m_obj); + JS_FreeValue(ctx, m_obj); + } + + if (!JS_IsUndefined(module->promise)) { + JSValue ret_val; + assert(module->cycle_root == module); + ret_val = JS_Call(ctx, module->resolving_funcs[1], JS_UNDEFINED, + 1, &error); + JS_FreeValue(ctx, ret_val); + } + return JS_UNDEFINED; +} + +static JSValue js_async_module_execution_fulfilled(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int magic, JSValue *func_data) +{ + JSModuleDef *module = JS_VALUE_GET_PTR(func_data[0]); + ExecModuleList exec_list_s, *exec_list = &exec_list_s; + int i; + + if (module->status == JS_MODULE_STATUS_EVALUATED) { + assert(module->eval_has_exception); + return JS_UNDEFINED; + } + assert(module->status == JS_MODULE_STATUS_EVALUATING_ASYNC); + assert(!module->eval_has_exception); + assert(module->async_evaluation); + module->async_evaluation = FALSE; + js_set_module_evaluated(ctx, module); + + exec_list->tab = NULL; + exec_list->count = 0; + exec_list->size = 0; + + if (gather_available_ancestors(ctx, module, exec_list) < 0) { + js_free(ctx, exec_list->tab); + return JS_EXCEPTION; + } + + /* sort by increasing async_evaluation timestamp */ + rqsort(exec_list->tab, exec_list->count, sizeof(exec_list->tab[0]), + exec_module_list_cmp, NULL); + + for(i = 0; i < exec_list->count; i++) { + JSModuleDef *m = exec_list->tab[i]; + if (m->status == JS_MODULE_STATUS_EVALUATED) { + assert(m->eval_has_exception); + } else if (m->has_tla) { + js_execute_async_module(ctx, m); + } else { + JSValue error; + if (js_execute_sync_module(ctx, m, &error) < 0) { + JSValue m_obj = JS_NewModuleValue(ctx, m); + js_async_module_execution_rejected(ctx, JS_UNDEFINED, + 1, (JSValueConst *)&error, 0, + &m_obj); + JS_FreeValue(ctx, m_obj); + JS_FreeValue(ctx, error); + } else { + js_set_module_evaluated(ctx, m); + } + } + } + js_free(ctx, exec_list->tab); + return JS_UNDEFINED; +} + +static int js_execute_async_module(JSContext *ctx, JSModuleDef *m) +{ + JSValue promise, m_obj; + JSValue resolve_funcs[2], ret_val; + promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0); + if (JS_IsException(promise)) + return -1; + m_obj = JS_NewModuleValue(ctx, m); + resolve_funcs[0] = JS_NewCFunctionData(ctx, js_async_module_execution_fulfilled, 0, 0, 1, (JSValueConst *)&m_obj); + resolve_funcs[1] = JS_NewCFunctionData(ctx, js_async_module_execution_rejected, 0, 0, 1, (JSValueConst *)&m_obj); + ret_val = js_promise_then(ctx, promise, 2, (JSValueConst *)resolve_funcs); + JS_FreeValue(ctx, ret_val); + JS_FreeValue(ctx, m_obj); + JS_FreeValue(ctx, resolve_funcs[0]); + JS_FreeValue(ctx, resolve_funcs[1]); + JS_FreeValue(ctx, promise); + return 0; +} + +/* return < 0 in case of exception. *pvalue contains the exception. */ +static int js_execute_sync_module(JSContext *ctx, JSModuleDef *m, + JSValue *pvalue) +{ + if (m->init_func) { + /* C module init : no asynchronous execution */ + if (m->init_func(ctx, m) < 0) + goto fail; + } else { + JSValue promise; + JSPromiseStateEnum state; + + promise = js_async_function_call(ctx, m->func_obj, JS_UNDEFINED, 0, NULL, 0); + if (JS_IsException(promise)) + goto fail; + state = JS_PromiseState(ctx, promise); + if (state == JS_PROMISE_FULFILLED) { + JS_FreeValue(ctx, promise); + } else if (state == JS_PROMISE_REJECTED) { + *pvalue = JS_PromiseResult(ctx, promise); + JS_FreeValue(ctx, promise); + return -1; + } else { + JS_FreeValue(ctx, promise); + JS_ThrowTypeError(ctx, "promise is pending"); + fail: + *pvalue = JS_GetException(ctx); + return -1; + } + } + *pvalue = JS_UNDEFINED; + return 0; +} + +/* spec: InnerModuleEvaluation. Return (index, JS_UNDEFINED) or (-1, + exception) */ +static int js_inner_module_evaluation(JSContext *ctx, JSModuleDef *m, + int index, JSModuleDef **pstack_top, + JSValue *pvalue) { JSModuleDef *m1; int i; - JSValue ret_val; - if (m->eval_mark) - return JS_UNDEFINED; /* avoid cycles */ + if (js_check_stack_overflow(ctx->rt, 0)) { + JS_ThrowStackOverflow(ctx); + *pvalue = JS_GetException(ctx); + return -1; + } + +#ifdef DUMP_MODULE_RESOLVE + { + char buf1[ATOM_GET_STR_BUF_SIZE]; + printf("js_inner_module_evaluation '%s':\n", JS_AtomGetStr(ctx, buf1, sizeof(buf1), m->module_name)); + } +#endif - if (m->evaluated) { - /* if the module was already evaluated, rethrow the exception - it raised */ + if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED) { if (m->eval_has_exception) { - return JS_Throw(ctx, JS_DupValue(ctx, m->eval_exception)); + *pvalue = JS_DupValue(ctx, m->eval_exception); + return -1; } else { - return JS_UNDEFINED; + *pvalue = JS_UNDEFINED; + return index; } } + if (m->status == JS_MODULE_STATUS_EVALUATING) { + *pvalue = JS_UNDEFINED; + return index; + } + assert(m->status == JS_MODULE_STATUS_LINKED); - m->eval_mark = TRUE; + m->status = JS_MODULE_STATUS_EVALUATING; + m->dfs_index = index; + m->dfs_ancestor_index = index; + m->pending_async_dependencies = 0; + index++; + /* push 'm' on stack */ + m->stack_prev = *pstack_top; + *pstack_top = m; for(i = 0; i < m->req_module_entries_count; i++) { JSReqModuleEntry *rme = &m->req_module_entries[i]; m1 = rme->module; - if (!m1->eval_mark) { - ret_val = js_evaluate_module(ctx, m1); - if (JS_IsException(ret_val)) { - m->eval_mark = FALSE; - return ret_val; + index = js_inner_module_evaluation(ctx, m1, index, pstack_top, pvalue); + if (index < 0) + return -1; + assert(m1->status == JS_MODULE_STATUS_EVALUATING || + m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m1->status == JS_MODULE_STATUS_EVALUATED); + if (m1->status == JS_MODULE_STATUS_EVALUATING) { + m->dfs_ancestor_index = min_int(m->dfs_ancestor_index, + m1->dfs_ancestor_index); + } else { + m1 = m1->cycle_root; + assert(m1->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m1->status == JS_MODULE_STATUS_EVALUATED); + if (m1->eval_has_exception) { + *pvalue = JS_DupValue(ctx, m1->eval_exception); + return -1; } - JS_FreeValue(ctx, ret_val); + } + if (m1->async_evaluation) { + m->pending_async_dependencies++; + if (js_resize_array(ctx, (void **)&m1->async_parent_modules, sizeof(m1->async_parent_modules[0]), &m1->async_parent_modules_size, m1->async_parent_modules_count + 1)) { + *pvalue = JS_GetException(ctx); + return -1; + } + m1->async_parent_modules[m1->async_parent_modules_count++] = m; } } - if (m->init_func) { - /* C module init */ - if (m->init_func(ctx, m) < 0) - ret_val = JS_EXCEPTION; - else - ret_val = JS_UNDEFINED; + if (m->pending_async_dependencies > 0) { + assert(!m->async_evaluation); + m->async_evaluation = TRUE; + m->async_evaluation_timestamp = + ctx->rt->module_async_evaluation_next_timestamp++; + } else if (m->has_tla) { + assert(!m->async_evaluation); + m->async_evaluation = TRUE; + m->async_evaluation_timestamp = + ctx->rt->module_async_evaluation_next_timestamp++; + js_execute_async_module(ctx, m); } else { - ret_val = JS_CallFree(ctx, m->func_obj, JS_UNDEFINED, 0, NULL); - m->func_obj = JS_UNDEFINED; + if (js_execute_sync_module(ctx, m, pvalue) < 0) + return -1; } - if (JS_IsException(ret_val)) { - /* save the thrown exception value */ - m->eval_has_exception = TRUE; - m->eval_exception = JS_DupValue(ctx, ctx->rt->current_exception); + + assert(m->dfs_ancestor_index <= m->dfs_index); + if (m->dfs_index == m->dfs_ancestor_index) { + for(;;) { + /* pop m1 from stack */ + m1 = *pstack_top; + *pstack_top = m1->stack_prev; + if (!m1->async_evaluation) { + m1->status = JS_MODULE_STATUS_EVALUATED; + } else { + m1->status = JS_MODULE_STATUS_EVALUATING_ASYNC; + } + /* spec bug: cycle_root must be assigned before the test */ + m1->cycle_root = m; + if (m1 == m) + break; + } } - m->eval_mark = FALSE; - m->evaluated = TRUE; - return ret_val; + *pvalue = JS_UNDEFINED; + return index; } -static warn_unused JSAtom js_parse_from_clause(JSParseState *s) +/* Run the <eval> function of the module and of all its requested + modules. Return a promise or an exception. */ +static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m) +{ + JSModuleDef *m1, *stack_top; + JSValue ret_val, result; + + assert(m->status == JS_MODULE_STATUS_LINKED || + m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED); + if (m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED) { + m = m->cycle_root; + } + /* a promise may be created only on the cycle_root of a cycle */ + if (!JS_IsUndefined(m->promise)) + return JS_DupValue(ctx, m->promise); + m->promise = JS_NewPromiseCapability(ctx, m->resolving_funcs); + if (JS_IsException(m->promise)) + return JS_EXCEPTION; + + stack_top = NULL; + if (js_inner_module_evaluation(ctx, m, 0, &stack_top, &result) < 0) { + while (stack_top != NULL) { + m1 = stack_top; + assert(m1->status == JS_MODULE_STATUS_EVALUATING); + m1->status = JS_MODULE_STATUS_EVALUATED; + m1->eval_has_exception = TRUE; + m1->eval_exception = JS_DupValue(ctx, result); + m1->cycle_root = m; /* spec bug: should be present */ + stack_top = m1->stack_prev; + } + JS_FreeValue(ctx, result); + assert(m->status == JS_MODULE_STATUS_EVALUATED); + assert(m->eval_has_exception); + ret_val = JS_Call(ctx, m->resolving_funcs[1], JS_UNDEFINED, + 1, (JSValueConst *)&m->eval_exception); + JS_FreeValue(ctx, ret_val); + } else { + assert(m->status == JS_MODULE_STATUS_EVALUATING_ASYNC || + m->status == JS_MODULE_STATUS_EVALUATED); + assert(!m->eval_has_exception); + if (!m->async_evaluation) { + JSValue value; + assert(m->status == JS_MODULE_STATUS_EVALUATED); + value = JS_UNDEFINED; + ret_val = JS_Call(ctx, m->resolving_funcs[0], JS_UNDEFINED, + 1, (JSValueConst *)&value); + JS_FreeValue(ctx, ret_val); + } + assert(stack_top == NULL); + } + return JS_DupValue(ctx, m->promise); +} + +static __exception JSAtom js_parse_from_clause(JSParseState *s) { JSAtom module_name; if (!token_is_pseudo_keyword(s, JS_ATOM_from)) { @@ -28454,7 +29217,7 @@ static warn_unused JSAtom js_parse_from_clause(JSParseState *s) return module_name; } -static warn_unused int js_parse_export(JSParseState *s) +static __exception int js_parse_export(JSParseState *s) { JSContext *ctx = s->ctx; JSModuleDef *m = s->cur_func->module; @@ -28657,7 +29420,7 @@ static int add_import(JSParseState *s, JSModuleDef *m, return 0; } -static warn_unused int js_parse_import(JSParseState *s) +static __exception int js_parse_import(JSParseState *s) { JSContext *ctx = s->ctx; JSModuleDef *m = s->cur_func->module; @@ -28772,7 +29535,7 @@ static warn_unused int js_parse_import(JSParseState *s) return js_parse_expect_semi(s); } -static warn_unused int js_parse_source_element(JSParseState *s) +static __exception int js_parse_source_element(JSParseState *s) { JSFunctionDef *fd = s->cur_func; int tok; @@ -29696,7 +30459,8 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, } var_idx = idx; break; - } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) { + } else + if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) { dbuf_putc(bc, OP_get_loc); dbuf_put_u16(bc, idx); var_object_test(ctx, s, var_name, op, bc, &label_done, 1); @@ -29781,6 +30545,7 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, case OP_scope_get_ref: dbuf_putc(bc, OP_undefined); /* fall thru */ + case OP_scope_get_var_checkthis: case OP_scope_get_var_undef: case OP_scope_get_var: case OP_scope_put_var: @@ -29806,7 +30571,12 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, } } else { if (s->vars[var_idx].is_lexical) { - dbuf_putc(bc, OP_get_loc_check); + if (op == OP_scope_get_var_checkthis) { + /* only used for 'this' return in derived class constructors */ + dbuf_putc(bc, OP_get_loc_checkthis); + } else { + dbuf_putc(bc, OP_get_loc_check); + } } else { dbuf_putc(bc, OP_get_loc); } @@ -30263,12 +31033,17 @@ static int resolve_scope_private_field(JSContext *ctx, JSFunctionDef *s, /* obj func value */ dbuf_putc(bc, OP_call_method); dbuf_put_u16(bc, 1); + dbuf_putc(bc, OP_drop); } break; default: abort(); } break; + case OP_scope_in_private_field: + get_loc_or_ref(bc, is_ref, idx); + dbuf_putc(bc, OP_private_in); + break; default: abort(); } @@ -30387,12 +31162,13 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) is_arg_scope = (scope_idx == ARG_SCOPE_END); if (!is_arg_scope) { /* add unscoped variables */ + /* XXX: propagate is_const and var_kind too ? */ for(i = 0; i < fd->arg_count; i++) { vd = &fd->args[i]; if (vd->var_name != JS_ATOM_NULL) { get_closure_var(ctx, s, fd, - TRUE, i, vd->var_name, FALSE, FALSE, - JS_VAR_NORMAL); + TRUE, i, vd->var_name, FALSE, + vd->is_lexical, JS_VAR_NORMAL); } } for(i = 0; i < fd->var_count; i++) { @@ -30402,8 +31178,8 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) vd->var_name != JS_ATOM__ret_ && vd->var_name != JS_ATOM_NULL) { get_closure_var(ctx, s, fd, - FALSE, i, vd->var_name, FALSE, FALSE, - JS_VAR_NORMAL); + FALSE, i, vd->var_name, FALSE, + vd->is_lexical, JS_VAR_NORMAL); } } } else { @@ -30412,8 +31188,8 @@ static void add_eval_variables(JSContext *ctx, JSFunctionDef *s) /* do not close top level last result */ if (vd->scope_level == 0 && is_var_in_arg_scope(vd)) { get_closure_var(ctx, s, fd, - FALSE, i, vd->var_name, FALSE, FALSE, - JS_VAR_NORMAL); + FALSE, i, vd->var_name, FALSE, + vd->is_lexical, JS_VAR_NORMAL); } } } @@ -30446,7 +31222,7 @@ static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv, /* for direct eval compilation: add references to the variables of the calling function */ -static warn_unused int add_closure_variables(JSContext *ctx, JSFunctionDef *s, +static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s, JSFunctionBytecode *b, int scope_idx) { int i, count; @@ -30866,7 +31642,7 @@ static int get_label_pos(JSFunctionDef *s, int label) /* convert global variable accesses to local variables or closure variables when necessary */ -static warn_unused int resolve_variables(JSContext *ctx, JSFunctionDef *s) +static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) { int pos, pos_next, bc_len, op, len, i, idx, line_num; uint8_t *bc_buf; @@ -30945,6 +31721,7 @@ static warn_unused int resolve_variables(JSContext *ctx, JSFunctionDef *s) dbuf_putc(&bc_out, op); dbuf_put_u16(&bc_out, s->scopes[scope].first + 1); break; + case OP_scope_get_var_checkthis: case OP_scope_get_var_undef: case OP_scope_get_var: case OP_scope_put_var: @@ -30974,6 +31751,7 @@ static warn_unused int resolve_variables(JSContext *ctx, JSFunctionDef *s) case OP_scope_get_private_field: case OP_scope_get_private_field2: case OP_scope_put_private_field: + case OP_scope_in_private_field: { int ret; var_name = get_u32(bc_buf + pos + 1); @@ -31186,6 +31964,17 @@ static warn_unused int resolve_variables(JSContext *ctx, JSFunctionDef *s) /* only used during parsing */ break; + case OP_get_field_opt_chain: /* equivalent to OP_get_field */ + { + JSAtom name = get_u32(bc_buf + pos + 1); + dbuf_putc(&bc_out, OP_get_field); + dbuf_put_u32(&bc_out, name); + } + break; + case OP_get_array_el_opt_chain: /* equivalent to OP_get_array_el */ + dbuf_putc(&bc_out, OP_get_array_el); + break; + default: no_change: dbuf_put(&bc_out, bc_buf + pos, len); @@ -31430,7 +32219,7 @@ static void put_short_code(DynBuf *bc_out, int op, int idx) } /* peephole optimizations and resolve goto/labels */ -static warn_unused int resolve_labels(JSContext *ctx, JSFunctionDef *s) +static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) { int pos, pos_next, bc_len, op, op1, len, i, line_num; const uint8_t *bc_buf; @@ -32239,7 +33028,8 @@ static warn_unused int resolve_labels(JSContext *ctx, JSFunctionDef *s) bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false); } goto shrink; - } else if (diff == (int16_t)diff && op == OP_goto) { + } else + if (diff == (int16_t)diff && op == OP_goto) { //put_u16(bc_out.buf + pos, diff); jp->size = 2; delta = 2; @@ -32316,14 +33106,15 @@ typedef struct StackSizeState { int bc_len; int stack_len_max; uint16_t *stack_level_tab; + int32_t *catch_pos_tab; int *pc_stack; int pc_stack_len; int pc_stack_size; } StackSizeState; /* 'op' is only used for error indication */ -static warn_unused int ss_check(JSContext *ctx, StackSizeState *s, - int pos, int op, int stack_len) +static __exception int ss_check(JSContext *ctx, StackSizeState *s, + int pos, int op, int stack_len, int catch_pos) { if ((unsigned)pos >= s->bc_len) { JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos); @@ -32339,9 +33130,13 @@ static warn_unused int ss_check(JSContext *ctx, StackSizeState *s, if (s->stack_level_tab[pos] != 0xffff) { /* already explored: check that the stack size is consistent */ if (s->stack_level_tab[pos] != stack_len) { - JS_ThrowInternalError(ctx, "unconsistent stack size: %d %d (pc=%d)", + JS_ThrowInternalError(ctx, "inconsistent stack size: %d %d (pc=%d)", s->stack_level_tab[pos], stack_len, pos); return -1; + } else if (s->catch_pos_tab[pos] != catch_pos) { + JS_ThrowInternalError(ctx, "inconsistent catch position: %d %d (pc=%d)", + s->catch_pos_tab[pos], catch_pos, pos); + return -1; } else { return 0; } @@ -32349,6 +33144,7 @@ static warn_unused int ss_check(JSContext *ctx, StackSizeState *s, /* mark as explored and store the stack size */ s->stack_level_tab[pos] = stack_len; + s->catch_pos_tab[pos] = catch_pos; /* queue the new PC to explore */ if (js_resize_array(ctx, (void **)&s->pc_stack, sizeof(s->pc_stack[0]), @@ -32358,12 +33154,12 @@ static warn_unused int ss_check(JSContext *ctx, StackSizeState *s, return 0; } -static warn_unused int compute_stack_size(JSContext *ctx, +static __exception int compute_stack_size(JSContext *ctx, JSFunctionDef *fd, int *pstack_size) { StackSizeState s_s, *s = &s_s; - int i, diff, n_pop, pos_next, stack_len, pos, op; + int i, diff, n_pop, pos_next, stack_len, pos, op, catch_pos, catch_level; const JSOpCode *oi; const uint8_t *bc_buf; @@ -32376,24 +33172,33 @@ static warn_unused int compute_stack_size(JSContext *ctx, return -1; for(i = 0; i < s->bc_len; i++) s->stack_level_tab[i] = 0xffff; - s->stack_len_max = 0; s->pc_stack = NULL; + s->catch_pos_tab = js_malloc(ctx, sizeof(s->catch_pos_tab[0]) * + s->bc_len); + if (!s->catch_pos_tab) + goto fail; + + s->stack_len_max = 0; s->pc_stack_len = 0; s->pc_stack_size = 0; /* breadth-first graph exploration */ - if (ss_check(ctx, s, 0, OP_invalid, 0)) + if (ss_check(ctx, s, 0, OP_invalid, 0, -1)) goto fail; while (s->pc_stack_len > 0) { pos = s->pc_stack[--s->pc_stack_len]; stack_len = s->stack_level_tab[pos]; + catch_pos = s->catch_pos_tab[pos]; op = bc_buf[pos]; if (op == 0 || op >= OP_COUNT) { JS_ThrowInternalError(ctx, "invalid opcode (op=%d, pc=%d)", op, pos); goto fail; } oi = &short_opcode_info(op); +#if defined(DUMP_BYTECODE) && (DUMP_BYTECODE & 64) + printf("%5d: %10s %5d %5d\n", pos, oi->name, stack_len, catch_pos); +#endif pos_next = pos + oi->size; if (pos_next > s->bc_len) { JS_ThrowInternalError(ctx, "bytecode buffer overflow (op=%d, pc=%d)", op, pos); @@ -32449,55 +33254,104 @@ static warn_unused int compute_stack_size(JSContext *ctx, case OP_if_true8: case OP_if_false8: diff = (int8_t)bc_buf[pos + 1]; - if (ss_check(ctx, s, pos + 1 + diff, op, stack_len)) + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos)) goto fail; break; #endif case OP_if_true: case OP_if_false: - case OP_catch: diff = get_u32(bc_buf + pos + 1); - if (ss_check(ctx, s, pos + 1 + diff, op, stack_len)) + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos)) goto fail; break; case OP_gosub: diff = get_u32(bc_buf + pos + 1); - if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1)) + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len + 1, catch_pos)) goto fail; break; case OP_with_get_var: case OP_with_delete_var: diff = get_u32(bc_buf + pos + 5); - if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1)) + if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 1, catch_pos)) goto fail; break; case OP_with_make_ref: case OP_with_get_ref: case OP_with_get_ref_undef: diff = get_u32(bc_buf + pos + 5); - if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2)) + if (ss_check(ctx, s, pos + 5 + diff, op, stack_len + 2, catch_pos)) goto fail; break; case OP_with_put_var: diff = get_u32(bc_buf + pos + 5); - if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1)) + if (ss_check(ctx, s, pos + 5 + diff, op, stack_len - 1, catch_pos)) goto fail; break; - + case OP_catch: + diff = get_u32(bc_buf + pos + 1); + if (ss_check(ctx, s, pos + 1 + diff, op, stack_len, catch_pos)) + goto fail; + catch_pos = pos; + break; + case OP_for_of_start: + case OP_for_await_of_start: + catch_pos = pos; + break; + /* we assume the catch offset entry is only removed with + some op codes */ + case OP_drop: + catch_level = stack_len; + goto check_catch; + case OP_nip: + catch_level = stack_len - 1; + goto check_catch; + case OP_nip1: + catch_level = stack_len - 1; + goto check_catch; + case OP_iterator_close: + catch_level = stack_len + 2; + check_catch: + /* Note: for for_of_start/for_await_of_start we consider + the catch offset is on the first stack entry instead of + the thirst */ + if (catch_pos >= 0) { + int level; + level = s->stack_level_tab[catch_pos]; + if (bc_buf[catch_pos] != OP_catch) + level++; /* for_of_start, for_wait_of_start */ + /* catch_level = stack_level before op_catch is executed ? */ + if (catch_level == level) { + catch_pos = s->catch_pos_tab[catch_pos]; + } + } + break; + case OP_nip_catch: + if (catch_pos < 0) { + JS_ThrowInternalError(ctx, "nip_catch: no catch op (pc=%d)", pos); + goto fail; + } + stack_len = s->stack_level_tab[catch_pos]; + if (bc_buf[catch_pos] != OP_catch) + stack_len++; /* for_of_start, for_wait_of_start */ + stack_len++; /* no stack overflow is possible by construction */ + catch_pos = s->catch_pos_tab[catch_pos]; + break; default: break; } - if (ss_check(ctx, s, pos_next, op, stack_len)) + if (ss_check(ctx, s, pos_next, op, stack_len, catch_pos)) goto fail; done_insn: ; } - js_free(ctx, s->stack_level_tab); js_free(ctx, s->pc_stack); + js_free(ctx, s->catch_pos_tab); + js_free(ctx, s->stack_level_tab); *pstack_size = s->stack_len_max; return 0; fail: - js_free(ctx, s->stack_level_tab); js_free(ctx, s->pc_stack); + js_free(ctx, s->catch_pos_tab); + js_free(ctx, s->stack_level_tab); *pstack_size = 0; return -1; } @@ -32679,10 +33533,8 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) } } else { b->vardefs = (void *)((uint8_t*)b + vardefs_offset); - if (fd->arg_count) - memcpy(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0])); - if (fd->var_count) - memcpy(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0])); + memcpy_no_ub(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0])); + memcpy_no_ub(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0])); } b->var_count = fd->var_count; b->arg_count = fd->arg_count; @@ -32744,6 +33596,8 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) b->super_allowed = fd->super_allowed; b->arguments_allowed = fd->arguments_allowed; b->backtrace_barrier = fd->backtrace_barrier; + b->is_direct_or_indirect_eval = (fd->eval_type == JS_EVAL_TYPE_DIRECT || + fd->eval_type == JS_EVAL_TYPE_INDIRECT); b->realm = JS_DupContext(ctx); add_gc_object(ctx->rt, &b->header, JS_GC_OBJ_TYPE_FUNCTION_BYTECODE); @@ -32809,7 +33663,7 @@ static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b) } } -static warn_unused int js_parse_directives(JSParseState *s) +static __exception int js_parse_directives(JSParseState *s) { char str[20]; JSParsePos pos; @@ -32983,7 +33837,7 @@ static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s) /* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */ -static warn_unused int js_parse_function_decl2(JSParseState *s, +static __exception int js_parse_function_decl2(JSParseState *s, JSParseFunctionEnum func_type, JSFunctionKindEnum func_kind, JSAtom func_name, @@ -33026,8 +33880,9 @@ static warn_unused int js_parse_function_decl2(JSParseState *s, func_type == JS_PARSE_FUNC_EXPR && (func_kind & JS_FUNC_GENERATOR)) || (s->token.u.ident.atom == JS_ATOM_await && - func_type == JS_PARSE_FUNC_EXPR && - (func_kind & JS_FUNC_ASYNC))) { + ((func_type == JS_PARSE_FUNC_EXPR && + (func_kind & JS_FUNC_ASYNC)) || + func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT))) { return js_parse_error_reserved_identifier(s); } } @@ -33121,7 +33976,8 @@ static warn_unused int js_parse_function_decl2(JSParseState *s, func_type == JS_PARSE_FUNC_SETTER || func_type == JS_PARSE_FUNC_CLASS_CONSTRUCTOR || func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR); - fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW); + fd->has_arguments_binding = (func_type != JS_PARSE_FUNC_ARROW && + func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT); fd->has_this_binding = fd->has_arguments_binding; fd->is_derived_class_constructor = (func_type == JS_PARSE_FUNC_DERIVED_CLASS_CONSTRUCTOR); if (func_type == JS_PARSE_FUNC_ARROW) { @@ -33129,6 +33985,11 @@ static warn_unused int js_parse_function_decl2(JSParseState *s, fd->super_call_allowed = fd->parent->super_call_allowed; fd->super_allowed = fd->parent->super_allowed; fd->arguments_allowed = fd->parent->arguments_allowed; + } else if (func_type == JS_PARSE_FUNC_CLASS_STATIC_INIT) { + fd->new_target_allowed = TRUE; // although new.target === undefined + fd->super_call_allowed = FALSE; + fd->super_allowed = TRUE; + fd->arguments_allowed = FALSE; } else { fd->new_target_allowed = TRUE; fd->super_call_allowed = fd->is_derived_class_constructor; @@ -33166,7 +34027,7 @@ static warn_unused int js_parse_function_decl2(JSParseState *s, if (add_arg(ctx, fd, name) < 0) goto fail; fd->defined_arg_count = 1; - } else { + } else if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) { if (s->token.val == '(') { int skip_bits; /* if there is an '=' inside the parameter list, we @@ -33227,6 +34088,8 @@ static warn_unused int js_parse_function_decl2(JSParseState *s, goto fail; } if (fd->has_parameter_expressions) { + if (js_parse_check_duplicate_parameter(s, name)) + goto fail; if (define_var(s, fd, name, JS_VAR_DEF_LET) < 0) goto fail; } @@ -33387,8 +34250,10 @@ static warn_unused int js_parse_function_decl2(JSParseState *s, } } - if (js_parse_expect(s, '{')) - goto fail; + if (func_type != JS_PARSE_FUNC_CLASS_STATIC_INIT) { + if (js_parse_expect(s, '{')) + goto fail; + } if (js_parse_directives(s)) goto fail; @@ -33418,9 +34283,15 @@ static warn_unused int js_parse_function_decl2(JSParseState *s, if (js_is_live_code(s)) { emit_return(s, FALSE); } -done: + done: s->cur_func = fd->parent; + /* Reparse identifiers after the function is terminated so that + the token is parsed in the englobing function. It could be done + by just using next_token() here for normal functions, but it is + necessary for arrow functions with an expression body. */ + reparse_ident_token(s); + /* create the function object */ { int idx; @@ -33531,7 +34402,7 @@ done: return -1; } -static warn_unused int js_parse_function_decl(JSParseState *s, +static __exception int js_parse_function_decl(JSParseState *s, JSParseFunctionEnum func_type, JSFunctionKindEnum func_kind, JSAtom func_name, @@ -33543,7 +34414,7 @@ static warn_unused int js_parse_function_decl(JSParseState *s, NULL); } -static warn_unused int js_parse_program(JSParseState *s) +static __exception int js_parse_program(JSParseState *s) { JSFunctionDef *fd = s->cur_func; int idx; @@ -33572,12 +34443,24 @@ static warn_unused int js_parse_program(JSParseState *s) if (!s->is_module) { /* return the value of the hidden variable eval_ret_idx */ - emit_op(s, OP_get_loc); - emit_u16(s, fd->eval_ret_idx); + if (fd->func_kind == JS_FUNC_ASYNC) { + /* wrap the return value in an object so that promises can + be safely returned */ + emit_op(s, OP_object); + emit_op(s, OP_dup); - emit_op(s, OP_return); + emit_op(s, OP_get_loc); + emit_u16(s, fd->eval_ret_idx); + + emit_op(s, OP_put_field); + emit_atom(s, JS_ATOM_value); + } else { + emit_op(s, OP_get_loc); + emit_u16(s, fd->eval_ret_idx); + } + emit_return(s, TRUE); } else { - emit_op(s, OP_return_undef); + emit_return(s, FALSE); } return 0; @@ -33620,7 +34503,6 @@ static JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj, ret_val = js_evaluate_module(ctx, m); if (JS_IsException(ret_val)) { fail: - js_free_modules(ctx, JS_FREE_MODULE_NOT_EVALUATED); return JS_EXCEPTION; } } else { @@ -33635,33 +34517,8 @@ JSValue JS_EvalFunction(JSContext *ctx, JSValue fun_obj) return JS_EvalFunctionInternal(ctx, fun_obj, ctx->global_obj, NULL, NULL); } -static void skip_shebang(JSParseState *s) -{ - const uint8_t *p = s->buf_ptr; - int c; - - if (p[0] == '#' && p[1] == '!') { - p += 2; - while (p < s->buf_end) { - if (*p == '\n' || *p == '\r') { - break; - } else if (*p >= 0x80) { - c = unicode_from_utf8(p, UTF8_CHAR_LEN_MAX, &p); - if (c == CP_LS || c == CP_PS) { - break; - } else if (c == -1) { - p++; /* skip invalid UTF-8 */ - } - } else { - p++; - } - } - s->buf_ptr = p; - } -} - /* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ -static JSValue JS_EvalInternalImpl(JSContext *ctx, JSValueConst this_obj, +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) { @@ -33675,7 +34532,7 @@ static JSValue JS_EvalInternalImpl(JSContext *ctx, JSValueConst this_obj, JSModuleDef *m; js_parse_init(ctx, s, input, input_len, filename, line); - skip_shebang(s); + skip_shebang(&s->buf_ptr, s->buf_end); eval_type = flags & JS_EVAL_TYPE_MASK; m = NULL; @@ -33733,6 +34590,10 @@ static JSValue JS_EvalInternalImpl(JSContext *ctx, JSValueConst this_obj, goto fail; } fd->module = m; + if (m != NULL || (flags & JS_EVAL_FLAG_ASYNC)) { + fd->in_function_body = TRUE; + fd->func_kind = JS_FUNC_ASYNC; + } s->is_module = (m != NULL); s->allow_html_comments = !s->is_module; @@ -33747,6 +34608,9 @@ static JSValue JS_EvalInternalImpl(JSContext *ctx, JSValueConst this_obj, goto fail1; } + if (m != NULL) + m->has_tla = fd->has_await; + /* create the function object and all the enclosed functions */ fun_obj = js_create_function(ctx, fd); if (JS_IsException(fun_obj)) @@ -33756,7 +34620,7 @@ static JSValue JS_EvalInternalImpl(JSContext *ctx, JSValueConst this_obj, m->func_obj = fun_obj; if (js_resolve_module(ctx, m) < 0) goto fail1; - fun_obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); + fun_obj = JS_NewModuleValue(ctx, m); } if (flags & JS_EVAL_FLAG_COMPILE_ONLY) { ret_val = fun_obj; @@ -33952,8 +34816,6 @@ typedef enum BCTagEnum { BC_TAG_OBJECT, BC_TAG_ARRAY, BC_TAG_BIG_INT, - BC_TAG_BIG_FLOAT, - BC_TAG_BIG_DECIMAL, BC_TAG_TEMPLATE_OBJECT, BC_TAG_FUNCTION_BYTECODE, BC_TAG_MODULE, @@ -33963,24 +34825,21 @@ typedef enum BCTagEnum { BC_TAG_DATE, BC_TAG_OBJECT_VALUE, BC_TAG_OBJECT_REFERENCE, +#ifdef CONFIG_BIGNUM + BC_TAG_BIG_FLOAT, + BC_TAG_BIG_DECIMAL, +#endif } BCTagEnum; #ifdef CONFIG_BIGNUM -#define BC_BASE_VERSION 2 +#define BC_VERSION 0x43 #else -#define BC_BASE_VERSION 1 -#endif -#define BC_BE_VERSION 0x40 -#ifdef WORDS_BIGENDIAN -#define BC_VERSION (BC_BASE_VERSION | BC_BE_VERSION) -#else -#define BC_VERSION BC_BASE_VERSION +#define BC_VERSION 3 #endif typedef struct BCWriterState { JSContext *ctx; DynBuf dbuf; - BOOL byte_swap : 8; BOOL allow_bytecode : 8; BOOL allow_sab : 8; BOOL allow_reference : 8; @@ -34010,8 +34869,6 @@ static const char * const bc_tag_str[] = { "object", "array", "bigint", - "bigfloat", - "bigdecimal", "template", "function", "module", @@ -34021,9 +34878,22 @@ static const char * const bc_tag_str[] = { "Date", "ObjectValue", "ObjectReference", +#ifdef CONFIG_BIGNUM + "bigfloat", + "bigdecimal", +#endif }; #endif +static inline BOOL is_be(void) +{ + union { + uint16_t a; + uint8_t b; + } u = {0x100}; + return u.b; +} + static void bc_put_u8(BCWriterState *s, uint8_t v) { dbuf_putc(&s->dbuf, v); @@ -34031,21 +34901,21 @@ static void bc_put_u8(BCWriterState *s, uint8_t v) static void bc_put_u16(BCWriterState *s, uint16_t v) { - if (s->byte_swap) + if (is_be()) v = bswap16(v); dbuf_put_u16(&s->dbuf, v); } -static maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v) +static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v) { - if (s->byte_swap) + if (is_be()) v = bswap32(v); dbuf_put_u32(&s->dbuf, v); } static void bc_put_u64(BCWriterState *s, uint64_t v) { - if (s->byte_swap) + if (is_be()) v = bswap64(v); dbuf_put(&s->dbuf, (uint8_t *)&v, sizeof(v)); } @@ -34070,7 +34940,7 @@ static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom) { uint32_t v; - if (atom < s->first_atom || JS_AtomIsTaggedInt(atom)) { + if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) { *pres = atom; return 0; } @@ -34110,8 +34980,8 @@ static int bc_put_atom(BCWriterState *s, JSAtom atom) { uint32_t v; - if (JS_AtomIsTaggedInt(atom)) { - v = (JS_AtomToUInt32(atom) << 1) | 1; + if (__JS_AtomIsTaggedInt(atom)) { + v = (__JS_AtomToUInt32(atom) << 1) | 1; } else { if (bc_atom_to_idx(s, &v, atom)) return -1; @@ -34215,7 +35085,7 @@ static int JS_WriteFunctionBytecode(BCWriterState *s, pos += len; } - if (s->byte_swap) + if (is_be()) bc_byte_swap(bc_buf, bc_len); dbuf_put(&s->dbuf, bc_buf, bc_len); @@ -34230,7 +35100,7 @@ static int JS_WriteFunctionBytecode(BCWriterState *s, static void JS_WriteString(BCWriterState *s, JSString *p) { int i; - bc_put_leb128(s, (p->len << 1) | p->is_wide_char); + bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char); if (p->is_wide_char) { for(i = 0; i < p->len; i++) bc_put_u16(s, p->u.str16[i]); @@ -34239,7 +35109,6 @@ static void JS_WriteString(BCWriterState *s, JSString *p) } } -#ifdef CONFIG_BIGNUM static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj) { uint32_t tag, tag1; @@ -34254,12 +35123,14 @@ static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj) case JS_TAG_BIG_INT: tag1 = BC_TAG_BIG_INT; break; +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: tag1 = BC_TAG_BIG_FLOAT; break; case JS_TAG_BIG_DECIMAL: tag1 = BC_TAG_BIG_DECIMAL; break; +#endif default: abort(); } @@ -34276,7 +35147,7 @@ static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj) e = a->expn + 3; else e = a->expn; - e = (e << 1) | a->sign; + e = (e * 2) | a->sign; if (e < INT32_MIN || e > INT32_MAX) { JS_ThrowInternalError(s->ctx, "bignum exponent is too large"); return -1; @@ -34305,20 +35176,14 @@ static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj) bc_put_leb128(s, len); /* always saved in byte based little endian representation */ for(j = 0; j < n1; j++) { - dbuf_putc(&s->dbuf, v >> (j * 8)); + bc_put_u8(s, v >> (j * 8)); } for(; i < a->len; i++) { limb_t v = a->tab[i]; #if LIMB_BITS == 32 -#ifdef WORDS_BIGENDIAN - v = bswap32(v); -#endif - dbuf_put_u32(&s->dbuf, v); + bc_put_u32(s, v); #else -#ifdef WORDS_BIGENDIAN - v = bswap64(v); -#endif - dbuf_put_u64(&s->dbuf, v); + bc_put_u64(s, v); #endif } } else { @@ -34361,20 +35226,19 @@ static int JS_WriteBigNum(BCWriterState *s, JSValueConst obj) v8 = d; bpos = 1; } else { - dbuf_putc(&s->dbuf, v8 | (d << 4)); + bc_put_u8(s, v8 | (d << 4)); bpos = 0; } } } /* flush the last digit */ if (bpos) { - dbuf_putc(&s->dbuf, v8); + bc_put_u8(s, v8); } } } return 0; } -#endif /* CONFIG_BIGNUM */ static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj); @@ -34397,6 +35261,7 @@ static int JS_WriteFunctionTag(BCWriterState *s, JSValueConst obj) bc_set_flags(&flags, &idx, b->arguments_allowed, 1); bc_set_flags(&flags, &idx, b->has_debug, 1); bc_set_flags(&flags, &idx, b->backtrace_barrier, 1); + bc_set_flags(&flags, &idx, b->is_direct_or_indirect_eval, 1); assert(idx <= 16); bc_put_u16(s, flags); bc_put_u8(s, b->js_mode); @@ -34503,6 +35368,8 @@ static int JS_WriteModule(BCWriterState *s, JSValueConst obj) bc_put_leb128(s, mi->req_module_idx); } + bc_put_u8(s, m->has_tla); + if (JS_WriteObjectRec(s, m->func_obj)) goto fail; return 0; @@ -34731,8 +35598,8 @@ static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) case JS_CLASS_NUMBER: case JS_CLASS_STRING: case JS_CLASS_BOOLEAN: -#ifdef CONFIG_BIGNUM case JS_CLASS_BIG_INT: +#ifdef CONFIG_BIGNUM case JS_CLASS_BIG_FLOAT: case JS_CLASS_BIG_DECIMAL: #endif @@ -34754,14 +35621,14 @@ static int JS_WriteObjectRec(BCWriterState *s, JSValueConst obj) goto fail; } break; -#ifdef CONFIG_BIGNUM case JS_TAG_BIG_INT: +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: case JS_TAG_BIG_DECIMAL: +#endif if (JS_WriteBigNum(s, obj)) goto fail; break; -#endif default: invalid_tag: JS_ThrowInternalError(s->ctx, "unsupported tag (%d)", tag); @@ -34779,15 +35646,10 @@ static int JS_WriteObjectAtoms(BCWriterState *s) JSRuntime *rt = s->ctx->rt; DynBuf dbuf1; int i, atoms_size; - uint8_t version; dbuf1 = s->dbuf; js_dbuf_init(s->ctx, &s->dbuf); - - version = BC_VERSION; - if (s->byte_swap) - version ^= BC_BE_VERSION; - bc_put_u8(s, version); + bc_put_u8(s, BC_VERSION); bc_put_leb128(s, s->idx_to_atom_count); for(i = 0; i < s->idx_to_atom_count; i++) { @@ -34820,8 +35682,6 @@ uint8_t *JS_WriteObject2(JSContext *ctx, size_t *psize, JSValueConst obj, memset(s, 0, sizeof(*s)); s->ctx = ctx; - /* XXX: byte swapped output is untested */ - s->byte_swap = ((flags & JS_WRITE_OBJ_BSWAP) != 0); s->allow_bytecode = ((flags & JS_WRITE_OBJ_BYTECODE) != 0); s->allow_sab = ((flags & JS_WRITE_OBJ_SAB) != 0); s->allow_reference = ((flags & JS_WRITE_OBJ_REFERENCE) != 0); @@ -34942,33 +35802,45 @@ static int bc_get_u8(BCReaderState *s, uint8_t *pval) static int bc_get_u16(BCReaderState *s, uint16_t *pval) { + uint16_t v; if (unlikely(s->buf_end - s->ptr < 2)) { *pval = 0; /* avoid warning */ return bc_read_error_end(s); } - *pval = get_u16(s->ptr); + v = get_u16(s->ptr); + if (is_be()) + v = bswap16(v); + *pval = v; s->ptr += 2; return 0; } -static maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval) +static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval) { + uint32_t v; if (unlikely(s->buf_end - s->ptr < 4)) { *pval = 0; /* avoid warning */ return bc_read_error_end(s); } - *pval = get_u32(s->ptr); + v = get_u32(s->ptr); + if (is_be()) + v = bswap32(v); + *pval = v; s->ptr += 4; return 0; } static int bc_get_u64(BCReaderState *s, uint64_t *pval) { + uint64_t v; if (unlikely(s->buf_end - s->ptr < 8)) { *pval = 0; /* avoid warning */ return bc_read_error_end(s); } - *pval = get_u64(s->ptr); + v = get_u64(s->ptr); + if (is_be()) + v = bswap64(v); + *pval = v; s->ptr += 8; return 0; } @@ -35025,7 +35897,7 @@ static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx) { JSAtom atom; - if (JS_AtomIsTaggedInt(idx)) { + if (__JS_AtomIsTaggedInt(idx)) { atom = idx; } else if (idx < s->first_atom) { atom = JS_DupAtom(s->ctx, idx); @@ -35049,7 +35921,7 @@ static int bc_get_atom(BCReaderState *s, JSAtom *patom) if (bc_get_leb128(s, &v)) return -1; if (v & 1) { - *patom = JS_AtomFromUInt32(v >> 1); + *patom = __JS_AtomFromUInt32(v >> 1); return 0; } else { return bc_idx_to_atom(s, patom, v >> 1); @@ -35080,7 +35952,13 @@ static JSString *JS_ReadString(BCReaderState *s) } memcpy(p->u.str8, s->ptr, size); s->ptr += size; - if (!is_wide_char) { + if (is_wide_char) { + if (is_be()) { + uint32_t i; + for (i = 0; i < len; i++) + p->u.str16[i] = bswap16(p->u.str16[i]); + } + } else { p->u.str8[size] = '\0'; /* add the trailing zero for 8 bit strings */ } #ifdef DUMP_READ_OBJECT @@ -35119,6 +35997,9 @@ static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b, } b->byte_code_buf = bc_buf; + if (is_be()) + bc_byte_swap(bc_buf, bc_len); + pos = 0; while (pos < bc_len) { op = bc_buf[pos]; @@ -35153,18 +36034,16 @@ static int JS_ReadFunctionBytecode(BCReaderState *s, JSFunctionBytecode *b, return 0; } -#ifdef CONFIG_BIGNUM static JSValue JS_ReadBigNum(BCReaderState *s, int tag) { JSValue obj = JS_UNDEFINED; uint8_t v8; int32_t e; uint32_t len; - limb_t l, i, n, j; + limb_t l, i, n; JSBigFloat *p; limb_t v; bf_t *a; - int bpos, d; p = js_new_bf(s->ctx); if (!p) @@ -35173,12 +36052,14 @@ static JSValue JS_ReadBigNum(BCReaderState *s, int tag) case BC_TAG_BIG_INT: obj = JS_MKPTR(JS_TAG_BIG_INT, p); break; +#ifdef CONFIG_BIGNUM case BC_TAG_BIG_FLOAT: obj = JS_MKPTR(JS_TAG_BIG_FLOAT, p); break; case BC_TAG_BIG_DECIMAL: obj = JS_MKPTR(JS_TAG_BIG_DECIMAL, p); break; +#endif default: abort(); } @@ -35212,45 +36093,23 @@ static JSValue JS_ReadBigNum(BCReaderState *s, int tag) JS_ThrowInternalError(s->ctx, "invalid bignum length"); goto fail; } - if (tag != BC_TAG_BIG_DECIMAL) - l = (len + sizeof(limb_t) - 1) / sizeof(limb_t); - else +#ifdef CONFIG_BIGNUM + if (tag == BC_TAG_BIG_DECIMAL) { l = (len + LIMB_DIGITS - 1) / LIMB_DIGITS; + } else +#endif + { + l = (len + sizeof(limb_t) - 1) / sizeof(limb_t); + } if (bf_resize(a, l)) { JS_ThrowOutOfMemory(s->ctx); goto fail; } - if (tag != BC_TAG_BIG_DECIMAL) { - n = len & (sizeof(limb_t) - 1); - if (n != 0) { - v = 0; - for(i = 0; i < n; i++) { - if (bc_get_u8(s, &v8)) - goto fail; - v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8); - } - a->tab[0] = v; - i = 1; - } else { - i = 0; - } - for(; i < l; i++) { -#if LIMB_BITS == 32 - if (bc_get_u32(s, &v)) - goto fail; -#ifdef WORDS_BIGENDIAN - v = bswap32(v); -#endif -#else - if (bc_get_u64(s, &v)) - goto fail; -#ifdef WORDS_BIGENDIAN - v = bswap64(v); -#endif -#endif - a->tab[i] = v; - } - } else { +#ifdef CONFIG_BIGNUM + if (tag == BC_TAG_BIG_DECIMAL) { + limb_t j; + int bpos, d; + bpos = 0; for(i = 0; i < l; i++) { if (i == 0 && (n = len % LIMB_DIGITS) != 0) { @@ -35277,6 +36136,32 @@ static JSValue JS_ReadBigNum(BCReaderState *s, int tag) } a->tab[i] = v; } + } else +#endif /* CONFIG_BIGNUM */ + { + n = len & (sizeof(limb_t) - 1); + if (n != 0) { + v = 0; + for(i = 0; i < n; i++) { + if (bc_get_u8(s, &v8)) + goto fail; + v |= (limb_t)v8 << ((sizeof(limb_t) - n + i) * 8); + } + a->tab[0] = v; + i = 1; + } else { + i = 0; + } + for(; i < l; i++) { +#if LIMB_BITS == 32 + if (bc_get_u32(s, &v)) + goto fail; +#else + if (bc_get_u64(s, &v)) + goto fail; +#endif + a->tab[i] = v; + } } } bc_read_trace(s, "}\n"); @@ -35285,7 +36170,6 @@ static JSValue JS_ReadBigNum(BCReaderState *s, int tag) JS_FreeValue(s->ctx, obj); return JS_EXCEPTION; } -#endif /* CONFIG_BIGNUM */ static JSValue JS_ReadObjectRec(BCReaderState *s); @@ -35335,6 +36219,7 @@ static JSValue JS_ReadFunctionTag(BCReaderState *s) bc.arguments_allowed = bc_get_flags(v16, &idx, 1); bc.has_debug = bc_get_flags(v16, &idx, 1); bc.backtrace_barrier = bc_get_flags(v16, &idx, 1); + bc.is_direct_or_indirect_eval = bc_get_flags(v16, &idx, 1); bc.read_only_bytecode = s->is_rom_data; if (bc_get_u8(s, &v8)) goto fail; @@ -35513,7 +36398,7 @@ static JSValue JS_ReadModule(BCReaderState *s) m = js_new_module_def(ctx, module_name); if (!m) goto fail; - obj = JS_DupValue(ctx, JS_MKPTR(JS_TAG_MODULE, m)); + obj = JS_NewModuleValue(ctx, m); if (bc_get_leb128_int(s, &m->req_module_entries_count)) goto fail; if (m->req_module_entries_count != 0) { @@ -35586,6 +36471,10 @@ static JSValue JS_ReadModule(BCReaderState *s) } } + if (bc_get_u8(s, &v8)) + goto fail; + m->has_tla = (v8 != 0); + m->func_obj = JS_ReadObjectRec(s); if (JS_IsException(m->func_obj)) goto fail; @@ -35864,7 +36753,7 @@ static JSValue JS_ReadObjectRec(BCReaderState *s) if (bc_get_u64(s, &u.u64)) return JS_EXCEPTION; bc_read_trace(s, "%g\n", u.d); - obj = JS_NewFloat64Impl(ctx, u.d); + obj = __JS_NewFloat64(ctx, u.d); } break; case BC_TAG_STRING: @@ -35910,13 +36799,13 @@ static JSValue JS_ReadObjectRec(BCReaderState *s) case BC_TAG_OBJECT_VALUE: obj = JS_ReadObjectValue(s); break; -#ifdef CONFIG_BIGNUM case BC_TAG_BIG_INT: +#ifdef CONFIG_BIGNUM case BC_TAG_BIG_FLOAT: case BC_TAG_BIG_DECIMAL: +#endif obj = JS_ReadBigNum(s, tag); break; -#endif case BC_TAG_OBJECT_REFERENCE: { uint32_t val; @@ -35950,7 +36839,6 @@ static int JS_ReadObjectAtoms(BCReaderState *s) if (bc_get_u8(s, &v8)) return -1; - /* XXX: could support byte swapped input */ if (v8 != BC_VERSION) { JS_ThrowSyntaxError(s->ctx, "invalid version (%d expected=%d)", v8, BC_VERSION); @@ -36172,7 +37060,7 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj, val = JS_NewInt64(ctx, e->u.i64); break; case JS_DEF_PROP_DOUBLE: - val = JS_NewFloat64Impl(ctx, e->u.f64); + val = __JS_NewFloat64(ctx, e->u.f64); break; case JS_DEF_PROP_UNDEFINED: val = JS_UNDEFINED; @@ -36236,7 +37124,7 @@ int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, val = JS_NewInt64(ctx, e->u.i64); break; case JS_DEF_PROP_DOUBLE: - val = JS_NewFloat64Impl(ctx, e->u.f64); + val = __JS_NewFloat64(ctx, e->u.f64); break; case JS_DEF_OBJECT: val = JS_NewObject(ctx); @@ -36325,12 +37213,10 @@ static JSValue js_global_isNaN(JSContext *ctx, JSValueConst this_val, static JSValue js_global_isFinite(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - BOOL res; double d; if (unlikely(JS_ToFloat64(ctx, &d, argv[0]))) return JS_EXCEPTION; - res = isfinite(d); - return JS_NewBool(ctx, res); + return JS_NewBool(ctx, isfinite(d)); } /* Object class */ @@ -36348,10 +37234,10 @@ static JSValue JS_ToObject(JSContext *ctx, JSValueConst val) case JS_TAG_OBJECT: case JS_TAG_EXCEPTION: return JS_DupValue(ctx, val); -#ifdef CONFIG_BIGNUM case JS_TAG_BIG_INT: obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_INT); goto set_value; +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: obj = JS_NewObjectClass(ctx, JS_CLASS_BIG_FLOAT); goto set_value; @@ -36469,7 +37355,7 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d, return -1; } -static warn_unused int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj, +static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj, JSAtom prop, JSValueConst desc, int flags) { @@ -36485,7 +37371,7 @@ static warn_unused int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj, return ret; } -static warn_unused int JS_ObjectDefineProperties(JSContext *ctx, +static __exception int JS_ObjectDefineProperties(JSContext *ctx, JSValueConst obj, JSValueConst properties) { @@ -36724,13 +37610,13 @@ static JSValue js_object_getOwnPropertyDescriptor(JSContext *ctx, JSValueConst t } else { if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_value, JS_DupValue(ctx, desc.value), flags) < 0 || JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, - JS_NewBool(ctx, (desc.flags & JS_PROP_WRITABLE) != 0), flags) < 0) + JS_NewBool(ctx, desc.flags & JS_PROP_WRITABLE), flags) < 0) goto exception1; } if (JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable, - JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0), flags) < 0 + JS_NewBool(ctx, desc.flags & JS_PROP_ENUMERABLE), flags) < 0 || JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable, - JS_NewBool(ctx, (desc.flags & JS_PROP_CONFIGURABLE) != 0), flags) < 0) + JS_NewBool(ctx, desc.flags & JS_PROP_CONFIGURABLE), flags) < 0) goto exception1; js_free_desc(ctx, &desc); } @@ -36970,6 +37856,32 @@ static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val, return JS_NewBool(ctx, ret); } +static JSValue js_object_hasOwn(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj; + JSAtom atom; + JSObject *p; + BOOL ret; + + obj = JS_ToObject(ctx, argv[0]); + if (JS_IsException(obj)) + return obj; + atom = JS_ValueToAtom(ctx, argv[1]); + if (unlikely(atom == JS_ATOM_NULL)) { + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; + } + p = JS_VALUE_GET_OBJ(obj); + ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom); + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, obj); + if (ret < 0) + return JS_EXCEPTION; + else + return JS_NewBool(ctx, ret); +} + static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -37459,7 +38371,7 @@ static JSValue js_object_propertyIsEnumerable(JSContext *ctx, JSValueConst this_ if (has_prop < 0) goto exception; if (has_prop) { - res = JS_NewBool(ctx, (desc.flags & JS_PROP_ENUMERABLE) != 0); + res = JS_NewBool(ctx, desc.flags & JS_PROP_ENUMERABLE); js_free_desc(ctx, &desc); } else { res = JS_FALSE; @@ -37516,32 +38428,6 @@ exception: return res; } -static JSValue js_object_hasOwn(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue obj; - JSAtom atom; - JSObject *p; - BOOL ret; - - obj = JS_ToObject(ctx, argv[0]); - if (JS_IsException(obj)) - return obj; - atom = JS_ValueToAtom(ctx, argv[1]); - if (unlikely(atom == JS_ATOM_NULL)) { - JS_FreeValue(ctx, obj); - return JS_EXCEPTION; - } - p = JS_VALUE_GET_OBJ(obj); - ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom); - JS_FreeAtom(ctx, atom); - JS_FreeValue(ctx, obj); - if (ret < 0) - return JS_EXCEPTION; - else - return JS_NewBool(ctx, ret); -} - static const JSCFunctionListEntry js_object_funcs[] = { JS_CFUNC_DEF("create", 2, js_object_create ), JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ), @@ -37550,6 +38436,7 @@ static const JSCFunctionListEntry js_object_funcs[] = { JS_CFUNC_DEF("defineProperties", 2, js_object_defineProperties ), JS_CFUNC_DEF("getOwnPropertyNames", 1, js_object_getOwnPropertyNames ), JS_CFUNC_DEF("getOwnPropertySymbols", 1, js_object_getOwnPropertySymbols ), + JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 0 ), JS_CFUNC_MAGIC_DEF("keys", 1, js_object_keys, JS_ITERATOR_KIND_KEY ), JS_CFUNC_MAGIC_DEF("values", 1, js_object_keys, JS_ITERATOR_KIND_VALUE ), JS_CFUNC_MAGIC_DEF("entries", 1, js_object_keys, JS_ITERATOR_KIND_KEY_AND_VALUE ), @@ -37671,7 +38558,7 @@ static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target, return JS_EXCEPTION; } -static warn_unused int js_get_length32(JSContext *ctx, uint32_t *pres, +static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, JSValueConst obj) { JSValue len_val; @@ -37683,7 +38570,7 @@ static warn_unused int js_get_length32(JSContext *ctx, uint32_t *pres, return JS_ToUint32Free(ctx, pres, len_val); } -static warn_unused int js_get_length64(JSContext *ctx, int64_t *pres, +static __exception int js_get_length64(JSContext *ctx, int64_t *pres, JSValueConst obj) { JSValue len_val; @@ -37719,7 +38606,9 @@ static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, if (js_get_length32(ctx, &len, array_arg)) return NULL; if (len > JS_MAX_LOCAL_VARS) { - JS_ThrowInternalError(ctx, "too many arguments"); + // XXX: check for stack overflow? + JS_ThrowRangeError(ctx, "too many arguments in function call (only %d allowed)", + JS_MAX_LOCAL_VARS); return NULL; } /* avoid allocating 0 bytes */ @@ -37768,9 +38657,9 @@ static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, if (!tab) return JS_EXCEPTION; if (magic & 1) { - ret = JS_CallConstructor2(ctx, this_val, this_arg, len, tab); + ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab); } else { - ret = JS_Call(ctx, this_val, this_arg, len, tab); + ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab); } free_arg_list(ctx, tab, len); return ret; @@ -37980,7 +38869,8 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv, int magic) { JSValue obj, msg, proto; - JSValueConst message; + JSValueConst message, options; + int arg_index; if (JS_IsUndefined(new_target)) new_target = JS_GetActiveFunction(ctx); @@ -38006,12 +38896,9 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, JS_FreeValue(ctx, proto); if (JS_IsException(obj)) return obj; - if (magic == JS_AGGREGATE_ERROR) { - message = argv[1]; - } else { - message = argv[0]; - } + arg_index = (magic == JS_AGGREGATE_ERROR); + message = argv[arg_index++]; if (!JS_IsUndefined(message)) { msg = JS_ToString(ctx, message); if (unlikely(JS_IsException(msg))) @@ -38020,6 +38907,22 @@ static JSValue js_error_constructor(JSContext *ctx, JSValueConst new_target, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); } + if (arg_index < argc) { + options = argv[arg_index]; + if (JS_IsObject(options)) { + int present = JS_HasProperty(ctx, options, JS_ATOM_cause); + if (present < 0) + goto exception; + if (present) { + JSValue cause = JS_GetProperty(ctx, options, JS_ATOM_cause); + if (JS_IsException(cause)) + goto exception; + JS_DefinePropertyValue(ctx, obj, JS_ATOM_cause, cause, + JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE); + } + } + } + if (magic == JS_AGGREGATE_ERROR) { JSValue error_list = iterator_to_array(ctx, argv[0]); if (JS_IsException(error_list)) @@ -38413,6 +39316,106 @@ static int JS_isConcatSpreadable(JSContext *ctx, JSValueConst obj) return JS_IsArray(ctx, obj); } +static JSValue js_array_at(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue obj, ret; + int64_t len, idx; + JSValue *arrp; + uint32_t count; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + if (JS_ToInt64Sat(ctx, &idx, argv[0])) + goto exception; + + if (idx < 0) + idx = len + idx; + if (idx < 0 || idx >= len) { + ret = JS_UNDEFINED; + } else if (js_get_fast_array(ctx, obj, &arrp, &count) && idx < count) { + ret = JS_DupValue(ctx, arrp[idx]); + } else { + int present = JS_TryGetPropertyInt64(ctx, obj, idx, &ret); + if (present < 0) + goto exception; + if (!present) + ret = JS_UNDEFINED; + } + JS_FreeValue(ctx, obj); + return ret; + exception: + JS_FreeValue(ctx, obj); + return JS_EXCEPTION; +} + +static JSValue js_array_with(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue arr, obj, ret, *arrp, *pval; + JSObject *p; + int64_t i, len, idx; + uint32_t count32; + + ret = JS_EXCEPTION; + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + if (JS_ToInt64Sat(ctx, &idx, argv[0])) + goto exception; + + if (idx < 0) + idx = len + idx; + + if (idx < 0 || idx >= len) { + JS_ThrowRangeError(ctx, "invalid array index: %" PRId64, idx); + goto exception; + } + + arr = js_allocate_fast_array(ctx, len); + if (JS_IsException(arr)) + goto exception; + + p = JS_VALUE_GET_OBJ(arr); + i = 0; + pval = p->u.array.u.values; + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + for (; i < idx; i++, pval++) + *pval = JS_DupValue(ctx, arrp[i]); + *pval = JS_DupValue(ctx, argv[1]); + for (i++, pval++; i < len; i++, pval++) + *pval = JS_DupValue(ctx, arrp[i]); + } else { + for (; i < idx; i++, pval++) + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) + goto fill_and_fail; + *pval = JS_DupValue(ctx, argv[1]); + for (i++, pval++; i < len; i++, pval++) { + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) { + fill_and_fail: + for (; i < len; i++, pval++) + *pval = JS_UNDEFINED; + goto exception; + } + } + } + + if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0) + goto exception; + + ret = arr; + arr = JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, obj); + return ret; +} + static JSValue js_array_concat(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -38778,9 +39781,10 @@ static JSValue js_array_includes(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { JSValue obj, val; - int64_t len, n, res; + int64_t len, n; JSValue *arrp; uint32_t count; + int res; obj = JS_ToObject(ctx, this_val); if (js_get_length64(ctx, &len, obj)) @@ -38911,13 +39915,21 @@ static JSValue js_array_lastIndexOf(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; } +enum { + ArrayFind, + ArrayFindIndex, + ArrayFindLast, + ArrayFindLastIndex, +}; + static JSValue js_array_find(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int findIndex) + int argc, JSValueConst *argv, int mode) { JSValueConst func, this_arg; JSValueConst args[3]; JSValue obj, val, index_val, res; - int64_t len, k; + int64_t len, k, end; + int dir; index_val = JS_UNDEFINED; val = JS_UNDEFINED; @@ -38933,7 +39945,17 @@ static JSValue js_array_find(JSContext *ctx, JSValueConst this_val, if (argc > 1) this_arg = argv[1]; - for(k = 0; k < len; k++) { + k = 0; + dir = 1; + end = len; + if (mode == ArrayFindLast || mode == ArrayFindLastIndex) { + k = len - 1; + dir = -1; + end = -1; + } + + // TODO(bnoordhuis) add fast path for fast arrays + for(; k != end; k += dir) { index_val = JS_NewInt64(ctx, k); if (JS_IsException(index_val)) goto exception; @@ -38947,7 +39969,7 @@ static JSValue js_array_find(JSContext *ctx, JSValueConst this_val, if (JS_IsException(res)) goto exception; if (JS_ToBoolFree(ctx, res)) { - if (findIndex) { + if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) { JS_FreeValue(ctx, val); JS_FreeValue(ctx, obj); return index_val; @@ -38961,7 +39983,7 @@ static JSValue js_array_find(JSContext *ctx, JSValueConst this_val, JS_FreeValue(ctx, index_val); } JS_FreeValue(ctx, obj); - if (findIndex) + if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) return JS_NewInt32(ctx, -1); else return JS_UNDEFINED; @@ -38984,7 +40006,8 @@ static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val, method = JS_GetProperty(ctx, obj, JS_ATOM_join); if (JS_IsException(method)) { ret = JS_EXCEPTION; - } else if (!JS_IsFunction(ctx, method)) { + } else + if (!JS_IsFunction(ctx, method)) { /* Use intrinsic Object.prototype.toString */ JS_FreeValue(ctx, method); ret = js_object_toString(ctx, obj, 0, NULL); @@ -39211,6 +40234,61 @@ static JSValue js_array_reverse(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; } +// Note: a.toReversed() is a.slice().reverse() with the twist that a.slice() +// leaves holes in sparse arrays intact whereas a.toReversed() replaces them +// with undefined, thus in effect creating a dense array. +// Does not use Array[@@species], always returns a base Array. +static JSValue js_array_toReversed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue arr, obj, ret, *arrp, *pval; + JSObject *p; + int64_t i, len; + uint32_t count32; + + ret = JS_EXCEPTION; + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + arr = js_allocate_fast_array(ctx, len); + if (JS_IsException(arr)) + goto exception; + + if (len > 0) { + p = JS_VALUE_GET_OBJ(arr); + + i = len - 1; + pval = p->u.array.u.values; + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + for (; i >= 0; i--, pval++) + *pval = JS_DupValue(ctx, arrp[i]); + } else { + // Query order is observable; test262 expects descending order. + for (; i >= 0; i--, pval++) { + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) { + // Exception; initialize remaining elements. + for (; i >= 0; i--, pval++) + *pval = JS_UNDEFINED; + goto exception; + } + } + } + + if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0) + goto exception; + } + + ret = arr; + arr = JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, obj); + return ret; +} + static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int splice) { @@ -39232,7 +40310,8 @@ static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val, if (argc == 0) { item_count = 0; del_count = 0; - } else if (argc == 1) { + } else + if (argc == 1) { item_count = 0; del_count = len - start; } else { @@ -39317,6 +40396,92 @@ static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; } +static JSValue js_array_toSpliced(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue arr, obj, ret, *arrp, *pval, *last; + JSObject *p; + int64_t i, j, len, newlen, start, add, del; + uint32_t count32; + + pval = NULL; + last = NULL; + ret = JS_EXCEPTION; + arr = JS_UNDEFINED; + + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + start = 0; + if (argc > 0) + if (JS_ToInt64Clamp(ctx, &start, argv[0], 0, len, len)) + goto exception; + + del = 0; + if (argc > 0) + del = len - start; + if (argc > 1) + if (JS_ToInt64Clamp(ctx, &del, argv[1], 0, del, 0)) + goto exception; + + add = 0; + if (argc > 2) + add = argc - 2; + + newlen = len + add - del; + if (newlen > MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "invalid array length"); + goto exception; + } + + arr = js_allocate_fast_array(ctx, newlen); + if (JS_IsException(arr)) + goto exception; + + if (newlen <= 0) + goto done; + + p = JS_VALUE_GET_OBJ(arr); + pval = &p->u.array.u.values[0]; + last = &p->u.array.u.values[newlen]; + + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + for (i = 0; i < start; i++, pval++) + *pval = JS_DupValue(ctx, arrp[i]); + for (j = 0; j < add; j++, pval++) + *pval = JS_DupValue(ctx, argv[2 + j]); + for (i += del; i < len; i++, pval++) + *pval = JS_DupValue(ctx, arrp[i]); + } else { + for (i = 0; i < start; i++, pval++) + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) + goto exception; + for (j = 0; j < add; j++, pval++) + *pval = JS_DupValue(ctx, argv[2 + j]); + for (i += del; i < len; i++, pval++) + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) + goto exception; + } + + assert(pval == last); + + if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, newlen)) < 0) + goto exception; + +done: + ret = arr; + arr = JS_UNDEFINED; + +exception: + while (pval != last) + *pval++ = JS_UNDEFINED; + + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, obj); + return ret; +} + static JSValue js_array_copyWithin(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -39620,6 +40785,68 @@ fail: return JS_EXCEPTION; } +// Note: a.toSorted() is a.slice().sort() with the twist that a.slice() +// leaves holes in sparse arrays intact whereas a.toSorted() replaces them +// with undefined, thus in effect creating a dense array. +// Does not use Array[@@species], always returns a base Array. +static JSValue js_array_toSorted(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue arr, obj, ret, *arrp, *pval; + JSObject *p; + int64_t i, len; + uint32_t count32; + int ok; + + ok = JS_IsUndefined(argv[0]) || JS_IsFunction(ctx, argv[0]); + if (!ok) + return JS_ThrowTypeError(ctx, "not a function"); + + ret = JS_EXCEPTION; + arr = JS_UNDEFINED; + obj = JS_ToObject(ctx, this_val); + if (js_get_length64(ctx, &len, obj)) + goto exception; + + arr = js_allocate_fast_array(ctx, len); + if (JS_IsException(arr)) + goto exception; + + if (len > 0) { + p = JS_VALUE_GET_OBJ(arr); + i = 0; + pval = p->u.array.u.values; + if (js_get_fast_array(ctx, obj, &arrp, &count32) && count32 == len) { + for (; i < len; i++, pval++) + *pval = JS_DupValue(ctx, arrp[i]); + } else { + for (; i < len; i++, pval++) { + if (-1 == JS_TryGetPropertyInt64(ctx, obj, i, pval)) { + for (; i < len; i++, pval++) + *pval = JS_UNDEFINED; + goto exception; + } + } + } + + if (JS_SetProperty(ctx, arr, JS_ATOM_length, JS_NewInt64(ctx, len)) < 0) + goto exception; + } + + ret = js_array_sort(ctx, arr, argc, argv); + if (JS_IsException(ret)) + goto exception; + JS_FreeValue(ctx, ret); + + ret = arr; + arr = JS_UNDEFINED; + +exception: + JS_FreeValue(ctx, arr); + JS_FreeValue(ctx, obj); + return ret; +} + typedef struct JSArrayIteratorData { JSValue obj; JSIteratorKindEnum kind; @@ -39772,6 +40999,8 @@ static const JSCFunctionListEntry js_iterator_proto_funcs[] = { }; static const JSCFunctionListEntry js_array_proto_funcs[] = { + JS_CFUNC_DEF("at", 1, js_array_at ), + JS_CFUNC_DEF("with", 2, js_array_with ), JS_CFUNC_DEF("concat", 1, js_array_concat ), JS_CFUNC_MAGIC_DEF("every", 1, js_array_every, special_every ), JS_CFUNC_MAGIC_DEF("some", 1, js_array_every, special_some ), @@ -39781,8 +41010,10 @@ static const JSCFunctionListEntry js_array_proto_funcs[] = { JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce ), JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight ), JS_CFUNC_DEF("fill", 1, js_array_fill ), - JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, 0 ), - JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, 1 ), + JS_CFUNC_MAGIC_DEF("find", 1, js_array_find, ArrayFind ), + JS_CFUNC_MAGIC_DEF("findIndex", 1, js_array_find, ArrayFindIndex ), + JS_CFUNC_MAGIC_DEF("findLast", 1, js_array_find, ArrayFindLast ), + JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_array_find, ArrayFindLastIndex ), JS_CFUNC_DEF("indexOf", 1, js_array_indexOf ), JS_CFUNC_DEF("lastIndexOf", 1, js_array_lastIndexOf ), JS_CFUNC_DEF("includes", 1, js_array_includes ), @@ -39794,9 +41025,12 @@ static const JSCFunctionListEntry js_array_proto_funcs[] = { JS_CFUNC_MAGIC_DEF("shift", 0, js_array_pop, 1 ), JS_CFUNC_MAGIC_DEF("unshift", 1, js_array_push, 1 ), JS_CFUNC_DEF("reverse", 0, js_array_reverse ), + JS_CFUNC_DEF("toReversed", 0, js_array_toReversed ), JS_CFUNC_DEF("sort", 1, js_array_sort ), + JS_CFUNC_DEF("toSorted", 1, js_array_toSorted ), JS_CFUNC_MAGIC_DEF("slice", 2, js_array_slice, 0 ), JS_CFUNC_MAGIC_DEF("splice", 2, js_array_slice, 1 ), + JS_CFUNC_DEF("toSpliced", 2, js_array_toSpliced ), JS_CFUNC_DEF("copyWithin", 2, js_array_copyWithin ), JS_CFUNC_MAGIC_DEF("flatMap", 1, js_array_flatten, 1 ), JS_CFUNC_MAGIC_DEF("flat", 0, js_array_flatten, 0 ), @@ -39824,17 +41058,19 @@ static JSValue js_number_constructor(JSContext *ctx, JSValueConst new_target, if (JS_IsException(val)) return val; switch(JS_VALUE_GET_TAG(val)) { -#ifdef CONFIG_BIGNUM case JS_TAG_BIG_INT: +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: +#endif { JSBigFloat *p = JS_VALUE_GET_PTR(val); double d; bf_get_float64(&p->num, &d, BF_RNDN); JS_FreeValue(ctx, val); - val = __JS_NewFloat64(ctx, d); + val = JS_NewFloat64(ctx, d); } break; +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_DECIMAL: val = JS_ToStringFree(ctx, val); if (JS_IsException(val)) @@ -39983,8 +41219,16 @@ static JSValue js_number_toString(JSContext *ctx, JSValueConst this_val, if (base < 0) goto fail; } + if (JS_VALUE_GET_TAG(val) == JS_TAG_INT) { + char buf1[70], *ptr; + ptr = i64toa(buf1 + sizeof(buf1), JS_VALUE_GET_INT(val), base); + return JS_NewString(ctx, ptr); + } if (JS_ToFloat64Free(ctx, &d, val)) return JS_EXCEPTION; + if (base != 10 && isfinite(d)) { + return js_dtoa_radix(ctx, d, base); + } return js_dtoa(ctx, d, base, 0, JS_DTOA_VAR_FORMAT); fail: JS_FreeValue(ctx, val); @@ -40008,7 +41252,7 @@ static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val, if (f < 0 || f > 100) return JS_ThrowRangeError(ctx, "invalid number of digits"); if (fabs(d) >= 1e21) { - return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d)); + return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); } else { return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT); } @@ -40029,7 +41273,7 @@ static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val, if (JS_ToInt32Sat(ctx, &f, argv[0])) return JS_EXCEPTION; if (!isfinite(d)) { - return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d)); + return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); } if (JS_IsUndefined(argv[0])) { flags = 0; @@ -40061,7 +41305,7 @@ static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; if (!isfinite(d)) { to_string: - return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d)); + return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); } if (p < 1 || p > 100) return JS_ThrowRangeError(ctx, "invalid number of digits"); @@ -40182,17 +41426,14 @@ static int js_string_get_own_property(JSContext *ctx, uint32_t idx, ch; /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ - if (JS_AtomIsTaggedInt(prop)) { + if (__JS_AtomIsTaggedInt(prop)) { p = JS_VALUE_GET_OBJ(obj); if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { p1 = JS_VALUE_GET_STRING(p->u.object_data); - idx = JS_AtomToUInt32(prop); + idx = __JS_AtomToUInt32(prop); if (idx < p1->len) { if (desc) { - if (p1->is_wide_char) - ch = p1->u.str16[idx]; - else - ch = p1->u.str8[idx]; + ch = string_get(p1, idx); desc->flags = JS_PROP_ENUMERABLE; desc->value = js_new_string_char(ctx, ch); desc->getter = JS_UNDEFINED; @@ -40215,8 +41456,8 @@ static int js_string_define_own_property(JSContext *ctx, JSObject *p; JSString *p1, *p2; - if (JS_AtomIsTaggedInt(prop)) { - idx = JS_AtomToUInt32(prop); + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); p = JS_VALUE_GET_OBJ(this_obj); if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING) goto def; @@ -40250,8 +41491,8 @@ static int js_string_delete_property(JSContext *ctx, { uint32_t idx; - if (JS_AtomIsTaggedInt(prop)) { - idx = JS_AtomToUInt32(prop); + if (__JS_AtomIsTaggedInt(prop)) { + idx = __JS_AtomToUInt32(prop); if (idx < js_string_obj_get_length(ctx, obj)) { return FALSE; } @@ -40347,7 +41588,7 @@ static JSValue js_string_fromCodePoint(JSContext *ctx, JSValueConst this_val, } else { if (JS_ToFloat64(ctx, &d, argv[i])) goto fail; - if (d < 0 || d > 0x10ffff || (c = (int)d) != d) + if (isnan(d) || d < 0 || d > 0x10ffff || (c = (int)d) != d) goto range_error; } if (string_buffer_putc(b, c)) @@ -40458,10 +41699,7 @@ static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val, if (idx < 0 || idx >= p->len) { ret = JS_NAN; } else { - if (p->is_wide_char) - c = p->u.str16[idx]; - else - c = p->u.str8[idx]; + c = string_get(p, idx); ret = JS_NewInt32(ctx, c); } JS_FreeValue(ctx, val); @@ -40469,7 +41707,7 @@ static JSValue js_string_charCodeAt(JSContext *ctx, JSValueConst this_val, } static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) + int argc, JSValueConst *argv, int is_at) { JSValue val, ret; JSString *p; @@ -40483,13 +41721,15 @@ static JSValue js_string_charAt(JSContext *ctx, JSValueConst this_val, JS_FreeValue(ctx, val); return JS_EXCEPTION; } + if (idx < 0 && is_at) + idx += p->len; if (idx < 0 || idx >= p->len) { - ret = js_new_string8(ctx, NULL, 0); - } else { - if (p->is_wide_char) - c = p->u.str16[idx]; + if (is_at) + ret = JS_UNDEFINED; else - c = p->u.str8[idx]; + ret = js_new_string8(ctx, NULL, 0); + } else { + c = string_get(p, idx); ret = js_new_string_char(ctx, c); } JS_FreeValue(ctx, val); @@ -40597,6 +41837,80 @@ static int64_t string_advance_index(JSString *p, int64_t index, BOOL unicode) return index; } +/* return the position of the first invalid character in the string or + -1 if none */ +static int js_string_find_invalid_codepoint(JSString *p) +{ + int i; + if (!p->is_wide_char) + return -1; + for(i = 0; i < p->len; i++) { + uint32_t c = p->u.str16[i]; + if (is_surrogate(c)) { + if (is_hi_surrogate(c) && (i + 1) < p->len + && is_lo_surrogate(p->u.str16[i + 1])) { + i++; + } else { + return i; + } + } + } + return -1; +} + +static JSValue js_string_isWellFormed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue str; + JSString *p; + BOOL ret; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return JS_EXCEPTION; + p = JS_VALUE_GET_STRING(str); + ret = (js_string_find_invalid_codepoint(p) < 0); + JS_FreeValue(ctx, str); + return JS_NewBool(ctx, ret); +} + +static JSValue js_string_toWellFormed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue str, ret; + JSString *p; + int i; + + str = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(str)) + return JS_EXCEPTION; + + p = JS_VALUE_GET_STRING(str); + /* avoid reallocating the string if it is well-formed */ + i = js_string_find_invalid_codepoint(p); + if (i < 0) + return str; + + ret = js_new_string16(ctx, p->u.str16, p->len); + JS_FreeValue(ctx, str); + if (JS_IsException(ret)) + return JS_EXCEPTION; + + p = JS_VALUE_GET_STRING(ret); + for (; i < p->len; i++) { + uint32_t c = p->u.str16[i]; + if (is_surrogate(c)) { + if (is_hi_surrogate(c) && (i + 1) < p->len + && is_lo_surrogate(p->u.str16[i + 1])) { + i++; + } else { + p->u.str16[i] = 0xFFFD; + } + } + } + return ret; +} + static JSValue js_string_indexOf(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int lastIndexOf) { @@ -40679,7 +41993,7 @@ static JSValue js_string_includes(JSContext *ctx, JSValueConst this_val, ret = js_is_regexp(ctx, argv[0]); if (ret) { if (ret > 0) - JS_ThrowTypeError(ctx, "regex not supported"); + JS_ThrowTypeError(ctx, "regexp not supported"); goto fail; } v = JS_ToString(ctx, argv[0]); @@ -40803,7 +42117,7 @@ static JSValue js_string_match(JSContext *ctx, JSValueConst this_val, JS_FreeValue(ctx, S); return JS_EXCEPTION; } - result = JS_InvokeFree(ctx, rx, atom, 1, &S); + result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst *)&S); JS_FreeValue(ctx, S); return result; } @@ -41241,7 +42555,7 @@ static JSValue js_string_pad(JSContext *ctx, JSValueConst this_val, } } if (n > JS_STRING_LEN_MAX) { - JS_ThrowInternalError(ctx, "string too long"); + JS_ThrowRangeError(ctx, "invalid string length"); goto fail2; } if (string_buffer_init(ctx, b, n)) @@ -41303,8 +42617,9 @@ static JSValue js_string_repeat(JSContext *ctx, JSValueConst this_val, len = p->len; if (len == 0 || n == 1) return str; + // XXX: potential arithmetic overflow if (val * len > JS_STRING_LEN_MAX) { - JS_ThrowInternalError(ctx, "string too long"); + JS_ThrowRangeError(ctx, "invalid string length"); goto fail; } if (string_buffer_init2(ctx, b, n * len, p->is_wide_char)) @@ -41367,10 +42682,10 @@ static int string_prevc(JSString *p, int *pidx) idx--; if (p->is_wide_char) { c = p->u.str16[idx]; - if (c >= 0xdc00 && c < 0xe000 && idx > 0) { + if (is_lo_surrogate(c) && idx > 0) { c1 = p->u.str16[idx - 1]; - if (c1 >= 0xd800 && c1 <= 0xdc00) { - c = (((c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; + if (is_hi_surrogate(c1)) { + c = from_surrogate(c1, c); idx--; } } @@ -41409,26 +42724,6 @@ static BOOL test_final_sigma(JSString *p, int sigma_pos) return !lre_is_cased(c1); } -static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - JSValue a, b; - int cmp; - - a = JS_ToStringCheckObject(ctx, this_val); - if (JS_IsException(a)) - return JS_EXCEPTION; - b = JS_ToString(ctx, argv[0]); - if (JS_IsException(b)) { - JS_FreeValue(ctx, a); - return JS_EXCEPTION; - } - cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b)); - JS_FreeValue(ctx, a); - JS_FreeValue(ctx, b); - return JS_NewInt32(ctx, cmp); -} - static JSValue js_string_toLowerCase(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int to_lower) { @@ -41514,23 +42809,38 @@ static JSValue JS_NewUTF32String(JSContext *ctx, const uint32_t *buf, int len) return JS_EXCEPTION; } +static int js_string_normalize1(JSContext *ctx, uint32_t **pout_buf, + JSValueConst val, + UnicodeNormalizationEnum n_type) +{ + int buf_len, out_len; + uint32_t *buf, *out_buf; + + buf_len = JS_ToUTF32String(ctx, &buf, val); + if (buf_len < 0) + return -1; + out_len = unicode_normalize(&out_buf, buf, buf_len, n_type, + ctx->rt, (DynBufReallocFunc *)js_realloc_rt); + js_free(ctx, buf); + if (out_len < 0) + return -1; + *pout_buf = out_buf; + return out_len; +} + static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { const char *form, *p; size_t form_len; - int is_compat, buf_len, out_len; + int is_compat, out_len; UnicodeNormalizationEnum n_type; JSValue val; - uint32_t *buf, *out_buf; + uint32_t *out_buf; val = JS_ToStringCheckObject(ctx, this_val); if (JS_IsException(val)) return val; - buf_len = JS_ToUTF32String(ctx, &buf, val); - JS_FreeValue(ctx, val); - if (buf_len < 0) - return JS_EXCEPTION; if (argc == 0 || JS_IsUndefined(argv[0])) { n_type = UNICODE_NFC; @@ -41556,22 +42866,96 @@ static JSValue js_string_normalize(JSContext *ctx, JSValueConst this_val, JS_FreeCString(ctx, form); JS_ThrowRangeError(ctx, "bad normalization form"); fail1: - js_free(ctx, buf); + JS_FreeValue(ctx, val); return JS_EXCEPTION; } JS_FreeCString(ctx, form); } - out_len = unicode_normalize(&out_buf, buf, buf_len, n_type, - ctx->rt, (DynBufReallocFunc *)js_realloc_rt); - js_free(ctx, buf); + out_len = js_string_normalize1(ctx, &out_buf, val, n_type); + JS_FreeValue(ctx, val); if (out_len < 0) return JS_EXCEPTION; val = JS_NewUTF32String(ctx, out_buf, out_len); js_free(ctx, out_buf); return val; } -#endif /* CONFIG_ALL_UNICODE */ + +/* return < 0, 0 or > 0 */ +static int js_UTF32_compare(const uint32_t *buf1, int buf1_len, + const uint32_t *buf2, int buf2_len) +{ + int i, len, c, res; + len = min_int(buf1_len, buf2_len); + for(i = 0; i < len; i++) { + /* Note: range is limited so a subtraction is valid */ + c = buf1[i] - buf2[i]; + if (c != 0) + return c; + } + if (buf1_len == buf2_len) + res = 0; + else if (buf1_len < buf2_len) + res = -1; + else + res = 1; + return res; +} + +static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue a, b; + int cmp, a_len, b_len; + uint32_t *a_buf, *b_buf; + + a = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(a)) + return JS_EXCEPTION; + b = JS_ToString(ctx, argv[0]); + if (JS_IsException(b)) { + JS_FreeValue(ctx, a); + return JS_EXCEPTION; + } + a_len = js_string_normalize1(ctx, &a_buf, a, UNICODE_NFC); + JS_FreeValue(ctx, a); + if (a_len < 0) { + JS_FreeValue(ctx, b); + return JS_EXCEPTION; + } + + b_len = js_string_normalize1(ctx, &b_buf, b, UNICODE_NFC); + JS_FreeValue(ctx, b); + if (b_len < 0) { + js_free(ctx, a_buf); + return JS_EXCEPTION; + } + cmp = js_UTF32_compare(a_buf, a_len, b_buf, b_len); + js_free(ctx, a_buf); + js_free(ctx, b_buf); + return JS_NewInt32(ctx, cmp); +} +#else /* CONFIG_ALL_UNICODE */ +static JSValue js_string_localeCompare(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue a, b; + int cmp; + + a = JS_ToStringCheckObject(ctx, this_val); + if (JS_IsException(a)) + return JS_EXCEPTION; + b = JS_ToString(ctx, argv[0]); + if (JS_IsException(b)) { + JS_FreeValue(ctx, a); + return JS_EXCEPTION; + } + cmp = js_string_compare(ctx, JS_VALUE_GET_STRING(a), JS_VALUE_GET_STRING(b)); + JS_FreeValue(ctx, a); + JS_FreeValue(ctx, b); + return JS_NewInt32(ctx, cmp); +} +#endif /* !CONFIG_ALL_UNICODE */ /* also used for String.prototype.valueOf */ static JSValue js_string_toString(JSContext *ctx, JSValueConst this_val, @@ -41743,10 +43127,13 @@ static const JSCFunctionListEntry js_string_funcs[] = { static const JSCFunctionListEntry js_string_proto_funcs[] = { JS_PROP_INT32_DEF("length", 0, JS_PROP_CONFIGURABLE ), + JS_CFUNC_MAGIC_DEF("at", 1, js_string_charAt, 1 ), JS_CFUNC_DEF("charCodeAt", 1, js_string_charCodeAt ), - JS_CFUNC_DEF("charAt", 1, js_string_charAt ), + JS_CFUNC_MAGIC_DEF("charAt", 1, js_string_charAt, 0 ), JS_CFUNC_DEF("concat", 1, js_string_concat ), JS_CFUNC_DEF("codePointAt", 1, js_string_codePointAt ), + JS_CFUNC_DEF("isWellFormed", 0, js_string_isWellFormed ), + JS_CFUNC_DEF("toWellFormed", 0, js_string_toWellFormed ), JS_CFUNC_MAGIC_DEF("indexOf", 1, js_string_indexOf, 0 ), JS_CFUNC_MAGIC_DEF("lastIndexOf", 1, js_string_indexOf, 1 ), JS_CFUNC_MAGIC_DEF("includes", 1, js_string_includes, 0 ), @@ -41852,7 +43239,7 @@ static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, uint32_t tag; if (unlikely(argc == 0)) { - return JS_NewFloat64Impl(ctx, is_max ? INFINITY : -INFINITY); + return __JS_NewFloat64(ctx, is_max ? INFINITY : -INFINITY); } tag = JS_VALUE_GET_TAG(argv[0]); @@ -41966,14 +43353,16 @@ static double js_math_fround(double a) static JSValue js_math_imul(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - int a, b; + uint32_t a, b, c; + int32_t d; - if (JS_ToInt32(ctx, &a, argv[0])) + if (JS_ToUint32(ctx, &a, argv[0])) return JS_EXCEPTION; - if (JS_ToInt32(ctx, &b, argv[1])) + if (JS_ToUint32(ctx, &b, argv[1])) return JS_EXCEPTION; - /* purposely ignoring overflow */ - return JS_NewInt32(ctx, a * b); + c = a * b; + memcpy(&d, &c, sizeof(d)); + return JS_NewInt32(ctx, d); } static JSValue js_math_clz32(JSContext *ctx, JSValueConst this_val, @@ -42025,7 +43414,7 @@ static JSValue js_math_random(JSContext *ctx, JSValueConst this_val, v = xorshift64star(&ctx->random_state); /* 1.0 <= u.d < 2 */ u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12); - return JS_NewFloat64Impl(ctx, u.d - 1.0); + return __JS_NewFloat64(ctx, u.d - 1.0); } // MSVC inexplicably refuses to initialize the array below with @@ -42089,47 +43478,12 @@ static const JSCFunctionListEntry js_math_obj[] = { /* Date */ -#if 0 -/* OS dependent: return the UTC time in ms since 1970. */ -static JSValue js___date_now(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int64_t d; - struct timeval tv; - gettimeofday(&tv, NULL); - d = (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); - return JS_NewInt64(ctx, d); -} -#endif - -/* OS dependent: return the UTC time in microseconds since 1970. */ -// FIXME: Unused, remove? -static JSValue js___date_clock(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) -{ - int64_t d; -#ifdef _MSC_VER - SYSTEMTIME st; - GetSystemTime(&st); - SystemTimeToFileTime(&st, (FILETIME *) &d); - d /= 10; -#else - struct timeval tv; - gettimeofday(&tv, NULL); - d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec; -#endif - return JS_NewInt64(ctx, d); -} - /* OS dependent. d = argv[0] is in ms from 1970. Return the difference between UTC time and local time 'd' in minutes */ -static int getTimezoneOffset(int64_t time) { -#if defined(_WIN32) - /* XXX: TODO */ - return 0; -#else +static int getTimezoneOffset(int64_t time) +{ time_t ti; - struct tm tm; + int res; time /= 1000; /* convert to seconds */ if (sizeof(time_t) == 4) { @@ -42153,9 +43507,27 @@ static int getTimezoneOffset(int64_t time) { } } ti = time; - localtime_r(&ti, &tm); - return -tm.tm_gmtoff / 60; +#if defined(_WIN32) + { + struct tm *tm; + time_t gm_ti, loc_ti; + + tm = gmtime(&ti); + gm_ti = mktime(tm); + + tm = localtime(&ti); + loc_ti = mktime(tm); + + res = (gm_ti - loc_ti) / 60; + } +#else + { + struct tm tm; + localtime_r(&ti, &tm); + res = -tm.tm_gmtoff / 60; + } #endif + return res; } #if 0 @@ -42167,7 +43539,7 @@ static JSValue js___date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val if (JS_ToFloat64(ctx, &dd, argv[0])) return JS_EXCEPTION; if (isnan(dd)) - return __JS_NewFloat64(ctx, dd); + return JS_NewFloat64(ctx, dd); else return JS_NewInt32(ctx, getTimezoneOffset((int64_t)dd)); } @@ -42232,6 +43604,9 @@ static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, /* XXX: re_flags = LRE_FLAG_OCTAL unless strict mode? */ for (i = 0; i < len; i++) { switch(str[i]) { + case 'd': + mask = LRE_FLAG_INDICES; + break; case 'g': mask = LRE_FLAG_GLOBAL; break; @@ -42245,7 +43620,7 @@ static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, mask = LRE_FLAG_DOTALL; break; case 'u': - mask = LRE_FLAG_UTF16; + mask = LRE_FLAG_UNICODE; break; case 'y': mask = LRE_FLAG_STICKY; @@ -42263,7 +43638,7 @@ static JSValue js_compile_regexp(JSContext *ctx, JSValueConst pattern, JS_FreeCString(ctx, str); } - str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UTF16)); + str = JS_ToCStringLen2(ctx, &len, pattern, !(re_flags & LRE_FLAG_UNICODE)); if (!str) return JS_EXCEPTION; re_bytecode_buf = lre_compile(&re_bytecode_len, error_msg, @@ -42564,7 +43939,7 @@ static JSValue js_regexp_get_flag(JSContext *ctx, JSValueConst this_val, int mas } flags = lre_get_flags(re->bytecode->u.str8); - return JS_NewBool(ctx, (flags & mask) != 0); + return JS_NewBool(ctx, flags & mask); } static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val) @@ -42575,6 +43950,11 @@ static JSValue js_regexp_get_flags(JSContext *ctx, JSValueConst this_val) if (JS_VALUE_GET_TAG(this_val) != JS_TAG_OBJECT) return JS_ThrowTypeErrorNotAnObject(ctx); + res = JS_ToBoolFree(ctx, JS_GetPropertyStr(ctx, this_val, "hasIndices")); + if (res < 0) + goto exception; + if (res) + *p++ = 'd'; res = JS_ToBoolFree(ctx, JS_GetProperty(ctx, this_val, JS_ATOM_global)); if (res < 0) goto exception; @@ -42654,25 +44034,32 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, { JSRegExp *re = js_get_regexp(ctx, this_val, TRUE); JSString *str; - JSValue str_val, obj, val, groups = JS_UNDEFINED; + JSValue t, ret, str_val, obj, val, groups; + JSValue indices, indices_groups; uint8_t *re_bytecode; - int ret; uint8_t **capture, *str_buf; - int capture_count, shift, i, re_flags; + int rc, capture_count, shift, i, re_flags; int64_t last_index; const char *group_name_ptr; if (!re) return JS_EXCEPTION; + str_val = JS_ToString(ctx, argv[0]); if (JS_IsException(str_val)) - return str_val; - val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex); - if (JS_IsException(val) || - JS_ToLengthFree(ctx, &last_index, val)) { - JS_FreeValue(ctx, str_val); return JS_EXCEPTION; - } + + ret = JS_EXCEPTION; + obj = JS_NULL; + groups = JS_UNDEFINED; + indices = JS_UNDEFINED; + indices_groups = JS_UNDEFINED; + capture = NULL; + + val = JS_GetProperty(ctx, this_val, JS_ATOM_lastIndex); + if (JS_IsException(val) || JS_ToLengthFree(ctx, &last_index, val)) + goto fail; + re_bytecode = re->bytecode->u.str8; re_flags = lre_get_flags(re_bytecode); if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) { @@ -42680,27 +44067,23 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, } str = JS_VALUE_GET_STRING(str_val); capture_count = lre_get_capture_count(re_bytecode); - capture = NULL; if (capture_count > 0) { capture = js_malloc(ctx, sizeof(capture[0]) * capture_count * 2); - if (!capture) { - JS_FreeValue(ctx, str_val); - return JS_EXCEPTION; - } + if (!capture) + goto fail; } shift = str->is_wide_char; str_buf = str->u.str8; if (last_index > str->len) { - ret = 2; + rc = 2; } else { - ret = lre_exec(capture, re_bytecode, - str_buf, last_index, str->len, - shift, ctx); + rc = lre_exec(capture, re_bytecode, + str_buf, last_index, str->len, + shift, ctx); } - obj = JS_NULL; - if (ret != 1) { - if (ret >= 0) { - if (ret == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) { + if (rc != 1) { + if (rc >= 0) { + if (rc == 2 || (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY))) { if (JS_SetProperty(ctx, this_val, JS_ATOM_lastIndex, JS_NewInt32(ctx, 0)) < 0) goto fail; @@ -42709,7 +44092,6 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, JS_ThrowInternalError(ctx, "out of memory in regexp execution"); goto fail; } - JS_FreeValue(ctx, str_val); } else { int prop_flags; if (re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) { @@ -42727,52 +44109,124 @@ static JSValue js_regexp_exec(JSContext *ctx, JSValueConst this_val, if (JS_IsException(groups)) goto fail; } + if (re_flags & LRE_FLAG_INDICES) { + indices = JS_NewArray(ctx); + if (JS_IsException(indices)) + goto fail; + if (group_name_ptr) { + indices_groups = JS_NewObjectProto(ctx, JS_NULL); + if (JS_IsException(indices_groups)) + goto fail; + } + } for(i = 0; i < capture_count; i++) { - int start, end; + const char *name = NULL; + uint8_t **match = &capture[2 * i]; + int start = -1; + int end = -1; JSValue val; - if (capture[2 * i] == NULL || - capture[2 * i + 1] == NULL) { + + if (group_name_ptr && i > 0) { + if (*group_name_ptr) name = group_name_ptr; + group_name_ptr += strlen(group_name_ptr) + 1; + } + + if (match[0] && match[1]) { + start = (match[0] - str_buf) >> shift; + end = (match[1] - str_buf) >> shift; + } + + if (!JS_IsUndefined(indices)) { val = JS_UNDEFINED; - } else { - start = (capture[2 * i] - str_buf) >> shift; - end = (capture[2 * i + 1] - str_buf) >> shift; + if (start != -1) { + val = JS_NewArray(ctx); + if (JS_IsException(val)) + goto fail; + if (JS_DefinePropertyValueUint32(ctx, val, 0, + JS_NewInt32(ctx, start), + prop_flags) < 0) { + JS_FreeValue(ctx, val); + goto fail; + } + if (JS_DefinePropertyValueUint32(ctx, val, 1, + JS_NewInt32(ctx, end), + prop_flags) < 0) { + JS_FreeValue(ctx, val); + goto fail; + } + } + if (name && !JS_IsUndefined(indices_groups)) { + val = JS_DupValue(ctx, val); + if (JS_DefinePropertyValueStr(ctx, indices_groups, + name, val, prop_flags) < 0) { + JS_FreeValue(ctx, val); + goto fail; + } + } + if (JS_DefinePropertyValueUint32(ctx, indices, i, val, + prop_flags) < 0) { + goto fail; + } + } + + val = JS_UNDEFINED; + if (start != -1) { val = js_sub_string(ctx, str, start, end); if (JS_IsException(val)) goto fail; } - if (group_name_ptr && i > 0) { - if (*group_name_ptr) { - if (JS_DefinePropertyValueStr(ctx, groups, group_name_ptr, - JS_DupValue(ctx, val), - prop_flags) < 0) { - JS_FreeValue(ctx, val); - goto fail; - } + + if (name) { + if (JS_DefinePropertyValueStr(ctx, groups, name, + JS_DupValue(ctx, val), + prop_flags) < 0) { + JS_FreeValue(ctx, val); + goto fail; } - group_name_ptr += strlen(group_name_ptr) + 1; } + if (JS_DefinePropertyValueUint32(ctx, obj, i, val, prop_flags) < 0) goto fail; } + + t = groups, groups = JS_UNDEFINED; if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_groups, - groups, prop_flags) < 0) + t, prop_flags) < 0) { goto fail; - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index, - JS_NewInt32(ctx, (capture[0] - str_buf) >> shift), prop_flags) < 0) + } + + t = JS_NewInt32(ctx, (capture[0] - str_buf) >> shift); + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_index, t, prop_flags) < 0) goto fail; - if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, str_val, prop_flags) < 0) - goto fail1; + + t = str_val, str_val = JS_UNDEFINED; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_input, t, prop_flags) < 0) + goto fail; + + if (!JS_IsUndefined(indices)) { + t = indices_groups, indices_groups = JS_UNDEFINED; + if (JS_DefinePropertyValue(ctx, indices, JS_ATOM_groups, + t, prop_flags) < 0) { + goto fail; + } + t = indices, indices = JS_UNDEFINED; + if (JS_DefinePropertyValue(ctx, obj, JS_ATOM_indices, + t, prop_flags) < 0) { + goto fail; + } + } } - js_free(ctx, capture); - return obj; + ret = obj; + obj = JS_UNDEFINED; fail: - JS_FreeValue(ctx, groups); + JS_FreeValue(ctx, indices_groups); + JS_FreeValue(ctx, indices); JS_FreeValue(ctx, str_val); -fail1: + JS_FreeValue(ctx, groups); JS_FreeValue(ctx, obj); js_free(ctx, capture); - return JS_EXCEPTION; + return ret; } /* delete portions of a string that match a given regex */ @@ -42851,7 +44305,7 @@ static JSValue JS_RegExpDelete(JSContext *ctx, JSValueConst this_val, JSValueCon break; } if (end == start) { - if (!(re_flags & LRE_FLAG_UTF16) || (unsigned)end >= str->len || !str->is_wide_char) { + if (!(re_flags & LRE_FLAG_UNICODE) || (unsigned)end >= str->len || !str->is_wide_char) { end++; } else { string_getc(str, &end); @@ -42924,7 +44378,7 @@ static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val, { // [Symbol.match](str) JSValueConst rx = this_val; - JSValue A, S, result, matchStr; + JSValue A, S, flags, result, matchStr; int global, n, fullUnicode, isEmpty; JSString *p; @@ -42932,16 +44386,23 @@ static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val, return JS_ThrowTypeErrorNotAnObject(ctx); A = JS_UNDEFINED; + flags = JS_UNDEFINED; result = JS_UNDEFINED; matchStr = JS_UNDEFINED; S = JS_ToString(ctx, argv[0]); if (JS_IsException(S)) goto exception; - global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global)); - if (global < 0) + flags = JS_GetProperty(ctx, rx, JS_ATOM_flags); + if (JS_IsException(flags)) + goto exception; + flags = JS_ToStringFree(ctx, flags); + if (JS_IsException(flags)) goto exception; + p = JS_VALUE_GET_STRING(flags); + // TODO(bnoordhuis) query 'u' flag the same way? + global = (-1 != string_indexof_char(p, 'g', 0)); if (!global) { A = JS_RegExpExec(ctx, rx, S); } else { @@ -42985,12 +44446,14 @@ static JSValue js_regexp_Symbol_match(JSContext *ctx, JSValueConst this_val, } } JS_FreeValue(ctx, result); + JS_FreeValue(ctx, flags); JS_FreeValue(ctx, S); return A; exception: JS_FreeValue(ctx, A); JS_FreeValue(ctx, result); + JS_FreeValue(ctx, flags); JS_FreeValue(ctx, S); return JS_EXCEPTION; } @@ -43233,8 +44696,8 @@ static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, // [Symbol.replace](str, rep) JSValueConst rx = this_val, rep = argv[1]; JSValueConst args[6]; - JSValue str, rep_val, matched, tab, rep_str, namedCaptures, res; - JSString *sp, *rp; + JSValue flags, str, rep_val, matched, tab, rep_str, namedCaptures, res; + JSString *p, *sp, *rp; StringBuffer b_s, *b = &b_s; ValueBuffer v_b, *results = &v_b; int nextSourcePosition, n, j, functionalReplace, is_global, fullUnicode; @@ -43250,6 +44713,7 @@ static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, rep_val = JS_UNDEFINED; matched = JS_UNDEFINED; tab = JS_UNDEFINED; + flags = JS_UNDEFINED; rep_str = JS_UNDEFINED; namedCaptures = JS_UNDEFINED; @@ -43266,10 +44730,18 @@ static JSValue js_regexp_Symbol_replace(JSContext *ctx, JSValueConst this_val, goto exception; rp = JS_VALUE_GET_STRING(rep_val); } - fullUnicode = 0; - is_global = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_global)); - if (is_global < 0) + + flags = JS_GetProperty(ctx, rx, JS_ATOM_flags); + if (JS_IsException(flags)) goto exception; + flags = JS_ToStringFree(ctx, flags); + if (JS_IsException(flags)) + goto exception; + p = JS_VALUE_GET_STRING(flags); + + // TODO(bnoordhuis) query 'u' flag the same way? + fullUnicode = 0; + is_global = (-1 != string_indexof_char(p, 'g', 0)); if (is_global) { fullUnicode = JS_ToBoolFree(ctx, JS_GetProperty(ctx, rx, JS_ATOM_unicode)); if (fullUnicode < 0) @@ -43403,6 +44875,7 @@ done1: value_buffer_free(results); JS_FreeValue(ctx, rep_val); JS_FreeValue(ctx, matched); + JS_FreeValue(ctx, flags); JS_FreeValue(ctx, tab); JS_FreeValue(ctx, rep_str); JS_FreeValue(ctx, namedCaptures); @@ -43603,12 +45076,13 @@ static const JSCFunctionListEntry js_regexp_funcs[] = { static const JSCFunctionListEntry js_regexp_proto_funcs[] = { JS_CGETSET_DEF("flags", js_regexp_get_flags, NULL ), JS_CGETSET_DEF("source", js_regexp_get_source, NULL ), - JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, 1 ), - JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, 2 ), - JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, 4 ), - JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, 8 ), - JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, 16 ), - JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, 32 ), + JS_CGETSET_MAGIC_DEF("global", js_regexp_get_flag, NULL, LRE_FLAG_GLOBAL ), + JS_CGETSET_MAGIC_DEF("ignoreCase", js_regexp_get_flag, NULL, LRE_FLAG_IGNORECASE ), + JS_CGETSET_MAGIC_DEF("multiline", js_regexp_get_flag, NULL, LRE_FLAG_MULTILINE ), + JS_CGETSET_MAGIC_DEF("dotAll", js_regexp_get_flag, NULL, LRE_FLAG_DOTALL ), + JS_CGETSET_MAGIC_DEF("unicode", js_regexp_get_flag, NULL, LRE_FLAG_UNICODE ), + JS_CGETSET_MAGIC_DEF("sticky", js_regexp_get_flag, NULL, LRE_FLAG_STICKY ), + JS_CGETSET_MAGIC_DEF("hasIndices", js_regexp_get_flag, NULL, LRE_FLAG_INDICES ), JS_CFUNC_DEF("exec", 1, js_regexp_exec ), JS_CFUNC_DEF("compile", 2, js_regexp_compile ), JS_CFUNC_DEF("test", 1, js_regexp_test ), @@ -43766,7 +45240,7 @@ static JSValue json_parse_value(JSParseState *s) case TOK_IDENT: if (s->token.u.ident.atom == JS_ATOM_false || s->token.u.ident.atom == JS_ATOM_true) { - val = JS_NewBool(ctx, (s->token.u.ident.atom == JS_ATOM_true)); + val = JS_NewBool(ctx, s->token.u.ident.atom == JS_ATOM_true); } else if (s->token.u.ident.atom == JS_ATOM_null) { val = JS_NULL; } else { @@ -43778,7 +45252,7 @@ static JSValue json_parse_value(JSParseState *s) default: def_token: if (s->token.val == TOK_EOF) { - js_parse_error(s, "unexpected end of input"); + js_parse_error(s, "Unexpected end of JSON input"); } else { js_parse_error(s, "unexpected token: '%.*s'", (int)(s->buf_ptr - s->token.ptr), s->token.ptr); @@ -43945,24 +45419,27 @@ static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, JSValue v; JSValueConst args[2]; - if (JS_IsObject(val) + /* check for object.toJSON method */ + /* ECMA specifies this is done only for Object and BigInt */ + /* we do it for BigFloat and BigDecimal as an extension */ + if (JS_IsObject(val) || JS_IsBigInt(ctx, val) #ifdef CONFIG_BIGNUM - || JS_IsBigInt(ctx, val) /* XXX: probably useless */ + || JS_IsBigFloat(val) || JS_IsBigDecimal(val) #endif ) { - JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON); - if (JS_IsException(f)) + JSValue f = JS_GetProperty(ctx, val, JS_ATOM_toJSON); + if (JS_IsException(f)) + goto exception; + if (JS_IsFunction(ctx, f)) { + v = JS_CallFree(ctx, f, val, 1, &key); + JS_FreeValue(ctx, val); + val = v; + if (JS_IsException(val)) goto exception; - if (JS_IsFunction(ctx, f)) { - v = JS_CallFree(ctx, f, val, 1, &key); - JS_FreeValue(ctx, val); - val = v; - if (JS_IsException(val)) - goto exception; - } else { - JS_FreeValue(ctx, f); - } + } else { + JS_FreeValue(ctx, f); } + } if (!JS_IsUndefined(jsc->replacer_func)) { args[0] = key; @@ -43981,13 +45458,12 @@ static JSValue js_json_check(JSContext *ctx, JSONStringifyContext *jsc, case JS_TAG_STRING: case JS_TAG_INT: case JS_TAG_FLOAT64: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: -#endif case JS_TAG_BOOL: case JS_TAG_NULL: -#ifdef CONFIG_BIGNUM case JS_TAG_BIG_INT: +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_FLOAT: + case JS_TAG_BIG_DECIMAL: #endif case JS_TAG_EXCEPTION: return val; @@ -44018,37 +45494,31 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, tab = JS_UNDEFINED; prop = JS_UNDEFINED; - switch (JS_VALUE_GET_NORM_TAG(val)) { - case JS_TAG_OBJECT: + if (JS_IsObject(val)) { p = JS_VALUE_GET_OBJ(val); cl = p->class_id; if (cl == JS_CLASS_STRING) { val = JS_ToStringFree(ctx, val); if (JS_IsException(val)) goto exception; - val = JS_ToQuotedStringFree(ctx, val); - if (JS_IsException(val)) - goto exception; - return string_buffer_concat_value_free(jsc->b, val); + goto concat_primitive; } else if (cl == JS_CLASS_NUMBER) { val = JS_ToNumberFree(ctx, val); if (JS_IsException(val)) goto exception; - return string_buffer_concat_value_free(jsc->b, val); - } else if (cl == JS_CLASS_BOOLEAN) { - ret = string_buffer_concat_value(jsc->b, p->u.object_data); - JS_FreeValue(ctx, val); - return ret; - } + goto concat_primitive; + } else if (cl == JS_CLASS_BOOLEAN || cl == JS_CLASS_BIG_INT #ifdef CONFIG_BIGNUM - else if (cl == JS_CLASS_BIG_FLOAT) { - return string_buffer_concat_value_free(jsc->b, val); - } else if (cl == JS_CLASS_BIG_INT) { - JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify"); - goto exception; - } + || cl == JS_CLASS_BIG_FLOAT + || cl == JS_CLASS_BIG_DECIMAL #endif - v = js_array_includes(ctx, jsc->stack, 1, &val); + ) + { + /* This will thow the same error as for the primitive object */ + set_value(ctx, &val, JS_DupValue(ctx, p->u.object_data)); + goto concat_primitive; + } + v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val); if (JS_IsException(v)) goto exception; if (JS_ToBoolFree(ctx, v)) { @@ -44069,7 +45539,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, sep = JS_DupValue(ctx, jsc->empty); sep1 = JS_DupValue(ctx, jsc->empty); } - v = js_array_push(ctx, jsc->stack, 1, &val, 0); + v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0); if (check_exception_free(ctx, v)) goto exception; ret = JS_IsArray(ctx, val); @@ -44109,7 +45579,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, if (!JS_IsUndefined(jsc->property_list)) tab = JS_DupValue(ctx, jsc->property_list); else - tab = js_object_keys(ctx, JS_UNDEFINED, 1, &val, JS_ITERATOR_KIND_KEY); + tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY); if (JS_IsException(tab)) goto exception; if (js_get_length64(ctx, &len, tab)) @@ -44144,7 +45614,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, has_content = TRUE; } } - if (has_content && JS_VALUE_GET_STRING(jsc->gap)->len != 0) { + if (has_content && !JS_IsEmptyString(jsc->gap)) { string_buffer_putc8(jsc->b, '\n'); string_buffer_concat_value(jsc->b, indent); } @@ -44159,6 +45629,9 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, JS_FreeValue(ctx, indent1); JS_FreeValue(ctx, prop); return 0; + } + concat_primitive: + switch (JS_VALUE_GET_NORM_TAG(val)) { case JS_TAG_STRING: val = JS_ToQuotedStringFree(ctx, val); if (JS_IsException(val)) @@ -44170,18 +45643,18 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, } goto concat_value; case JS_TAG_INT: -#ifdef CONFIG_BIGNUM - case JS_TAG_BIG_FLOAT: -#endif case JS_TAG_BOOL: case JS_TAG_NULL: concat_value: return string_buffer_concat_value_free(jsc->b, val); -#ifdef CONFIG_BIGNUM case JS_TAG_BIG_INT: - JS_ThrowTypeError(ctx, "bigint are forbidden in JSON.stringify"); - goto exception; +#ifdef CONFIG_BIGNUM + case JS_TAG_BIG_FLOAT: + case JS_TAG_BIG_DECIMAL: #endif + /* reject big numbers: use toJSON method to override */ + JS_ThrowTypeError(ctx, "Do not know how to serialize a BigInt"); + goto exception; default: JS_FreeValue(ctx, val); return 0; @@ -44257,7 +45730,7 @@ JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, continue; } present = js_array_includes(ctx, jsc->property_list, - 1, &v); + 1, (JSValueConst *)&v); if (JS_IsException(present)) { JS_FreeValue(ctx, v); goto exception; @@ -44381,7 +45854,7 @@ static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val, tab = build_arg_list(ctx, &len, array_arg); if (!tab) return JS_EXCEPTION; - ret = JS_CallConstructor2(ctx, func, new_target, len, tab); + ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab); free_arg_list(ctx, tab, len); return ret; } @@ -44471,8 +45944,8 @@ static JSValue js_reflect_set(JSContext *ctx, JSValueConst this_val, atom = JS_ValueToAtom(ctx, prop); if (unlikely(atom == JS_ATOM_NULL)) return JS_EXCEPTION; - ret = JS_SetPropertyGeneric(ctx, obj, atom, - JS_DupValue(ctx, val), receiver, 0); + ret = JS_SetPropertyInternal(ctx, obj, atom, + JS_DupValue(ctx, val), receiver, 0); JS_FreeAtom(ctx, atom); if (ret < 0) return JS_EXCEPTION; @@ -44586,7 +46059,7 @@ static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj) return JS_EXCEPTION; if (JS_IsUndefined(method)) return JS_GetPrototype(ctx, s->target); - ret = JS_CallFree(ctx, method, s->handler, 1, &s->target); + ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); if (JS_IsException(ret)) return ret; if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL && @@ -44673,7 +46146,7 @@ static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj) return -1; if (JS_IsUndefined(method)) return JS_IsExtensible(ctx, s->target); - ret = JS_CallFree(ctx, method, s->handler, 1, &s->target); + ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); if (JS_IsException(ret)) return -1; res = JS_ToBoolFree(ctx, ret); @@ -44699,7 +46172,7 @@ static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj) return -1; if (JS_IsUndefined(method)) return JS_PreventExtensions(ctx, s->target); - ret = JS_CallFree(ctx, method, s->handler, 1, &s->target); + ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); if (JS_IsException(ret)) return -1; res = JS_ToBoolFree(ctx, ret); @@ -44819,9 +46292,9 @@ static int js_proxy_set(JSContext *ctx, JSValueConst obj, JSAtom atom, if (!s) return -1; if (JS_IsUndefined(method)) { - return JS_SetPropertyGeneric(ctx, s->target, atom, - JS_DupValue(ctx, value), receiver, - flags); + return JS_SetPropertyInternal(ctx, s->target, atom, + JS_DupValue(ctx, value), receiver, + flags); } atom_val = JS_AtomToValue(ctx, atom); if (JS_IsException(atom_val)) { @@ -44887,17 +46360,17 @@ static JSValue js_create_desc(JSContext *ctx, JSValueConst val, } if (flags & JS_PROP_HAS_WRITABLE) { JS_DefinePropertyValue(ctx, ret, JS_ATOM_writable, - JS_NewBool(ctx, (flags & JS_PROP_WRITABLE) != 0), + JS_NewBool(ctx, flags & JS_PROP_WRITABLE), JS_PROP_C_W_E); } if (flags & JS_PROP_HAS_ENUMERABLE) { JS_DefinePropertyValue(ctx, ret, JS_ATOM_enumerable, - JS_NewBool(ctx, (flags & JS_PROP_ENUMERABLE) != 0), + JS_NewBool(ctx, flags & JS_PROP_ENUMERABLE), JS_PROP_C_W_E); } if (flags & JS_PROP_HAS_CONFIGURABLE) { JS_DefinePropertyValue(ctx, ret, JS_ATOM_configurable, - JS_NewBool(ctx, (flags & JS_PROP_CONFIGURABLE) != 0), + JS_NewBool(ctx, flags & JS_PROP_CONFIGURABLE), JS_PROP_C_W_E); } return ret; @@ -45183,7 +46656,7 @@ static int js_proxy_get_own_property_names(JSContext *ctx, JS_VALUE_GET_OBJ(s->target), JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK); } - prop_array = JS_CallFree(ctx, method, s->handler, 1, &s->target); + prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); if (JS_IsException(prop_array)) return -1; tab = NULL; @@ -45349,20 +46822,35 @@ static JSValue js_proxy_call(JSContext *ctx, JSValueConst func_obj, return ret; } -static int js_proxy_isArray(JSContext *ctx, JSValueConst obj) -{ - JSProxyData *s = JS_GetOpaque(obj, JS_CLASS_PROXY); - if (!s) - return FALSE; - if (js_check_stack_overflow(ctx->rt, 0)) { - JS_ThrowStackOverflow(ctx); - return -1; - } - if (s->is_revoked) { - JS_ThrowTypeErrorRevokedProxy(ctx); - return -1; +/* `js_resolve_proxy`: resolve the proxy chain + `*pval` is updated with to ultimate proxy target + `throw_exception` controls whether exceptions are thown or not + - return -1 in case of error + - otherwise return 0 + */ +static int js_resolve_proxy(JSContext *ctx, JSValueConst *pval, BOOL throw_exception) { + int depth = 0; + JSObject *p; + JSProxyData *s; + + while (JS_VALUE_GET_TAG(*pval) == JS_TAG_OBJECT) { + p = JS_VALUE_GET_OBJ(*pval); + if (p->class_id != JS_CLASS_PROXY) + break; + if (depth++ > 1000) { + if (throw_exception) + JS_ThrowStackOverflow(ctx); + return -1; + } + s = p->u.opaque; + if (s->is_revoked) { + if (throw_exception) + JS_ThrowTypeErrorRevokedProxy(ctx); + return -1; + } + *pval = s->target; } - return JS_IsArray(ctx, s->target); + return 0; } static const JSClassExoticMethods js_proxy_exotic_methods = { @@ -45523,7 +47011,7 @@ static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val, if (JS_IsException(val)) return val; /* XXX: use JS_ToStringInternal() with a flags */ - ret = js_string_constructor(ctx, JS_UNDEFINED, 1, &val); + ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val); JS_FreeValue(ctx, val); return ret; } @@ -45673,7 +47161,7 @@ static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target, break; } if (is_set) { - ret = JS_Call(ctx, adder, obj, 1, &item); + ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item); if (JS_IsException(ret)) { JS_FreeValue(ctx, item); goto fail; @@ -45757,7 +47245,7 @@ static uint32_t map_hash_key(JSContext *ctx, JSValueConst key) h = (uintptr_t)JS_VALUE_GET_PTR(key) * 3163; break; case JS_TAG_INT: - d = JS_VALUE_GET_INT(key) * 3163; + d = JS_VALUE_GET_INT(key); goto hash_float64; case JS_TAG_FLOAT64: d = JS_VALUE_GET_FLOAT64(key); @@ -45767,7 +47255,7 @@ static uint32_t map_hash_key(JSContext *ctx, JSValueConst key) hash_float64: u.d = d; h = (u.u32[0] ^ u.u32[1]) * 3163; - break; + return h ^= JS_TAG_FLOAT64; default: h = 0; /* XXX: bignum support */ break; @@ -45991,7 +47479,7 @@ static JSValue js_map_has(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; key = map_normalize_key(ctx, argv[0]); mr = map_find_record(ctx, s, key); - return JS_NewBool(ctx, (mr != NULL)); + return JS_NewBool(ctx, mr != NULL); } static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val, @@ -46083,6 +47571,123 @@ static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val, return JS_UNDEFINED; } +static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv, int is_map) +{ + JSValueConst cb, args[2]; + JSValue res, iter, next, groups, key, v, prop; + JSAtom key_atom = JS_ATOM_NULL; + int64_t idx; + BOOL done; + + // "is function?" check must be observed before argv[0] is accessed + cb = argv[1]; + if (check_function(ctx, cb)) + return JS_EXCEPTION; + + iter = JS_GetIterator(ctx, argv[0], /*is_async*/FALSE); + if (JS_IsException(iter)) + return JS_EXCEPTION; + + key = JS_UNDEFINED; + key_atom = JS_ATOM_NULL; + v = JS_UNDEFINED; + prop = JS_UNDEFINED; + groups = JS_UNDEFINED; + + next = JS_GetProperty(ctx, iter, JS_ATOM_next); + if (JS_IsException(next)) + goto exception; + + if (is_map) { + groups = js_map_constructor(ctx, JS_UNDEFINED, 0, NULL, 0); + } else { + groups = JS_NewObjectProto(ctx, JS_NULL); + } + if (JS_IsException(groups)) + goto exception; + + for (idx = 0; ; idx++) { + if (idx >= MAX_SAFE_INTEGER) { + JS_ThrowTypeError(ctx, "too many elements"); + goto iterator_close_exception; + } + v = JS_IteratorNext(ctx, iter, next, 0, NULL, &done); + if (JS_IsException(v)) + goto exception; + if (done) + break; // v is JS_UNDEFINED + + args[0] = v; + args[1] = JS_NewInt64(ctx, idx); + key = JS_Call(ctx, cb, ctx->global_obj, 2, args); + if (JS_IsException(key)) + goto iterator_close_exception; + + if (is_map) { + prop = js_map_get(ctx, groups, 1, (JSValueConst *)&key, 0); + } else { + key_atom = JS_ValueToAtom(ctx, key); + JS_FreeValue(ctx, key); + key = JS_UNDEFINED; + if (key_atom == JS_ATOM_NULL) + goto iterator_close_exception; + prop = JS_GetProperty(ctx, groups, key_atom); + } + if (JS_IsException(prop)) + goto exception; + + if (JS_IsUndefined(prop)) { + prop = JS_NewArray(ctx); + if (JS_IsException(prop)) + goto exception; + if (is_map) { + args[0] = key; + args[1] = prop; + res = js_map_set(ctx, groups, 2, args, 0); + if (JS_IsException(res)) + goto exception; + JS_FreeValue(ctx, res); + } else { + prop = JS_DupValue(ctx, prop); + if (JS_DefinePropertyValue(ctx, groups, key_atom, prop, + JS_PROP_C_W_E) < 0) { + goto exception; + } + } + } + res = js_array_push(ctx, prop, 1, (JSValueConst *)&v, /*unshift*/0); + if (JS_IsException(res)) + goto exception; + // res is an int64 + + JS_FreeValue(ctx, prop); + JS_FreeValue(ctx, key); + JS_FreeAtom(ctx, key_atom); + JS_FreeValue(ctx, v); + prop = JS_UNDEFINED; + key = JS_UNDEFINED; + key_atom = JS_ATOM_NULL; + v = JS_UNDEFINED; + } + + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, next); + return groups; + + iterator_close_exception: + JS_IteratorClose(ctx, iter, TRUE); + exception: + JS_FreeAtom(ctx, key_atom); + JS_FreeValue(ctx, prop); + JS_FreeValue(ctx, key); + JS_FreeValue(ctx, v); + JS_FreeValue(ctx, groups); + JS_FreeValue(ctx, iter); + JS_FreeValue(ctx, next); + return JS_EXCEPTION; +} + static void js_map_finalizer(JSRuntime *rt, JSValue val) { JSObject *p; @@ -46263,6 +47868,7 @@ static JSValue js_map_iterator_next(JSContext *ctx, JSValueConst this_val, } static const JSCFunctionListEntry js_map_funcs[] = { + JS_CFUNC_MAGIC_DEF("groupBy", 2, js_object_groupBy, 1 ), JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL ), }; @@ -46383,12 +47989,6 @@ static const JSCFunctionListEntry js_generator_proto_funcs[] = { /* Promise */ -typedef enum JSPromiseStateEnum { - JS_PROMISE_PENDING, - JS_PROMISE_FULFILLED, - JS_PROMISE_REJECTED, -} JSPromiseStateEnum; - typedef struct JSPromiseData { JSPromiseStateEnum promise_state; /* 0=fulfill, 1=reject, list of JSPromiseReactionData.link */ @@ -46413,6 +48013,22 @@ typedef struct JSPromiseReactionData { JSValue handler; } JSPromiseReactionData; +JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise) +{ + JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); + if (!s) + return -1; + return s->promise_state; +} + +JSValue JS_PromiseResult(JSContext *ctx, JSValue promise) +{ + JSPromiseData *s = JS_GetOpaque(promise, JS_CLASS_PROMISE); + if (!s) + return JS_UNDEFINED; + return JS_DupValue(ctx, s->promise_result); +} + static int js_create_resolving_functions(JSContext *ctx, JSValue *args, JSValueConst promise); @@ -46458,7 +48074,7 @@ static JSValue promise_reaction_job(JSContext *ctx, int argc, functions */ if (!JS_IsUndefined(func)) { res2 = JS_Call(ctx, func, JS_UNDEFINED, - 1, &res); + 1, (JSValueConst *)&res); } else { res2 = JS_UNDEFINED; } @@ -46541,7 +48157,7 @@ static JSValue js_promise_resolve_thenable_job(JSContext *ctx, res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args); if (JS_IsException(res)) { JSValue error = JS_GetException(ctx); - res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, &error); + res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); JS_FreeValue(ctx, error); } JS_FreeValue(ctx, args[0]); @@ -46741,7 +48357,7 @@ static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target, if (JS_IsException(ret)) { JSValue ret2, error; error = JS_GetException(ctx); - ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, &error); + ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); JS_FreeValue(ctx, error); if (JS_IsException(ret2)) goto fail1; @@ -46798,10 +48414,10 @@ static JSValue js_new_promise_capability(JSContext *ctx, if (JS_IsUndefined(ctor)) { result_promise = js_promise_constructor(ctx, ctor, 1, - &executor); + (JSValueConst *)&executor); } else { result_promise = JS_CallConstructor(ctx, ctor, 1, - &executor); + (JSValueConst *)&executor); } if (JS_IsException(result_promise)) goto fail; @@ -46858,17 +48474,14 @@ static JSValue js_promise_resolve(JSContext *ctx, JSValueConst this_val, return result_promise; } -#if 0 -static JSValue js_promise___newPromiseCapability(JSContext *ctx, - JSValueConst this_val, - int argc, JSValueConst *argv) +static JSValue js_promise_withResolvers(JSContext *ctx, + JSValueConst this_val, + int argc, JSValueConst *argv) { JSValue result_promise, resolving_funcs[2], obj; - JSValueConst ctor; - ctor = argv[0]; - if (!JS_IsObject(ctor)) + if (!JS_IsObject(this_val)) return JS_ThrowTypeErrorNotAnObject(ctx); - result_promise = js_new_promise_capability(ctx, resolving_funcs, ctor); + result_promise = js_new_promise_capability(ctx, resolving_funcs, this_val); if (JS_IsException(result_promise)) return result_promise; obj = JS_NewObject(ctx); @@ -46883,9 +48496,8 @@ static JSValue js_promise___newPromiseCapability(JSContext *ctx, JS_DefinePropertyValue(ctx, obj, JS_ATOM_reject, resolving_funcs[1], JS_PROP_C_W_E); return obj; } -#endif -static warn_unused int remainingElementsCount_add(JSContext *ctx, +static __exception int remainingElementsCount_add(JSContext *ctx, JSValueConst resolve_element_env, int addend) { @@ -46966,10 +48578,10 @@ static JSValue js_promise_all_resolve_element(JSContext *ctx, error = js_aggregate_error_constructor(ctx, values); if (JS_IsException(error)) return JS_EXCEPTION; - ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, &error); + ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error); JS_FreeValue(ctx, error); } else { - ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, &values); + ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values); } if (JS_IsException(ret)) return ret; @@ -47005,7 +48617,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, fail_reject: error = JS_GetException(ctx); ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, - &error); + (JSValueConst *)&error); JS_FreeValue(ctx, error); if (JS_IsException(ret)) goto fail; @@ -47036,7 +48648,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, if (done) break; next_promise = JS_Call(ctx, promise_resolve, - this_val, 1, &item); + this_val, 1, (JSValueConst *)&item); JS_FreeValue(ctx, item); if (JS_IsException(next_promise)) { fail_reject1: @@ -47104,7 +48716,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, values = error; } ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED, - 1, &values); + 1, (JSValueConst *)&values); if (check_exception_free(ctx, ret)) goto fail_reject; } @@ -47147,7 +48759,7 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, fail_reject: error = JS_GetException(ctx); ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, - &error); + (JSValueConst *)&error); JS_FreeValue(ctx, error); if (JS_IsException(ret)) goto fail; @@ -47166,7 +48778,7 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, if (done) break; next_promise = JS_Call(ctx, promise_resolve, - this_val, 1, &item); + this_val, 1, (JSValueConst *)&item); JS_FreeValue(ctx, item); if (JS_IsException(next_promise)) { fail_reject1: @@ -47193,7 +48805,7 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, goto done; } -static warn_unused int perform_promise_then(JSContext *ctx, +static __exception int perform_promise_then(JSContext *ctx, JSValueConst promise, JSValueConst *resolve_reject, JSValueConst *cap_resolving_funcs) @@ -47311,7 +48923,7 @@ static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_va res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL); if (JS_IsException(res)) return res; - promise = js_promise_resolve(ctx, ctor, 1, &res, 0); + promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0); JS_FreeValue(ctx, res); if (JS_IsException(promise)) return promise; @@ -47326,7 +48938,7 @@ static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_va JS_FreeValue(ctx, promise); return then_func; } - ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, &then_func); + ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func); JS_FreeValue(ctx, then_func); return ret; } @@ -47373,7 +48985,7 @@ static const JSCFunctionListEntry js_promise_funcs[] = { JS_CFUNC_MAGIC_DEF("allSettled", 1, js_promise_all, PROMISE_MAGIC_allSettled ), JS_CFUNC_MAGIC_DEF("any", 1, js_promise_all, PROMISE_MAGIC_any ), JS_CFUNC_DEF("race", 1, js_promise_race ), - //JS_CFUNC_DEF("__newPromiseCapability", 1, js_promise___newPromiseCapability ), + JS_CFUNC_DEF("withResolvers", 0, js_promise_withResolvers ), JS_CGETSET_DEF("[Symbol.species]", js_get_this, NULL), }; @@ -47526,7 +49138,7 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst thi is_reject = 1; done_resolve: res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, - 1, &err); + 1, (JSValueConst *)&err); JS_FreeValue(ctx, err); JS_FreeValue(ctx, res2); JS_FreeValue(ctx, resolving_funcs[0]); @@ -47538,7 +49150,7 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst thi int res; value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor, - 1, &value, 0); + 1, (JSValueConst *)&value, 0); if (JS_IsException(value_wrapper_promise)) { JS_FreeValue(ctx, value); goto reject; @@ -47786,8 +49398,7 @@ static JSValue js_global_decodeURI(JSContext *ctx, JSValueConst this_val, } c = (c << 6) | (c1 & 0x3f); } - if (c < c_min || c > 0x10FFFF || - (c >= 0xd800 && c < 0xe000)) { + if (c < c_min || c > 0x10FFFF || is_surrogate(c)) { js_throw_URIError(ctx, "malformed UTF-8"); goto fail; } @@ -47862,21 +49473,21 @@ static JSValue js_global_encodeURI(JSContext *ctx, JSValueConst this_val, if (isURIUnescaped(c, isComponent)) { string_buffer_putc16(b, c); } else { - if (c >= 0xdc00 && c <= 0xdfff) { + if (is_lo_surrogate(c)) { js_throw_URIError(ctx, "invalid character"); goto fail; - } else if (c >= 0xd800 && c <= 0xdbff) { + } else if (is_hi_surrogate(c)) { if (k >= p->len) { js_throw_URIError(ctx, "expecting surrogate pair"); goto fail; } c1 = string_get(p, k); k++; - if (c1 < 0xdc00 || c1 > 0xdfff) { + if (!is_lo_surrogate(c1)) { js_throw_URIError(ctx, "expecting surrogate pair"); goto fail; } - c = (((c & 0x3ff) << 10) | (c1 & 0x3ff)) + 0x10000; + c = from_surrogate(c, c1); } if (c < 0x80) { encodeURI_hex(b, c); @@ -47984,12 +49595,7 @@ static const JSCFunctionListEntry js_global_funcs[] = { JS_PROP_DOUBLE_DEF("Infinity", 1.0 / 0.0, 0 ), JS_PROP_DOUBLE_DEF("NaN", NAN, 0 ), JS_PROP_UNDEFINED_DEF("undefined", 0 ), - - /* for the 'Date' implementation */ - JS_CFUNC_DEF("__date_clock", 0, js___date_clock ), - //JS_CFUNC_DEF("__date_now", 0, js___date_now ), - //JS_CFUNC_DEF("__date_getTimezoneOffset", 1, js___date_getTimezoneOffset ), - //JS_CFUNC_DEF("__date_create", 3, js___date_create ), + JS_PROP_STRING_DEF("[Symbol.toStringTag]", "global", JS_PROP_CONFIGURABLE ), }; /* Date */ @@ -48009,7 +49615,7 @@ static int64_t floor_div(int64_t a, int64_t b) { static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); -static warn_unused int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val) +static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val) { if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { JSObject *p = JS_VALUE_GET_OBJ(this_val); @@ -48069,8 +49675,8 @@ static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; static char const day_names[] = "SunMonTueWedThuFriSat"; -static warn_unused int get_date_fields(JSContext *ctx, JSValueConst obj, - double fields[9], int is_local, int force) +static __exception int get_date_fields(JSContext *ctx, JSValueConst obj, + double fields[minimum_length(9)], int is_local, int force) { double dval; int64_t d, days, wd, y, i, md, h, m, s, ms, tz = 0; @@ -48083,7 +49689,7 @@ static warn_unused int get_date_fields(JSContext *ctx, JSValueConst obj, return FALSE; /* NaN */ d = 0; /* initialize all fields to 0 */ } else { - d = dval; + d = dval; /* assuming -8.64e15 <= dval <= -8.64e15 */ if (is_local) { tz = -getTimezoneOffset(d); d += tz * 60000; @@ -48129,33 +49735,63 @@ static double time_clip(double t) { return NAN; } -/* The spec mandates the use of 'double' and it fixes the order +/* The spec mandates the use of 'double' and it specifies the order of the operations */ -static double set_date_fields(const double fields[], int is_local) { - int64_t y; - double days, d, h, m1; - int i, m, md; - - m1 = fields[1]; - m = fmod(m1, 12); - if (m < 0) - m += 12; - y = (int64_t)(fields[0] + floor(m1 / 12)); - days = days_from_year(y); - - for(i = 0; i < m; i++) { - md = month_days[i]; +static double set_date_fields(double fields[minimum_length(7)], int is_local) { + double y, m, dt, ym, mn, day, h, s, milli, time, tv; + int yi, mi, i; + int64_t days; + volatile double temp; /* enforce evaluation order */ + + /* emulate 21.4.1.15 MakeDay ( year, month, date ) */ + y = fields[0]; + m = fields[1]; + dt = fields[2]; + ym = y + floor(m / 12); + mn = fmod(m, 12); + if (mn < 0) + mn += 12; + if (ym < -271821 || ym > 275760) + return NAN; + + yi = ym; + mi = mn; + days = days_from_year(yi); + for(i = 0; i < mi; i++) { + days += month_days[i]; if (i == 1) - md += days_in_year(y) - 365; - days += md; + days += days_in_year(yi) - 365; + } + day = days + dt - 1; + + /* emulate 21.4.1.14 MakeTime ( hour, min, sec, ms ) */ + h = fields[3]; + m = fields[4]; + s = fields[5]; + milli = fields[6]; + /* Use a volatile intermediary variable to ensure order of evaluation + * as specified in ECMA. This fixes a test262 error on + * test262/test/built-ins/Date/UTC/fp-evaluation-order.js. + * Without the volatile qualifier, the compile can generate code + * that performs the computation in a different order or with instructions + * that produce a different result such as FMA (float multiply and add). + */ + time = h * 3600000; + time += (temp = m * 60000); + time += (temp = s * 1000); + time += milli; + + /* emulate 21.4.1.16 MakeDate ( day, time ) */ + tv = (temp = day * 86400000) + time; /* prevent generation of FMA */ + if (!isfinite(tv)) + return NAN; + + /* adjust for local time and clip */ + if (is_local) { + int64_t ti = tv < INT64_MIN ? INT64_MIN : tv >= 0x1p63 ? INT64_MAX : (int64_t)tv; + tv += getTimezoneOffset(ti) * 60000; } - days += fields[2] - 1; - h = fields[3] * 3600000 + fields[4] * 60000 + - fields[5] * 1000 + fields[6]; - d = days * 86400000 + h; - if (is_local) - d += getTimezoneOffset(d) * 60000; - return time_clip(d); + return time_clip(tv); } static JSValue get_date_field(JSContext *ctx, JSValueConst this_val, @@ -48195,20 +49831,19 @@ static JSValue set_date_field(JSContext *ctx, JSValueConst this_val, res = get_date_fields(ctx, this_val, fields, is_local, first_field == 0); if (res < 0) return JS_EXCEPTION; - if (res && argc > 0) { - n = end_field - first_field; - if (argc < n) - n = argc; - for(i = 0; i < n; i++) { - if (JS_ToFloat64(ctx, &a, argv[i])) - return JS_EXCEPTION; - if (!isfinite(a)) - goto done; - fields[first_field + i] = trunc(a); - } - d = set_date_fields(fields, is_local); + + // Argument coercion is observable and must be done unconditionally. + n = min_int(argc, end_field - first_field); + for(i = 0; i < n; i++) { + if (JS_ToFloat64(ctx, &a, argv[i])) + return JS_EXCEPTION; + if (!isfinite(a)) + res = FALSE; + fields[first_field + i] = trunc(a); } -done: + if (res && argc > 0) + d = set_date_fields(fields, is_local); + return JS_SetThisTimeValue(ctx, this_val, d); } @@ -48318,7 +49953,7 @@ static JSValue get_date_string(JSContext *ctx, JSValueConst this_val, break; case 3: pos += snprintf(buf + pos, sizeof(buf) - pos, - "%02d:%02d:%02d %cM", (h + 1) % 12 - 1, m, s, + "%02d:%02d:%02d %cM", (h + 11) % 12 + 1, m, s, (h < 12) ? 'A' : 'P'); break; } @@ -48369,7 +50004,7 @@ static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target, } v = JS_ToPrimitive(ctx, argv[0], HINT_NONE); if (JS_IsString(v)) { - dv = js_Date_parse(ctx, JS_UNDEFINED, 1, &v); + dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v); JS_FreeValue(ctx, v); if (JS_IsException(dv)) return JS_EXCEPTION; @@ -48442,142 +50077,418 @@ static JSValue js_Date_UTC(JSContext *ctx, JSValueConst this_val, return JS_NewFloat64(ctx, set_date_fields(fields, 0)); } -static void string_skip_spaces(JSString *sp, int *pp) { - while (*pp < sp->len && string_get(sp, *pp) == ' ') +/* Date string parsing */ + +static BOOL string_skip_char(const uint8_t *sp, int *pp, int c) { + if (sp[*pp] == c) { *pp += 1; + return TRUE; + } else { + return FALSE; + } } -static void string_skip_non_spaces(JSString *sp, int *pp) { - while (*pp < sp->len && string_get(sp, *pp) != ' ') +/* skip spaces, update offset, return next char */ +static int string_skip_spaces(const uint8_t *sp, int *pp) { + int c; + while ((c = sp[*pp]) == ' ') *pp += 1; + return c; } -/* parse a numeric field with an optional sign if accept_sign is TRUE */ -static int string_get_digits(JSString *sp, int *pp, int64_t *pval) { - int64_t v = 0; +/* skip dashes dots and commas */ +static int string_skip_separators(const uint8_t *sp, int *pp) { + int c; + while ((c = sp[*pp]) == '-' || c == '/' || c == '.' || c == ',') + *pp += 1; + return c; +} + +/* skip a word, stop on spaces, digits and separators, update offset */ +static int string_skip_until(const uint8_t *sp, int *pp, const char *stoplist) { + int c; + while (!strchr(stoplist, c = sp[*pp])) + *pp += 1; + return c; +} + +/* parse a numeric field (max_digits = 0 -> no maximum) */ +static BOOL string_get_digits(const uint8_t *sp, int *pp, int *pval, + int min_digits, int max_digits) +{ + int v = 0; int c, p = *pp, p_start; - if (p >= sp->len) - return -1; p_start = p; - while (p < sp->len) { - c = string_get(sp, p); - if (!(c >= '0' && c <= '9')) { - if (p == p_start) - return -1; - else - break; - } + while ((c = sp[p]) >= '0' && c <= '9') { v = v * 10 + c - '0'; p++; + if (p - p_start == max_digits) + break; } + if (p - p_start < min_digits) + return FALSE; *pval = v; *pp = p; - return 0; + return TRUE; } -static int string_get_signed_digits(JSString *sp, int *pp, int64_t *pval) { - int res, sgn, p = *pp; - - if (p >= sp->len) - return -1; +static BOOL string_get_milliseconds(const uint8_t *sp, int *pp, int *pval) { + /* parse optional fractional part as milliseconds and truncate. */ + /* spec does not indicate which rounding should be used */ + int mul = 100, ms = 0, c, p_start, p = *pp; - sgn = string_get(sp, p); - if (sgn == '-' || sgn == '+') + c = sp[p]; + if (c == '.' || c == ',') { p++; + p_start = p; + while ((c = sp[p]) >= '0' && c <= '9') { + ms += (c - '0') * mul; + mul /= 10; + p++; + if (p - p_start == 9) + break; + } + if (p > p_start) { + /* only consume the separator if digits are present */ + *pval = ms; + *pp = p; + } + } + return TRUE; +} - res = string_get_digits(sp, &p, pval); - if (res == 0 && sgn == '-') - *pval = -*pval; - *pp = p; - return res; +static uint8_t upper_ascii(uint8_t c) { + return c >= 'a' && c <= 'z' ? c - 'a' + 'A' : c; } -/* parse a fixed width numeric field */ -static int string_get_fixed_width_digits(JSString *sp, int *pp, int n, int64_t *pval) { - int64_t v = 0; - int i, c, p = *pp; +static BOOL string_get_tzoffset(const uint8_t *sp, int *pp, int *tzp, BOOL strict) { + int tz = 0, sgn, hh, mm, p = *pp; - for(i = 0; i < n; i++) { - if (p >= sp->len) - return -1; - c = string_get(sp, p); - if (!(c >= '0' && c <= '9')) - return -1; - v = v * 10 + c - '0'; - p++; + sgn = sp[p++]; + if (sgn == '+' || sgn == '-') { + int n = p; + if (!string_get_digits(sp, &p, &hh, 1, 9)) + return FALSE; + n = p - n; + if (strict && n != 2 && n != 4) + return FALSE; + while (n > 4) { + n -= 2; + hh /= 100; + } + if (n > 2) { + mm = hh % 100; + hh = hh / 100; + } else { + mm = 0; + if (string_skip_char(sp, &p, ':') /* optional separator */ + && !string_get_digits(sp, &p, &mm, 2, 2)) + return FALSE; + } + if (hh > 23 || mm > 59) + return FALSE; + tz = hh * 60 + mm; + if (sgn != '+') + tz = -tz; + } else + if (sgn != 'Z') { + return FALSE; } - *pval = v; *pp = p; - return 0; + *tzp = tz; + return TRUE; } -static int string_get_milliseconds(JSString *sp, int *pp, int64_t *pval) { - /* parse milliseconds as a fractional part, round to nearest */ - /* XXX: the spec does not indicate which rounding should be used */ - int mul = 1000, ms = 0, p = *pp, c, p_start; - if (p >= sp->len) - return -1; - p_start = p; - while (p < sp->len) { - c = string_get(sp, p); - if (!(c >= '0' && c <= '9')) { - if (p == p_start) - return -1; - else - break; - } - if (mul == 1 && c >= '5') - ms += 1; - ms += (c - '0') * (mul /= 10); +static BOOL string_match(const uint8_t *sp, int *pp, const char *s) { + int p = *pp; + while (*s != '\0') { + if (upper_ascii(sp[p]) != upper_ascii(*s++)) + return FALSE; p++; } - *pval = ms; *pp = p; - return 0; + return TRUE; } - -static int find_abbrev(JSString *sp, int p, const char *list, int count) { +static int find_abbrev(const uint8_t *sp, int p, const char *list, int count) { int n, i; - if (p + 3 <= sp->len) { - for (n = 0; n < count; n++) { - for (i = 0; i < 3; i++) { - if (string_get(sp, p + i) != month_names[n * 3 + i]) - goto next; - } - return n; - next:; + for (n = 0; n < count; n++) { + for (i = 0;; i++) { + if (upper_ascii(sp[p + i]) != upper_ascii(list[n * 3 + i])) + break; + if (i == 2) + return n; } } return -1; } -static int string_get_month(JSString *sp, int *pp, int64_t *pval) { +static BOOL string_get_month(const uint8_t *sp, int *pp, int *pval) { int n; - string_skip_spaces(sp, pp); n = find_abbrev(sp, *pp, month_names, 12); if (n < 0) - return -1; + return FALSE; - *pval = n; + *pval = n + 1; *pp += 3; - return 0; + return TRUE; +} + +/* parse toISOString format */ +static BOOL js_date_parse_isostring(const uint8_t *sp, int fields[9], BOOL *is_local) { + int sgn, i, p = 0; + + /* initialize fields to the beginning of the Epoch */ + for (i = 0; i < 9; i++) { + fields[i] = (i == 2); + } + *is_local = FALSE; + + /* year is either yyyy digits or [+-]yyyyyy */ + sgn = sp[p]; + if (sgn == '-' || sgn == '+') { + p++; + if (!string_get_digits(sp, &p, &fields[0], 6, 6)) + return FALSE; + if (sgn == '-') { + if (fields[0] == 0) + return FALSE; // reject -000000 + fields[0] = -fields[0]; + } + } else { + if (!string_get_digits(sp, &p, &fields[0], 4, 4)) + return FALSE; + } + if (string_skip_char(sp, &p, '-')) { + if (!string_get_digits(sp, &p, &fields[1], 2, 2)) /* month */ + return FALSE; + if (fields[1] < 1) + return FALSE; + fields[1] -= 1; + if (string_skip_char(sp, &p, '-')) { + if (!string_get_digits(sp, &p, &fields[2], 2, 2)) /* day */ + return FALSE; + if (fields[2] < 1) + return FALSE; + } + } + if (string_skip_char(sp, &p, 'T')) { + *is_local = TRUE; + if (!string_get_digits(sp, &p, &fields[3], 2, 2) /* hour */ + || !string_skip_char(sp, &p, ':') + || !string_get_digits(sp, &p, &fields[4], 2, 2)) { /* minute */ + fields[3] = 100; // reject unconditionally + return TRUE; + } + if (string_skip_char(sp, &p, ':')) { + if (!string_get_digits(sp, &p, &fields[5], 2, 2)) /* second */ + return FALSE; + string_get_milliseconds(sp, &p, &fields[6]); + } + } + /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */ + if (sp[p]) { + *is_local = FALSE; + if (!string_get_tzoffset(sp, &p, &fields[8], TRUE)) + return FALSE; + } + /* error if extraneous characters */ + return sp[p] == '\0'; +} + +static struct { + char name[6]; + int16_t offset; +} const js_tzabbr[] = { + { "GMT", 0 }, // Greenwich Mean Time + { "UTC", 0 }, // Coordinated Universal Time + { "UT", 0 }, // Universal Time + { "Z", 0 }, // Zulu Time + { "EDT", -4 * 60 }, // Eastern Daylight Time + { "EST", -5 * 60 }, // Eastern Standard Time + { "CDT", -5 * 60 }, // Central Daylight Time + { "CST", -6 * 60 }, // Central Standard Time + { "MDT", -6 * 60 }, // Mountain Daylight Time + { "MST", -7 * 60 }, // Mountain Standard Time + { "PDT", -7 * 60 }, // Pacific Daylight Time + { "PST", -8 * 60 }, // Pacific Standard Time + { "WET", +0 * 60 }, // Western European Time + { "WEST", +1 * 60 }, // Western European Summer Time + { "CET", +1 * 60 }, // Central European Time + { "CEST", +2 * 60 }, // Central European Summer Time + { "EET", +2 * 60 }, // Eastern European Time + { "EEST", +3 * 60 }, // Eastern European Summer Time +}; + +static BOOL string_get_tzabbr(const uint8_t *sp, int *pp, int *offset) { + for (size_t i = 0; i < countof(js_tzabbr); i++) { + if (string_match(sp, pp, js_tzabbr[i].name)) { + *offset = js_tzabbr[i].offset; + return TRUE; + } + } + return FALSE; +} + +/* parse toString, toUTCString and other formats */ +static BOOL js_date_parse_otherstring(const uint8_t *sp, + int fields[minimum_length(9)], + BOOL *is_local) { + int c, i, val, p = 0, p_start; + int num[3]; + BOOL has_year = FALSE; + BOOL has_mon = FALSE; + BOOL has_time = FALSE; + int num_index = 0; + + /* initialize fields to the beginning of 2001-01-01 */ + fields[0] = 2001; + fields[1] = 1; + fields[2] = 1; + for (i = 3; i < 9; i++) { + fields[i] = 0; + } + *is_local = TRUE; + + while (string_skip_spaces(sp, &p)) { + p_start = p; + if ((c = sp[p]) == '+' || c == '-') { + if (has_time && string_get_tzoffset(sp, &p, &fields[8], FALSE)) { + *is_local = FALSE; + } else { + p++; + if (string_get_digits(sp, &p, &val, 1, 9)) { + if (c == '-') { + if (val == 0) + return FALSE; + val = -val; + } + fields[0] = val; + has_year = TRUE; + } + } + } else + if (string_get_digits(sp, &p, &val, 1, 9)) { + if (string_skip_char(sp, &p, ':')) { + /* time part */ + fields[3] = val; + if (!string_get_digits(sp, &p, &fields[4], 1, 2)) + return FALSE; + if (string_skip_char(sp, &p, ':')) { + if (!string_get_digits(sp, &p, &fields[5], 1, 2)) + return FALSE; + string_get_milliseconds(sp, &p, &fields[6]); + } + has_time = TRUE; + } else { + if (p - p_start > 2) { + fields[0] = val; + has_year = TRUE; + } else + if (val < 1 || val > 31) { + fields[0] = val + (val < 100) * 1900 + (val < 50) * 100; + has_year = TRUE; + } else { + if (num_index == 3) + return FALSE; + num[num_index++] = val; + } + } + } else + if (string_get_month(sp, &p, &fields[1])) { + has_mon = TRUE; + string_skip_until(sp, &p, "0123456789 -/("); + } else + if (has_time && string_match(sp, &p, "PM")) { + if (fields[3] < 12) + fields[3] += 12; + continue; + } else + if (has_time && string_match(sp, &p, "AM")) { + if (fields[3] == 12) + fields[3] -= 12; + continue; + } else + if (string_get_tzabbr(sp, &p, &fields[8])) { + *is_local = FALSE; + continue; + } else + if (c == '(') { /* skip parenthesized phrase */ + int level = 0; + while ((c = sp[p]) != '\0') { + p++; + level += (c == '('); + level -= (c == ')'); + if (!level) + break; + } + if (level > 0) + return FALSE; + } else + if (c == ')') { + return FALSE; + } else { + if (has_year + has_mon + has_time + num_index) + return FALSE; + /* skip a word */ + string_skip_until(sp, &p, " -/("); + } + string_skip_separators(sp, &p); + } + if (num_index + has_year + has_mon > 3) + return FALSE; + + switch (num_index) { + case 0: + if (!has_year) + return FALSE; + break; + case 1: + if (has_mon) + fields[2] = num[0]; + else + fields[1] = num[0]; + break; + case 2: + if (has_year) { + fields[1] = num[0]; + fields[2] = num[1]; + } else + if (has_mon) { + fields[0] = num[1] + (num[1] < 100) * 1900 + (num[1] < 50) * 100; + fields[2] = num[0]; + } else { + fields[1] = num[0]; + fields[2] = num[1]; + } + break; + case 3: + fields[0] = num[2] + (num[2] < 100) * 1900 + (num[2] < 50) * 100; + fields[1] = num[0]; + fields[2] = num[1]; + break; + default: + return FALSE; + } + if (fields[1] < 1 || fields[2] < 1) + return FALSE; + fields[1] -= 1; + return TRUE; } static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { - // parse(s) JSValue s, rv; - int64_t fields[] = { 0, 1, 1, 0, 0, 0, 0 }; - double fields1[7]; - int64_t tz, hh, mm; + int fields[9]; + double fields1[9]; double d; - int p, i, c, sgn, l; + int i, c; JSString *sp; + uint8_t buf[128]; BOOL is_local; rv = JS_NAN; @@ -48587,145 +50498,33 @@ static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, return JS_EXCEPTION; sp = JS_VALUE_GET_STRING(s); - p = 0; - if (p < sp->len && (((c = string_get(sp, p)) >= '0' && c <= '9') || c == '+' || c == '-')) { - /* ISO format */ - /* year field can be negative */ - if (string_get_signed_digits(sp, &p, &fields[0])) - goto done; - - for (i = 1; i < 7; i++) { - if (p >= sp->len) - break; - switch(i) { - case 1: - case 2: - c = '-'; - break; - case 3: - c = 'T'; - break; - case 4: - case 5: - c = ':'; - break; - case 6: - c = '.'; - break; - } - if (string_get(sp, p) != c) - break; - p++; - if (i == 6) { - if (string_get_milliseconds(sp, &p, &fields[i])) - goto done; - } else { - if (string_get_digits(sp, &p, &fields[i])) - goto done; - } - } - /* no time: UTC by default */ - is_local = (i > 3); - fields[1] -= 1; - - /* parse the time zone offset if present: [+-]HH:mm or [+-]HHmm */ - tz = 0; - if (p < sp->len) { - sgn = string_get(sp, p); - if (sgn == '+' || sgn == '-') { - p++; - l = sp->len - p; - if (l != 4 && l != 5) - goto done; - if (string_get_fixed_width_digits(sp, &p, 2, &hh)) - goto done; - if (l == 5) { - if (string_get(sp, p) != ':') - goto done; - p++; - } - if (string_get_fixed_width_digits(sp, &p, 2, &mm)) - goto done; - tz = hh * 60 + mm; - if (sgn == '-') - tz = -tz; - is_local = FALSE; - } else if (sgn == 'Z') { - p++; - is_local = FALSE; - } else { - goto done; - } - /* error if extraneous characters */ - if (p != sp->len) - goto done; - } - } else { - /* toString or toUTCString format */ - /* skip the day of the week */ - string_skip_non_spaces(sp, &p); - string_skip_spaces(sp, &p); - if (p >= sp->len) - goto done; - c = string_get(sp, p); - if (c >= '0' && c <= '9') { - /* day of month first */ - if (string_get_digits(sp, &p, &fields[2])) - goto done; - if (string_get_month(sp, &p, &fields[1])) - goto done; - } else { - /* month first */ - if (string_get_month(sp, &p, &fields[1])) - goto done; - string_skip_spaces(sp, &p); - if (string_get_digits(sp, &p, &fields[2])) - goto done; - } - /* year */ - string_skip_spaces(sp, &p); - if (string_get_signed_digits(sp, &p, &fields[0])) - goto done; - - /* hour, min, seconds */ - string_skip_spaces(sp, &p); - for(i = 0; i < 3; i++) { - if (i == 1 || i == 2) { - if (p >= sp->len) - goto done; - if (string_get(sp, p) != ':') - goto done; - p++; - } - if (string_get_digits(sp, &p, &fields[3 + i])) - goto done; - } - // XXX: parse optional milliseconds? - - /* parse the time zone offset if present: [+-]HHmm */ - is_local = FALSE; - tz = 0; - for (tz = 0; p < sp->len; p++) { - sgn = string_get(sp, p); - if (sgn == '+' || sgn == '-') { - p++; - if (string_get_fixed_width_digits(sp, &p, 2, &hh)) - goto done; - if (string_get_fixed_width_digits(sp, &p, 2, &mm)) - goto done; - tz = hh * 60 + mm; - if (sgn == '-') - tz = -tz; - break; - } + /* convert the string as a byte array */ + for (i = 0; i < sp->len && i < (int)countof(buf) - 1; i++) { + c = string_get(sp, i); + if (c > 255) + c = (c == 0x2212) ? '-' : 'x'; + buf[i] = c; + } + buf[i] = '\0'; + if (js_date_parse_isostring(buf, fields, &is_local) + || js_date_parse_otherstring(buf, fields, &is_local)) { + static int const field_max[6] = { 0, 11, 31, 24, 59, 59 }; + BOOL valid = TRUE; + /* check field maximum values */ + for (i = 1; i < 6; i++) { + if (fields[i] > field_max[i]) + valid = FALSE; + } + /* special case 24:00:00.000 */ + if (fields[3] == 24 && (fields[4] | fields[5] | fields[6])) + valid = FALSE; + if (valid) { + for(i = 0; i < 7; i++) + fields1[i] = fields[i]; + d = set_date_fields(fields1, is_local) - fields[8] * 60000; + rv = JS_NewFloat64(ctx, d); } } - for(i = 0; i < 7; i++) - fields1[i] = fields[i]; - d = set_date_fields(fields1, is_local) - tz * 60000; - rv = JS_NewFloat64(ctx, d); - -done: JS_FreeValue(ctx, s); return rv; } @@ -48756,9 +50555,7 @@ static JSValue js_date_Symbol_toPrimitive(JSContext *ctx, JSValueConst this_val, } switch (hint) { case JS_ATOM_number: -#ifdef CONFIG_BIGNUM case JS_ATOM_integer: -#endif hint_num = HINT_NUMBER; break; case JS_ATOM_string: @@ -48782,6 +50579,7 @@ static JSValue js_date_getTimezoneOffset(JSContext *ctx, JSValueConst this_val, if (isnan(v)) return JS_NAN; else + /* assuming -8.64e15 <= v <= -8.64e15 */ return JS_NewInt64(ctx, getTimezoneOffset((int64_t)trunc(v))); } @@ -48920,6 +50718,23 @@ static const JSCFunctionListEntry js_date_proto_funcs[] = { JS_CFUNC_DEF("toJSON", 1, js_date_toJSON ), }; +JSValue JS_NewDate(JSContext *ctx, double epoch_ms) +{ + JSValue obj = js_create_from_ctor(ctx, JS_UNDEFINED, JS_CLASS_DATE); + if (JS_IsException(obj)) + return JS_EXCEPTION; + JS_SetObjectData(ctx, obj, __JS_NewFloat64(ctx, time_clip(epoch_ms))); + return obj; +} + +JS_BOOL JS_IsDate(JSValue v) +{ + JSObject *p; + if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT) + return FALSE; + return JS_VALUE_GET_OBJ(v)->class_id == JS_CLASS_DATE; +} + void JS_AddIntrinsicDate(JSContext *ctx) { JSValueConst obj; @@ -48937,7 +50752,7 @@ void JS_AddIntrinsicDate(JSContext *ctx) void JS_AddIntrinsicEval(JSContext *ctx) { - ctx->eval_internal = JS_EvalInternalImpl; + ctx->eval_internal = __JS_EvalInternal; } #ifdef CONFIG_BIGNUM @@ -49239,6 +51054,7 @@ void JS_AddIntrinsicOperators(JSContext *ctx) js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_FLOAT]); js_operators_set_default(ctx, ctx->class_proto[JS_CLASS_BIG_DECIMAL]); } +#endif /* CONFIG_BIGNUM */ /* BigInt */ @@ -49256,14 +51072,20 @@ static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) case JS_TAG_BIG_INT: break; case JS_TAG_FLOAT64: +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_FLOAT: +#endif { bf_t *a, a_s; a = JS_ToBigFloat(ctx, &a_s, val); + if (!a) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } if (!bf_is_finite(a)) { JS_FreeValue(ctx, val); - val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to bigint"); + val = JS_ThrowRangeError(ctx, "cannot convert NaN or Infinity to BigInt"); } else { JSValue val1 = JS_NewBigInt(ctx); bf_t *r; @@ -49281,7 +51103,7 @@ static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) val = JS_ThrowOutOfMemory(ctx); } else if (ret & BF_ST_INEXACT) { JS_FreeValue(ctx, val1); - val = JS_ThrowRangeError(ctx, "cannot convert to bigint: not an integer"); + val = JS_ThrowRangeError(ctx, "cannot convert to BigInt: not an integer"); } else { val = JS_CompactBigInt(ctx, val1); } @@ -49290,11 +51112,13 @@ static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) bf_delete(a); } break; +#ifdef CONFIG_BIGNUM case JS_TAG_BIG_DECIMAL: val = JS_ToStringFree(ctx, val); - if (JS_IsException(val)) + if (JS_IsException(val)) break; goto redo; +#endif case JS_TAG_STRING: val = JS_StringToBigIntErr(ctx, val); break; @@ -49307,7 +51131,7 @@ static JSValue JS_ToBigIntCtorFree(JSContext *ctx, JSValue val) case JS_TAG_UNDEFINED: default: JS_FreeValue(ctx, val); - return JS_ThrowTypeError(ctx, "cannot convert to bigint"); + return JS_ThrowTypeError(ctx, "cannot convert to BigInt"); } return val; } @@ -49333,7 +51157,7 @@ static JSValue js_thisBigIntValue(JSContext *ctx, JSValueConst this_val) return JS_DupValue(ctx, p->u.object_data); } } - return JS_ThrowTypeError(ctx, "not a bigint"); + return JS_ThrowTypeError(ctx, "not a BigInt"); } static JSValue js_bigint_toString(JSContext *ctx, JSValueConst this_val, @@ -49367,6 +51191,7 @@ static JSValue js_bigint_valueOf(JSContext *ctx, JSValueConst this_val, return js_thisBigIntValue(ctx, this_val); } +#ifdef CONFIG_BIGNUM static JSValue js_bigint_div(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic) @@ -49495,6 +51320,7 @@ static JSValue js_bigint_op1(JSContext *ctx, JS_FreeBigInt(ctx, a, &a_s); return JS_NewBigInt64(ctx, res); } +#endif static JSValue js_bigint_asUintN(JSContext *ctx, JSValueConst this_val, @@ -49539,6 +51365,7 @@ static JSValue js_bigint_asUintN(JSContext *ctx, static const JSCFunctionListEntry js_bigint_funcs[] = { JS_CFUNC_MAGIC_DEF("asUintN", 2, js_bigint_asUintN, 0 ), JS_CFUNC_MAGIC_DEF("asIntN", 2, js_bigint_asUintN, 1 ), +#ifdef CONFIG_BIGNUM /* QuickJS extensions */ JS_CFUNC_MAGIC_DEF("tdiv", 2, js_bigint_div, BF_RNDZ ), JS_CFUNC_MAGIC_DEF("fdiv", 2, js_bigint_div, BF_RNDD ), @@ -49552,6 +51379,7 @@ static const JSCFunctionListEntry js_bigint_funcs[] = { JS_CFUNC_MAGIC_DEF("sqrtrem", 1, js_bigint_sqrt, 1 ), JS_CFUNC_MAGIC_DEF("floorLog2", 1, js_bigint_op1, 0 ), JS_CFUNC_MAGIC_DEF("ctz", 1, js_bigint_op1, 1 ), +#endif }; static const JSCFunctionListEntry js_bigint_proto_funcs[] = { @@ -49581,6 +51409,8 @@ void JS_AddIntrinsicBigInt(JSContext *ctx) countof(js_bigint_funcs)); } +#ifdef CONFIG_BIGNUM + /* BigFloat */ static JSValue js_thisBigFloatValue(JSContext *ctx, JSValueConst this_val) @@ -50054,6 +51884,10 @@ static JSValue js_bigfloat_fop(JSContext *ctx, JSValueConst this_val, if (JS_IsException(op1)) return op1; a = JS_ToBigFloat(ctx, &a_s, op1); + if (!a) { + JS_FreeValue(ctx, op1); + return JS_EXCEPTION; + } fe = &ctx->fp_env; if (argc > 1) { fe = JS_GetOpaque2(ctx, argv[1], JS_CLASS_FLOAT_ENV); @@ -50152,7 +51986,11 @@ static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val, return op2; } a = JS_ToBigFloat(ctx, &a_s, op1); + if (!a) + goto fail1; b = JS_ToBigFloat(ctx, &b_s, op2); + if (!b) + goto fail2; fe = &ctx->fp_env; if (argc > 2) { fe = JS_GetOpaque2(ctx, argv[2], JS_CLASS_FLOAT_ENV); @@ -50162,10 +52000,12 @@ static JSValue js_bigfloat_fop2(JSContext *ctx, JSValueConst this_val, res = JS_NewBigFloat(ctx); if (JS_IsException(res)) { fail: - if (a == &a_s) - bf_delete(a); if (b == &b_s) bf_delete(b); + fail2: + if (a == &a_s) + bf_delete(a); + fail1: JS_FreeValue(ctx, op1); JS_FreeValue(ctx, op2); return JS_EXCEPTION; @@ -50358,9 +52198,9 @@ static JSValue js_float_env_proto_get_status(JSContext *ctx, JSValueConst this_v case FE_RNDMODE: return JS_NewInt32(ctx, fe->flags & BF_RND_MASK); case FE_SUBNORMAL: - return JS_NewBool(ctx, (fe->flags & BF_FLAG_SUBNORMAL) != 0); + return JS_NewBool(ctx, fe->flags & BF_FLAG_SUBNORMAL); default: - return JS_NewBool(ctx, (fe->status & magic) != 0); + return JS_NewBool(ctx, fe->status & magic); } } @@ -51057,7 +52897,7 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) JS_PROP_HAS_GET | JS_PROP_HAS_SET | JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE); JS_FreeValue(ctx, obj1); - JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, &ctx->throw_type_error, 1)); + JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1)); ctx->global_obj = JS_NewObject(ctx); ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL); @@ -51083,11 +52923,13 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) JS_NewGlobalCConstructor2(ctx, obj1, "Error", ctx->class_proto[JS_CLASS_ERROR]); + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft = { .generic_magic = js_error_constructor }; for(i = 0; i < JS_NATIVE_ERROR_COUNT; i++) { JSValue func_obj; int n_args; n_args = 1 + (i == JS_AGGREGATE_ERROR); - func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_error_constructor, + func_obj = JS_NewCFunction3(ctx, ft.generic, native_error_name[i], n_args, JS_CFUNC_constructor_or_func_magic, i, obj1); JS_NewGlobalCConstructor2(ctx, func_obj, native_error_name[i], @@ -51114,8 +52956,22 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) /* XXX: create auto_initializer */ { /* initialize Array.prototype[Symbol.unscopables] */ - char const unscopables[] = "copyWithin" "\0" "entries" "\0" "fill" "\0" "find" "\0" - "findIndex" "\0" "flat" "\0" "flatMap" "\0" "includes" "\0" "keys" "\0" "values" "\0"; + static const char unscopables[] = + "copyWithin" "\0" + "entries" "\0" + "fill" "\0" + "find" "\0" + "findIndex" "\0" + "findLast" "\0" + "findLastIndex" "\0" + "flat" "\0" + "flatMap" "\0" + "includes" "\0" + "keys" "\0" + "toReversed" "\0" + "toSorted" "\0" + "toSpliced" "\0" + "values" "\0"; const char *p = unscopables; obj1 = JS_NewObjectProto(ctx, JS_NULL); for(p = unscopables; *p; p += strlen(p) + 1) { @@ -51239,9 +53095,7 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) static uint8_t const typed_array_size_log2[JS_TYPED_ARRAY_COUNT] = { 0, 0, 0, 1, 1, 2, 2, -#ifdef CONFIG_BIGNUM 3, 3, /* BigInt64Array, BigUint64Array */ -#endif 2, 3 }; @@ -51371,11 +53225,26 @@ static void js_array_buffer_finalizer(JSRuntime *rt, JSValue val) { JSObject *p = JS_VALUE_GET_OBJ(val); JSArrayBuffer *abuf = p->u.array_buffer; + struct list_head *el, *el1; + if (abuf) { /* The ArrayBuffer finalizer may be called before the typed array finalizers using it, so abuf->array_list is not necessarily empty. */ - // assert(list_empty(&abuf->array_list)); + list_for_each_safe(el, el1, &abuf->array_list) { + JSTypedArray *ta; + JSObject *p1; + + ta = list_entry(el, JSTypedArray, link); + ta->link.prev = NULL; + ta->link.next = NULL; + p1 = ta->obj; + /* Note: the typed array length and offset fields are not modified */ + if (p1->class_id != JS_CLASS_DATAVIEW) { + p1->u.array.count = 0; + p1->u.array.u.ptr = NULL; + } + } if (abuf->shared && rt->sab_funcs.sab_free) { rt->sab_funcs.sab_free(rt->sab_funcs.sab_opaque, abuf->data); } else { @@ -51685,6 +53554,16 @@ static JSValue js_typed_array_get_byteOffset(JSContext *ctx, return JS_NewInt32(ctx, ta->offset); } +JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValueConst *argv, + JSTypedArrayEnum type) +{ + if (type < JS_TYPED_ARRAY_UINT8C || type > JS_TYPED_ARRAY_FLOAT64) + return JS_ThrowRangeError(ctx, "invalid typed array type"); + + return js_typed_array_constructor(ctx, JS_UNDEFINED, argc, argv, + JS_CLASS_UINT8C_ARRAY + type); +} + /* Return the buffer associated to the typed array or an exception if it is not a typed array or if the buffer is detached. pbyte_offset, pbyte_length or pbytes_per_element can be NULL. */ @@ -51802,6 +53681,69 @@ fail: return JS_EXCEPTION; } +static JSValue js_typed_array_at(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSObject *p; + int64_t idx, len; + + p = get_typed_array(ctx, this_val, 0); + if (!p) + return JS_EXCEPTION; + + if (typed_array_is_detached(ctx, p)) { + JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + return JS_EXCEPTION; + } + + if (JS_ToInt64Sat(ctx, &idx, argv[0])) + return JS_EXCEPTION; + + len = p->u.array.count; + if (idx < 0) + idx = len + idx; + if (idx < 0 || idx >= len) + return JS_UNDEFINED; + return JS_GetPropertyInt64(ctx, this_val, idx); +} + +static JSValue js_typed_array_with(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue arr, val; + JSObject *p; + int64_t idx, len; + + p = get_typed_array(ctx, this_val, /*is_dataview*/0); + if (!p) + return JS_EXCEPTION; + + if (JS_ToInt64Sat(ctx, &idx, argv[0])) + return JS_EXCEPTION; + + len = p->u.array.count; + if (idx < 0) + idx = len + idx; + if (idx < 0 || idx >= len) + return JS_ThrowRangeError(ctx, "invalid array index"); + + val = JS_ToPrimitive(ctx, argv[1], HINT_NUMBER); + if (JS_IsException(val)) + return JS_EXCEPTION; + + arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, + p->class_id); + if (JS_IsException(arr)) { + JS_FreeValue(ctx, val); + return JS_EXCEPTION; + } + if (JS_SetPropertyInt64(ctx, arr, idx, val) < 0) { + JS_FreeValue(ctx, arr); + return JS_EXCEPTION; + } + return arr; +} + static JSValue js_typed_array_set(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) @@ -52091,14 +54033,10 @@ static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val, if (JS_ToUint32(ctx, &v, argv[0])) return JS_EXCEPTION; v64 = v; - } else -#ifdef CONFIG_BIGNUM - if (p->class_id <= JS_CLASS_BIG_UINT64_ARRAY) { + } else if (p->class_id <= JS_CLASS_BIG_UINT64_ARRAY) { if (JS_ToBigInt64(ctx, (int64_t *)&v64, argv[0])) return JS_EXCEPTION; - } else -#endif - { + } else { double d; if (JS_ToFloat64(ctx, &d, argv[0])) return JS_EXCEPTION; @@ -52160,12 +54098,13 @@ static JSValue js_typed_array_fill(JSContext *ctx, JSValueConst this_val, } static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int findIndex) + int argc, JSValueConst *argv, int mode) { JSValueConst func, this_arg; JSValueConst args[3]; JSValue val, index_val, res; - int len, k; + int len, k, end; + int dir; val = JS_UNDEFINED; len = js_typed_array_get_length_internal(ctx, this_val); @@ -52180,7 +54119,16 @@ static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val, if (argc > 1) this_arg = argv[1]; - for(k = 0; k < len; k++) { + k = 0; + dir = 1; + end = len; + if (mode == ArrayFindLast || mode == ArrayFindLastIndex) { + k = len - 1; + dir = -1; + end = -1; + } + + for(; k != end; k += dir) { index_val = JS_NewInt32(ctx, k); val = JS_GetPropertyValue(ctx, this_val, index_val); if (JS_IsException(val)) @@ -52192,7 +54140,7 @@ static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val, if (JS_IsException(res)) goto exception; if (JS_ToBoolFree(ctx, res)) { - if (findIndex) { + if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) { JS_FreeValue(ctx, val); return index_val; } else { @@ -52201,7 +54149,7 @@ static JSValue js_typed_array_find(JSContext *ctx, JSValueConst this_val, } JS_FreeValue(ctx, val); } - if (findIndex) + if (mode == ArrayFindIndex || mode == ArrayFindLastIndex) return JS_NewInt32(ctx, -1); else return JS_UNDEFINED; @@ -52213,7 +54161,7 @@ exception: #define special_indexOf 0 #define special_lastIndexOf 1 -#define special_includes (-1) +#define special_includes -1 static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int special) @@ -52280,13 +54228,14 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, is_int = 1; v64 = JS_VALUE_GET_INT(argv[0]); d = v64; - } else if (tag == JS_TAG_FLOAT64) { - d = JS_VALUE_GET_FLOAT64(argv[0]); - v64 = d; - is_int = (v64 == d); } else -#ifdef CONFIG_BIGNUM - if (tag == JS_TAG_BIG_INT) { + if (tag == JS_TAG_FLOAT64) { + d = JS_VALUE_GET_FLOAT64(argv[0]); + if (d >= INT64_MIN && d < 0x1p63) { + v64 = d; + is_int = (v64 == d); + } + } else if (tag == JS_TAG_BIG_INT) { JSBigFloat *p1 = JS_VALUE_GET_PTR(argv[0]); if (p->class_id == JS_CLASS_BIG_INT64_ARRAY) { @@ -52300,9 +54249,7 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, } d = 0; is_bigint = 1; - } else -#endif - { + } else { goto done; } @@ -52419,7 +54366,6 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, } } break; -#ifdef CONFIG_BIGNUM case JS_CLASS_BIG_INT64_ARRAY: if (is_bigint || (is_math_mode(ctx) && is_int && v64 >= -MAX_SAFE_INTEGER && @@ -52443,7 +54389,6 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, } } break; -#endif } done: @@ -52578,6 +54523,24 @@ static JSValue js_typed_array_reverse(JSContext *ctx, JSValueConst this_val, return JS_DupValue(ctx, this_val); } +static JSValue js_typed_array_toReversed(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue arr, ret; + JSObject *p; + + p = get_typed_array(ctx, this_val, /*is_dataview*/0); + if (!p) + return JS_EXCEPTION; + arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, + p->class_id); + if (JS_IsException(arr)) + return JS_EXCEPTION; + ret = js_typed_array_reverse(ctx, arr, argc, argv); + JS_FreeValue(ctx, arr); + return ret; +} + static JSValue js_typed_array_slice(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { @@ -52724,7 +54687,6 @@ static int js_TA_cmp_uint32(const void *a, const void *b, void *opaque) { return (y < x) - (y > x); } -#ifdef CONFIG_BIGNUM static int js_TA_cmp_int64(const void *a, const void *b, void *opaque) { int64_t x = *(const int64_t *)a; int64_t y = *(const int64_t *)b; @@ -52736,7 +54698,6 @@ static int js_TA_cmp_uint64(const void *a, const void *b, void *opaque) { uint64_t y = *(const uint64_t *)b; return (y < x) - (y > x); } -#endif static int js_TA_cmp_float32(const void *a, const void *b, void *opaque) { return js_cmp_doubles(*(const float *)a, *(const float *)b); @@ -52770,7 +54731,6 @@ static JSValue js_TA_get_uint32(JSContext *ctx, const void *a) { return JS_NewUint32(ctx, *(const uint32_t *)a); } -#ifdef CONFIG_BIGNUM static JSValue js_TA_get_int64(JSContext *ctx, const void *a) { return JS_NewBigInt64(ctx, *(int64_t *)a); } @@ -52778,19 +54738,18 @@ static JSValue js_TA_get_int64(JSContext *ctx, const void *a) { static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) { return JS_NewBigUint64(ctx, *(uint64_t *)a); } -#endif static JSValue js_TA_get_float32(JSContext *ctx, const void *a) { - return JS_NewFloat64Impl(ctx, *(const float *)a); + return __JS_NewFloat64(ctx, *(const float *)a); } static JSValue js_TA_get_float64(JSContext *ctx, const void *a) { - return JS_NewFloat64Impl(ctx, *(const double *)a); + return __JS_NewFloat64(ctx, *(const double *)a); } struct TA_sort_context { JSContext *ctx; - int exception; + int exception; /* 1 = exception, 2 = detached typed array */ JSValueConst arr; JSValueConst cmp; JSValue (*getfun)(JSContext *ctx, const void *a); @@ -52808,6 +54767,8 @@ static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) { cmp = 0; if (!psc->exception) { + /* Note: the typed array can be detached without causing an + error */ a_idx = *(uint32_t *)a; b_idx = *(uint32_t *)b; argv[0] = psc->getfun(ctx, psc->array_ptr + @@ -52835,8 +54796,9 @@ static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) { /* make sort stable: compare array offsets */ cmp = (a_idx > b_idx) - (a_idx < b_idx); } - if (validate_typed_array(ctx, psc->arr) < 0) { - psc->exception = 1; + if (unlikely(typed_array_is_detached(ctx, + JS_VALUE_GET_PTR(psc->arr)))) { + psc->exception = 2; } done: JS_FreeValue(ctx, argv[0]); @@ -52860,11 +54822,11 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val, tsc.arr = this_val; tsc.cmp = argv[0]; + if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp)) + return JS_EXCEPTION; len = js_typed_array_get_length_internal(ctx, this_val); if (len < 0) return JS_EXCEPTION; - if (!JS_IsUndefined(tsc.cmp) && check_function(ctx, tsc.cmp)) - return JS_EXCEPTION; if (len > 1) { p = JS_VALUE_GET_OBJ(this_val); @@ -52894,7 +54856,6 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val, tsc.getfun = js_TA_get_uint32; cmpfun = js_TA_cmp_uint32; break; -#ifdef CONFIG_BIGNUM case JS_CLASS_BIG_INT64_ARRAY: tsc.getfun = js_TA_get_int64; cmpfun = js_TA_cmp_int64; @@ -52903,7 +54864,6 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val, tsc.getfun = js_TA_get_uint64; cmpfun = js_TA_cmp_uint64; break; -#endif case JS_CLASS_FLOAT32_ARRAY: tsc.getfun = js_TA_get_float32; cmpfun = js_TA_cmp_float32; @@ -52932,44 +54892,48 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val, tsc.elt_size = elt_size; rqsort(array_idx, len, sizeof(array_idx[0]), js_TA_cmp_generic, &tsc); - if (tsc.exception) - goto fail; - array_tmp = js_malloc(ctx, len * elt_size); - if (!array_tmp) { - fail: - js_free(ctx, array_idx); - return JS_EXCEPTION; - } - memcpy(array_tmp, array_ptr, len * elt_size); - switch(elt_size) { - case 1: - for(i = 0; i < len; i++) { - j = array_idx[i]; - ((uint8_t *)array_ptr)[i] = ((uint8_t *)array_tmp)[j]; - } - break; - case 2: - for(i = 0; i < len; i++) { - j = array_idx[i]; - ((uint16_t *)array_ptr)[i] = ((uint16_t *)array_tmp)[j]; - } - break; - case 4: - for(i = 0; i < len; i++) { - j = array_idx[i]; - ((uint32_t *)array_ptr)[i] = ((uint32_t *)array_tmp)[j]; + if (tsc.exception) { + if (tsc.exception == 1) + goto fail; + /* detached typed array during the sort: no error */ + } else { + array_tmp = js_malloc(ctx, len * elt_size); + if (!array_tmp) { + fail: + js_free(ctx, array_idx); + return JS_EXCEPTION; } - break; - case 8: - for(i = 0; i < len; i++) { - j = array_idx[i]; - ((uint64_t *)array_ptr)[i] = ((uint64_t *)array_tmp)[j]; + memcpy(array_tmp, array_ptr, len * elt_size); + switch(elt_size) { + case 1: + for(i = 0; i < len; i++) { + j = array_idx[i]; + ((uint8_t *)array_ptr)[i] = ((uint8_t *)array_tmp)[j]; + } + break; + case 2: + for(i = 0; i < len; i++) { + j = array_idx[i]; + ((uint16_t *)array_ptr)[i] = ((uint16_t *)array_tmp)[j]; + } + break; + case 4: + for(i = 0; i < len; i++) { + j = array_idx[i]; + ((uint32_t *)array_ptr)[i] = ((uint32_t *)array_tmp)[j]; + } + break; + case 8: + for(i = 0; i < len; i++) { + j = array_idx[i]; + ((uint64_t *)array_ptr)[i] = ((uint64_t *)array_tmp)[j]; + } + break; + default: + abort(); } - break; - default: - abort(); + js_free(ctx, array_tmp); } - js_free(ctx, array_tmp); js_free(ctx, array_idx); } else { rqsort(array_ptr, len, elt_size, cmpfun, &tsc); @@ -52980,6 +54944,24 @@ static JSValue js_typed_array_sort(JSContext *ctx, JSValueConst this_val, return JS_DupValue(ctx, this_val); } +static JSValue js_typed_array_toSorted(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + JSValue arr, ret; + JSObject *p; + + p = get_typed_array(ctx, this_val, /*is_dataview*/0); + if (!p) + return JS_EXCEPTION; + arr = js_typed_array_constructor_ta(ctx, JS_UNDEFINED, this_val, + p->class_id); + if (JS_IsException(arr)) + return JS_EXCEPTION; + ret = js_typed_array_sort(ctx, arr, argc, argv); + JS_FreeValue(ctx, arr); + return ret; +} + static const JSCFunctionListEntry js_typed_array_base_funcs[] = { JS_CFUNC_DEF("from", 1, js_typed_array_from ), JS_CFUNC_DEF("of", 0, js_typed_array_of ), @@ -52991,6 +54973,8 @@ static const JSCFunctionListEntry js_typed_array_base_funcs[] = { static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = { JS_CGETSET_DEF("length", js_typed_array_get_length, NULL ), + JS_CFUNC_DEF("at", 1, js_typed_array_at ), + JS_CFUNC_DEF("with", 2, js_typed_array_with ), JS_CGETSET_MAGIC_DEF("buffer", js_typed_array_get_buffer, NULL, 0 ), JS_CGETSET_MAGIC_DEF("byteLength", js_typed_array_get_byteLength, NULL, 0 ), JS_CGETSET_MAGIC_DEF("byteOffset", js_typed_array_get_byteOffset, NULL, 0 ), @@ -53009,12 +54993,16 @@ static const JSCFunctionListEntry js_typed_array_base_proto_funcs[] = { JS_CFUNC_MAGIC_DEF("reduce", 1, js_array_reduce, special_reduce | special_TA ), JS_CFUNC_MAGIC_DEF("reduceRight", 1, js_array_reduce, special_reduceRight | special_TA ), JS_CFUNC_DEF("fill", 1, js_typed_array_fill ), - JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, 0 ), - JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, 1 ), + JS_CFUNC_MAGIC_DEF("find", 1, js_typed_array_find, ArrayFind ), + JS_CFUNC_MAGIC_DEF("findIndex", 1, js_typed_array_find, ArrayFindIndex ), + JS_CFUNC_MAGIC_DEF("findLast", 1, js_typed_array_find, ArrayFindLast ), + JS_CFUNC_MAGIC_DEF("findLastIndex", 1, js_typed_array_find, ArrayFindLastIndex ), JS_CFUNC_DEF("reverse", 0, js_typed_array_reverse ), + JS_CFUNC_DEF("toReversed", 0, js_typed_array_toReversed ), JS_CFUNC_DEF("slice", 2, js_typed_array_slice ), JS_CFUNC_DEF("subarray", 2, js_typed_array_subarray ), JS_CFUNC_DEF("sort", 1, js_typed_array_sort ), + JS_CFUNC_DEF("toSorted", 1, js_typed_array_toSorted ), JS_CFUNC_MAGIC_DEF("join", 1, js_typed_array_join, 0 ), JS_CFUNC_MAGIC_DEF("toLocaleString", 0, js_typed_array_join, 1 ), JS_CFUNC_MAGIC_DEF("indexOf", 1, js_typed_array_indexOf, special_indexOf ), @@ -53161,7 +55149,7 @@ static JSValue js_typed_array_constructor_ta(JSContext *ctx, { JSObject *p, *src_buffer; JSTypedArray *ta; - JSValue ctor, obj, buffer; + JSValue obj, buffer; uint32_t len, i; int size_log2; JSArrayBuffer *src_abuf, *abuf; @@ -53178,19 +55166,9 @@ static JSValue js_typed_array_constructor_ta(JSContext *ctx, len = p->u.array.count; src_buffer = ta->buffer; src_abuf = src_buffer->u.array_buffer; - if (!src_abuf->shared) { - ctor = JS_SpeciesConstructor(ctx, JS_MKPTR(JS_TAG_OBJECT, src_buffer), - JS_UNDEFINED); - if (JS_IsException(ctor)) - goto fail; - } else { - /* force ArrayBuffer default constructor */ - ctor = JS_UNDEFINED; - } size_log2 = typed_array_size_log2(classid); - buffer = js_array_buffer_constructor1(ctx, ctor, + buffer = js_array_buffer_constructor1(ctx, JS_UNDEFINED, (uint64_t)len << size_log2); - JS_FreeValue(ctx, ctor); if (JS_IsException(buffer)) goto fail; /* necessary because it could have been detached */ @@ -53296,7 +55274,7 @@ static void js_typed_array_finalizer(JSRuntime *rt, JSValue val) if (ta) { /* during the GC the finalizers are called in an arbitrary order so the ArrayBuffer finalizer may have been called */ - if (JS_IsLiveObject(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer))) { + if (ta->link.next) { list_del(&ta->link); } JS_FreeValueRT(rt, JS_MKPTR(JS_TAG_OBJECT, ta->buffer)); @@ -53379,7 +55357,8 @@ static JSValue js_dataview_getValue(JSContext *ctx, { JSTypedArray *ta; JSArrayBuffer *abuf; - int is_swap, size; + BOOL littleEndian, is_swap; + int size; uint8_t *ptr; uint32_t v; uint64_t pos; @@ -53390,12 +55369,8 @@ static JSValue js_dataview_getValue(JSContext *ctx, size = 1 << typed_array_size_log2(class_id); if (JS_ToIndex(ctx, &pos, argv[0])) return JS_EXCEPTION; - is_swap = FALSE; - if (argc > 1) - is_swap = JS_ToBool(ctx, argv[1]); -#ifndef WORDS_BIGENDIAN - is_swap ^= 1; -#endif + littleEndian = argc > 1 && JS_ToBool(ctx, argv[1]); + is_swap = littleEndian ^ !is_be(); abuf = ta->buffer->u.array_buffer; if (abuf->detached) return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); @@ -53407,7 +55382,7 @@ static JSValue js_dataview_getValue(JSContext *ctx, case JS_CLASS_INT8_ARRAY: return JS_NewInt32(ctx, *(int8_t *)ptr); case JS_CLASS_UINT8_ARRAY: - return JS_NewInt32(ctx, *ptr); + return JS_NewInt32(ctx, *(uint8_t *)ptr); case JS_CLASS_INT16_ARRAY: v = get_u16(ptr); if (is_swap) @@ -53428,7 +55403,6 @@ static JSValue js_dataview_getValue(JSContext *ctx, if (is_swap) v = bswap32(v); return JS_NewUint32(ctx, v); -#ifdef CONFIG_BIGNUM case JS_CLASS_BIG_INT64_ARRAY: { uint64_t v; @@ -53447,7 +55421,6 @@ static JSValue js_dataview_getValue(JSContext *ctx, return JS_NewBigUint64(ctx, v); } break; -#endif case JS_CLASS_FLOAT32_ARRAY: { union { @@ -53458,7 +55431,7 @@ static JSValue js_dataview_getValue(JSContext *ctx, if (is_swap) v = bswap32(v); u.i = v; - return JS_NewFloat64Impl(ctx, u.f); + return __JS_NewFloat64(ctx, u.f); } case JS_CLASS_FLOAT64_ARRAY: { @@ -53469,7 +55442,7 @@ static JSValue js_dataview_getValue(JSContext *ctx, u.i = get_u64(ptr); if (is_swap) u.i = bswap64(u.i); - return JS_NewFloat64Impl(ctx, u.f); + return __JS_NewFloat64(ctx, u.f); } default: abort(); @@ -53482,7 +55455,8 @@ static JSValue js_dataview_setValue(JSContext *ctx, { JSTypedArray *ta; JSArrayBuffer *abuf; - int is_swap, size; + BOOL littleEndian, is_swap; + int size; uint8_t *ptr; uint64_t v64; uint32_t v; @@ -53501,14 +55475,10 @@ static JSValue js_dataview_setValue(JSContext *ctx, if (class_id <= JS_CLASS_UINT32_ARRAY) { if (JS_ToUint32(ctx, &v, val)) return JS_EXCEPTION; - } else -#ifdef CONFIG_BIGNUM - if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) { + } else if (class_id <= JS_CLASS_BIG_UINT64_ARRAY) { if (JS_ToBigInt64(ctx, (int64_t *)&v64, val)) return JS_EXCEPTION; - } else -#endif - { + } else { double d; if (JS_ToFloat64(ctx, &d, val)) return JS_EXCEPTION; @@ -53525,12 +55495,8 @@ static JSValue js_dataview_setValue(JSContext *ctx, v64 = u.u64; } } - is_swap = FALSE; - if (argc > 2) - is_swap = JS_ToBool(ctx, argv[2]); -#ifndef WORDS_BIGENDIAN - is_swap ^= 1; -#endif + littleEndian = argc > 2 && JS_ToBool(ctx, argv[2]); + is_swap = littleEndian ^ !is_be(); abuf = ta->buffer->u.array_buffer; if (abuf->detached) return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); @@ -53556,10 +55522,8 @@ static JSValue js_dataview_setValue(JSContext *ctx, v = bswap32(v); put_u32(ptr, v); break; -#ifdef CONFIG_BIGNUM case JS_CLASS_BIG_INT64_ARRAY: case JS_CLASS_BIG_UINT64_ARRAY: -#endif case JS_CLASS_FLOAT64_ARRAY: if (is_swap) v64 = bswap64(v64); @@ -53581,10 +55545,8 @@ static const JSCFunctionListEntry js_dataview_proto_funcs[] = { JS_CFUNC_MAGIC_DEF("getUint16", 1, js_dataview_getValue, JS_CLASS_UINT16_ARRAY ), JS_CFUNC_MAGIC_DEF("getInt32", 1, js_dataview_getValue, JS_CLASS_INT32_ARRAY ), JS_CFUNC_MAGIC_DEF("getUint32", 1, js_dataview_getValue, JS_CLASS_UINT32_ARRAY ), -#ifdef CONFIG_BIGNUM JS_CFUNC_MAGIC_DEF("getBigInt64", 1, js_dataview_getValue, JS_CLASS_BIG_INT64_ARRAY ), JS_CFUNC_MAGIC_DEF("getBigUint64", 1, js_dataview_getValue, JS_CLASS_BIG_UINT64_ARRAY ), -#endif JS_CFUNC_MAGIC_DEF("getFloat32", 1, js_dataview_getValue, JS_CLASS_FLOAT32_ARRAY ), JS_CFUNC_MAGIC_DEF("getFloat64", 1, js_dataview_getValue, JS_CLASS_FLOAT64_ARRAY ), JS_CFUNC_MAGIC_DEF("setInt8", 2, js_dataview_setValue, JS_CLASS_INT8_ARRAY ), @@ -53593,10 +55555,8 @@ static const JSCFunctionListEntry js_dataview_proto_funcs[] = { JS_CFUNC_MAGIC_DEF("setUint16", 2, js_dataview_setValue, JS_CLASS_UINT16_ARRAY ), JS_CFUNC_MAGIC_DEF("setInt32", 2, js_dataview_setValue, JS_CLASS_INT32_ARRAY ), JS_CFUNC_MAGIC_DEF("setUint32", 2, js_dataview_setValue, JS_CLASS_UINT32_ARRAY ), -#ifdef CONFIG_BIGNUM JS_CFUNC_MAGIC_DEF("setBigInt64", 2, js_dataview_setValue, JS_CLASS_BIG_INT64_ARRAY ), JS_CFUNC_MAGIC_DEF("setBigUint64", 2, js_dataview_setValue, JS_CLASS_BIG_UINT64_ARRAY ), -#endif JS_CFUNC_MAGIC_DEF("setFloat32", 2, js_dataview_setValue, JS_CLASS_FLOAT32_ARRAY ), JS_CFUNC_MAGIC_DEF("setFloat64", 2, js_dataview_setValue, JS_CLASS_FLOAT64_ARRAY ), JS_PROP_STRING_DEF("[Symbol.toStringTag]", "DataView", JS_PROP_CONFIGURABLE ), @@ -53633,20 +55593,12 @@ static void *js_atomics_get_ptr(JSContext *ctx, if (JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT) goto fail; p = JS_VALUE_GET_OBJ(obj); -#ifdef CONFIG_BIGNUM if (is_waitable) err = (p->class_id != JS_CLASS_INT32_ARRAY && p->class_id != JS_CLASS_BIG_INT64_ARRAY); else err = !(p->class_id >= JS_CLASS_INT8_ARRAY && p->class_id <= JS_CLASS_BIG_UINT64_ARRAY); -#else - if (is_waitable) - err = (p->class_id != JS_CLASS_INT32_ARRAY); - else - err = !(p->class_id >= JS_CLASS_INT8_ARRAY && - p->class_id <= JS_CLASS_UINT32_ARRAY); -#endif if (err) { fail: JS_ThrowTypeError(ctx, "integer TypedArray expected"); @@ -53688,11 +55640,7 @@ static JSValue js_atomics_op(JSContext *ctx, int argc, JSValueConst *argv, int op) { int size_log2; -#ifdef CONFIG_BIGNUM uint64_t v, a, rep_val; -#else - uint32_t v, a, rep_val; -#endif void *ptr; JSValue ret; JSClassID class_id; @@ -53706,7 +55654,6 @@ static JSValue js_atomics_op(JSContext *ctx, if (op == ATOMICS_OP_LOAD) { v = 0; } else { -#ifdef CONFIG_BIGNUM if (size_log2 == 3) { int64_t v64; if (JS_ToBigInt64(ctx, &v64, argv[2])) @@ -53717,9 +55664,7 @@ static JSValue js_atomics_op(JSContext *ctx, return JS_EXCEPTION; rep_val = v64; } - } else -#endif - { + } else { uint32_t v32; if (JS_ToUint32(ctx, &v32, argv[2])) return JS_EXCEPTION; @@ -53736,7 +55681,6 @@ static JSValue js_atomics_op(JSContext *ctx, switch(op | (size_log2 << 3)) { -#ifdef CONFIG_BIGNUM #define OP(op_name, func_name) \ case ATOMICS_OP_ ## op_name | (0 << 3): \ a = func_name((_Atomic(uint8_t) *)ptr, v); \ @@ -53750,18 +55694,7 @@ static JSValue js_atomics_op(JSContext *ctx, case ATOMICS_OP_ ## op_name | (3 << 3): \ a = func_name((_Atomic(uint64_t) *)ptr, v); \ break; -#else -#define OP(op_name, func_name) \ - case ATOMICS_OP_ ## op_name | (0 << 3): \ - a = func_name((_Atomic(uint8_t) *)ptr, v); \ - break; \ - case ATOMICS_OP_ ## op_name | (1 << 3): \ - a = func_name((_Atomic(uint16_t) *)ptr, v); \ - break; \ - case ATOMICS_OP_ ## op_name | (2 << 3): \ - a = func_name((_Atomic(uint32_t) *)ptr, v); \ - break; -#endif + OP(ADD, atomic_fetch_add) OP(AND, atomic_fetch_and) OP(OR, atomic_fetch_or) @@ -53779,11 +55712,9 @@ static JSValue js_atomics_op(JSContext *ctx, case ATOMICS_OP_LOAD | (2 << 3): a = atomic_load((_Atomic(uint32_t) *)ptr); break; -#ifdef CONFIG_BIGNUM case ATOMICS_OP_LOAD | (3 << 3): a = atomic_load((_Atomic(uint64_t) *)ptr); break; -#endif case ATOMICS_OP_COMPARE_EXCHANGE | (0 << 3): { @@ -53806,7 +55737,6 @@ static JSValue js_atomics_op(JSContext *ctx, a = v1; } break; -#ifdef CONFIG_BIGNUM case ATOMICS_OP_COMPARE_EXCHANGE | (3 << 3): { uint64_t v1 = v; @@ -53814,7 +55744,6 @@ static JSValue js_atomics_op(JSContext *ctx, a = v1; } break; -#endif default: abort(); } @@ -53839,14 +55768,12 @@ static JSValue js_atomics_op(JSContext *ctx, case JS_CLASS_UINT32_ARRAY: ret = JS_NewUint32(ctx, a); break; -#ifdef CONFIG_BIGNUM case JS_CLASS_BIG_INT64_ARRAY: ret = JS_NewBigInt64(ctx, a); break; case JS_CLASS_BIG_UINT64_ARRAY: ret = JS_NewBigUint64(ctx, a); break; -#endif default: abort(); } @@ -53866,7 +55793,6 @@ static JSValue js_atomics_store(JSContext *ctx, argv[0], argv[1], 0); if (!ptr) return JS_EXCEPTION; -#ifdef CONFIG_BIGNUM if (size_log2 == 3) { int64_t v64; ret = JS_ToBigIntValueFree(ctx, JS_DupValue(ctx, argv[2])); @@ -53879,9 +55805,7 @@ static JSValue js_atomics_store(JSContext *ctx, if (abuf->detached) return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); atomic_store((_Atomic(uint64_t) *)ptr, v64); - } else -#endif - { + } else { uint32_t v; /* XXX: spec, would be simpler to return the written value */ ret = JS_ToIntegerFree(ctx, JS_DupValue(ctx, argv[2])); @@ -53917,11 +55841,7 @@ static JSValue js_atomics_isLockFree(JSContext *ctx, int v, ret; if (JS_ToInt32Sat(ctx, &v, argv[0])) return JS_EXCEPTION; - ret = (v == 1 || v == 2 || v == 4 -#ifdef CONFIG_BIGNUM - || v == 8 -#endif - ); + ret = (v == 1 || v == 2 || v == 4 || v == 8); return JS_NewBool(ctx, ret); } @@ -53953,20 +55873,18 @@ static JSValue js_atomics_wait(JSContext *ctx, argv[0], argv[1], 2); if (!ptr) return JS_EXCEPTION; -#ifdef CONFIG_BIGNUM if (size_log2 == 3) { if (JS_ToBigInt64(ctx, &v, argv[2])) return JS_EXCEPTION; - } else -#endif - { + } else { if (JS_ToInt32(ctx, &v32, argv[2])) return JS_EXCEPTION; v = v32; } if (JS_ToFloat64(ctx, &d, argv[3])) return JS_EXCEPTION; - if (isnan(d) || d > INT64_MAX) + /* must use INT64_MAX + 1 because INT64_MAX cannot be exactly represented as a double */ + if (isnan(d) || d >= 0x1p63) timeout = INT64_MAX; else if (d < 0) timeout = 0; @@ -54144,6 +56062,8 @@ void JS_AddIntrinsicTypedArrays(JSContext *ctx) countof(js_typed_array_base_funcs)); JS_SetConstructor(ctx, typed_array_base_func, typed_array_base_proto); + /* Used to squelch a -Wcast-function-type warning. */ + JSCFunctionType ft = { .generic_magic = js_typed_array_constructor }; for(i = JS_CLASS_UINT8C_ARRAY; i < JS_CLASS_UINT8C_ARRAY + JS_TYPED_ARRAY_COUNT; i++) { JSValue func_obj; char buf[ATOM_GET_STR_BUF_SIZE]; @@ -54156,7 +56076,7 @@ void JS_AddIntrinsicTypedArrays(JSContext *ctx) 0); name = JS_AtomGetStr(ctx, buf, sizeof(buf), JS_ATOM_Uint8ClampedArray + i - JS_CLASS_UINT8C_ARRAY); - func_obj = JS_NewCFunction3(ctx, (JSCFunction *)js_typed_array_constructor, + func_obj = JS_NewCFunction3(ctx, ft.generic, name, 3, JS_CFUNC_constructor_magic, i, typed_array_base_func); JS_NewGlobalCConstructor2(ctx, func_obj, name, ctx->class_proto[i]); @@ -54269,7 +56189,7 @@ void JS_FreeValue(JSContext *ctx, JSValue v) { notifyRefCountDecrease(p); #endif if (--p->ref_count <= 0) { - JS_FreeValueImpl(ctx, v); + __JS_FreeValue(ctx, v); } } } @@ -54280,7 +56200,7 @@ void JS_FreeValueRT(JSRuntime *rt, JSValue v) { notifyRefCountDecrease(p); #endif if (--p->ref_count <= 0) { - JS_FreeValueRTImpl(rt, v); + __JS_FreeValueRT(rt, v); } } } @@ -54301,7 +56221,7 @@ JSValue JS_NewInt64(JSContext *ctx, int64_t val) { if (val == (int32_t)val) { v = JS_NewInt32(ctx, (int32_t)val); } else { - v = JS_NewFloat64Impl(ctx, (double)val); + v = __JS_NewFloat64(ctx, (double)val); } return v; } @@ -54310,29 +56230,29 @@ JSValue JS_NewUint32(JSContext *ctx, uint32_t val) { if (val <= 0x7fffffff) { v = JS_NewInt32(ctx, val); } else { - v = JS_NewFloat64Impl(ctx, val); + v = __JS_NewFloat64(ctx, val); } return v; } -JSValue JS_NewFloat64(JSContext *ctx, double d) { - JSValue v; +JSValue JS_NewFloat64(JSContext *ctx, double d) +{ int32_t val; union { double d; uint64_t u; } u, t; - u.d = d; - val = (int32_t)d; - t.d = val; - /* -0 cannot be represented as integer, so we compare the bit - representation */ - if (u.u == t.u) { - v = JS_MKVAL(JS_TAG_INT, val); - } else { - v = JS_NewFloat64Impl(ctx, d); + if (d >= INT32_MIN && d <= INT32_MAX) { + u.d = d; + val = (int32_t)d; + t.d = val; + /* -0 cannot be represented as integer, so we compare the bit + representation */ + if (u.u == t.u) + return JS_MKVAL(JS_TAG_INT, val); } - return v; + return __JS_NewFloat64(ctx, d); } + JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name, int length) { return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0); @@ -54345,23 +56265,3 @@ JS_BOOL JS_IsRegExp(JSContext *ctx, JSValue val) return FALSE; return JS_VALUE_GET_OBJ(val)->class_id == JS_CLASS_REGEXP; } - -int JS_IsDate(JSValue v) -{ - JSObject *p; - if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT) - return FALSE; - return JS_VALUE_GET_OBJ(v)->class_id == JS_CLASS_DATE; -} - -JSValue JS_NewDate(JSContext *ctx, const char *s) -{ - JSValue dateString = JS_NewString(ctx, s); - JSAtom constrAtom = JS_NewAtom(ctx, "Date"); - JSValue constr = JS_GetGlobalVar(ctx, constrAtom, FALSE); - JSValue date = js_date_constructor(ctx, constr, 1, &dateString); - JS_FreeValue(ctx, constr); - JS_FreeValue(ctx, dateString); - JS_FreeAtom(ctx, constrAtom); - return date; -} diff --git a/src/shared/quickjs/quickjs.diff b/src/shared/quickjs/quickjs.diff deleted file mode 100644 index e87999050..000000000 --- a/src/shared/quickjs/quickjs.diff +++ /dev/null @@ -1,4061 +0,0 @@ -diff --git a/cutils.c b/cutils.c -index a02fb76..1f66fff 100644 ---- a/cutils.c -+++ b/cutils.c -@@ -166,8 +166,7 @@ int dbuf_putstr(DynBuf *s, const char *str) - return dbuf_put(s, (const uint8_t *)str, strlen(str)); - } - --int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, -- const char *fmt, ...) -+int FORMAT_ATTR(2, 3) dbuf_printf(DynBuf *s, const char *fmt, ...) - { - va_list ap; - char buf[128]; -diff --git a/cutils.h b/cutils.h -index 31f7cd8..ee0ce4a 100644 ---- a/cutils.h -+++ b/cutils.h -@@ -28,14 +28,33 @@ - #include <stdlib.h> - #include <inttypes.h> - -+#if defined(_MSC_VER) -+#include <BaseTsd.h> -+typedef SSIZE_T ssize_t; -+#else -+#include <sys/types.h> -+#endif -+ - /* set if CPU is big endian */ - #undef WORDS_BIGENDIAN - -+#ifdef __GNUC__ - #define likely(x) __builtin_expect(!!(x), 1) - #define unlikely(x) __builtin_expect(!!(x), 0) - #define force_inline inline __attribute__((always_inline)) - #define no_inline __attribute__((noinline)) --#define __maybe_unused __attribute__((unused)) -+#define maybe_unused __attribute__((unused)) -+#else -+#define likely(x) (x) -+#define unlikely(x) (x) -+#define force_inline -+#define no_inline -+#define maybe_unused -+#endif -+ -+#ifdef _MSC_VER -+#define alloca _alloca -+#endif - - #define xglue(x, y) x ## y - #define glue(x, y) xglue(x, y) -@@ -114,38 +133,24 @@ static inline int64_t min_int64(int64_t a, int64_t b) - /* WARNING: undefined if a = 0 */ - static inline int clz32(unsigned int a) - { -+#ifdef _MSC_VER -+ return (int) __lzcnt(a); -+#else - return __builtin_clz(a); -+#endif - } - --/* WARNING: undefined if a = 0 */ --static inline int clz64(uint64_t a) --{ -- return __builtin_clzll(a); --} -- --/* WARNING: undefined if a = 0 */ --static inline int ctz32(unsigned int a) --{ -- return __builtin_ctz(a); --} -- --/* WARNING: undefined if a = 0 */ --static inline int ctz64(uint64_t a) --{ -- return __builtin_ctzll(a); --} -- --struct __attribute__((packed)) packed_u64 { -+#pragma pack(push, 1) -+struct packed_u64 { - uint64_t v; - }; -- --struct __attribute__((packed)) packed_u32 { -+struct packed_u32 { - uint32_t v; - }; -- --struct __attribute__((packed)) packed_u16 { -+struct packed_u16 { - uint16_t v; - }; -+#pragma pack(pop) - - static inline uint64_t get_u64(const uint8_t *tab) - { -@@ -262,8 +267,15 @@ static inline int dbuf_put_u64(DynBuf *s, uint64_t val) - { - return dbuf_put(s, (uint8_t *)&val, 8); - } --int __attribute__((format(printf, 2, 3))) dbuf_printf(DynBuf *s, -- const char *fmt, ...); -+ -+#ifdef __GNUC__ -+#define FORMAT_ATTR(x, y) __attribute__((format(printf, x, y))) -+#else -+#define FORMAT_ATTR(x, y) -+#endif -+ -+int FORMAT_ATTR(2, 3) dbuf_printf(DynBuf *s, const char *fmt, ...); -+ - void dbuf_free(DynBuf *s); - static inline BOOL dbuf_error(DynBuf *s) { - return s->error; -diff --git a/libregexp.c b/libregexp.c -index 379bfc7..ad91f78 100644 ---- a/libregexp.c -+++ b/libregexp.c -@@ -271,7 +271,7 @@ static int cr_canonicalize(CharRange *cr) - } - - #ifdef DUMP_REOP --static __maybe_unused void lre_dump_bytecode(const uint8_t *buf, -+static MAYBE_UNUSED void lre_dump_bytecode(const uint8_t *buf, - int buf_len) - { - int pos, len, opcode, bc_len, re_flags, i; -@@ -427,7 +427,7 @@ static void re_emit_op_u16(REParseState *s, int op, uint32_t val) - dbuf_put_u16(&s->byte_code, val); - } - --static int __attribute__((format(printf, 2, 3))) re_parse_error(REParseState *s, const char *fmt, ...) -+static int FORMAT_ATTR(2, 3) re_parse_error(REParseState *s, const char *fmt, ...) - { - va_list ap; - va_start(ap, fmt); -@@ -1472,7 +1472,7 @@ static int re_parse_term(REParseState *s, BOOL is_backward_dir) - default: - parse_class_atom: - c = get_class_atom(s, cr, &p, FALSE); -- if ((int)c < 0) -+ if (c < 0) - return -1; - normal_char: - last_atom_start = s->byte_code.size; -@@ -1924,17 +1924,17 @@ static BOOL is_word_char(uint32_t c) - #define GET_CHAR(c, cptr, cbuf_end) \ - do { \ - if (cbuf_type == 0) { \ -- c = *cptr++; \ -+ (c) = *(cptr)++; \ - } else { \ - uint32_t __c1; \ -- c = *(uint16_t *)cptr; \ -- cptr += 2; \ -- if (c >= 0xd800 && c < 0xdc00 && \ -- cbuf_type == 2 && cptr < cbuf_end) { \ -- __c1 = *(uint16_t *)cptr; \ -+ (c) = *(uint16_t *)(cptr); \ -+ (cptr) += 2; \ -+ if ((c) >= 0xd800 && (c) < 0xdc00 && \ -+ cbuf_type == 2 && (cptr) < (cbuf_end)) { \ -+ __c1 = *(uint16_t *)(cptr); \ - if (__c1 >= 0xdc00 && __c1 < 0xe000) { \ -- c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \ -- cptr += 2; \ -+ (c) = ((((c) & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \ -+ (cptr) += 2; \ - } \ - } \ - } \ -@@ -1943,15 +1943,15 @@ static BOOL is_word_char(uint32_t c) - #define PEEK_CHAR(c, cptr, cbuf_end) \ - do { \ - if (cbuf_type == 0) { \ -- c = cptr[0]; \ -+ (c) = (cptr)[0]; \ - } else { \ - uint32_t __c1; \ -- c = ((uint16_t *)cptr)[0]; \ -- if (c >= 0xd800 && c < 0xdc00 && \ -- cbuf_type == 2 && (cptr + 2) < cbuf_end) { \ -- __c1 = ((uint16_t *)cptr)[1]; \ -+ (c) = ((uint16_t *)(cptr))[0]; \ -+ if ((c) >= 0xd800 && (c) < 0xdc00 && \ -+ cbuf_type == 2 && ((cptr) + 2) < (cbuf_end)) { \ -+ __c1 = ((uint16_t *)(cptr))[1]; \ - if (__c1 >= 0xdc00 && __c1 < 0xe000) { \ -- c = (((c & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \ -+ (c) = ((((c) & 0x3ff) << 10) | (__c1 & 0x3ff)) + 0x10000; \ - } \ - } \ - } \ -@@ -1960,15 +1960,15 @@ static BOOL is_word_char(uint32_t c) - #define PEEK_PREV_CHAR(c, cptr, cbuf_start) \ - do { \ - if (cbuf_type == 0) { \ -- c = cptr[-1]; \ -+ (c) = (cptr)[-1]; \ - } else { \ - uint32_t __c1; \ -- c = ((uint16_t *)cptr)[-1]; \ -- if (c >= 0xdc00 && c < 0xe000 && \ -- cbuf_type == 2 && (cptr - 4) >= cbuf_start) { \ -- __c1 = ((uint16_t *)cptr)[-2]; \ -+ (c) = ((uint16_t *)(cptr))[-1]; \ -+ if ((c) >= 0xdc00 && (c) < 0xe000 && \ -+ cbuf_type == 2 && ((cptr) - 4) >= (cbuf_start)) { \ -+ __c1 = ((uint16_t *)(cptr))[-2]; \ - if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \ -- c = (((__c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; \ -+ (c) = (((__c1 & 0x3ff) << 10) | ((c) & 0x3ff)) + 0x10000; \ - } \ - } \ - } \ -@@ -1977,18 +1977,18 @@ static BOOL is_word_char(uint32_t c) - #define GET_PREV_CHAR(c, cptr, cbuf_start) \ - do { \ - if (cbuf_type == 0) { \ -- cptr--; \ -- c = cptr[0]; \ -+ (cptr)--; \ -+ (c) = (cptr)[0]; \ - } else { \ - uint32_t __c1; \ -- cptr -= 2; \ -- c = ((uint16_t *)cptr)[0]; \ -- if (c >= 0xdc00 && c < 0xe000 && \ -- cbuf_type == 2 && cptr > cbuf_start) { \ -- __c1 = ((uint16_t *)cptr)[-1]; \ -+ (cptr) -= 2; \ -+ (c) = ((uint16_t *)(cptr))[0]; \ -+ if ((c) >= 0xdc00 && (c) < 0xe000 && \ -+ cbuf_type == 2 && (cptr) > (cbuf_start)) { \ -+ __c1 = ((uint16_t *)(cptr))[-1]; \ - if (__c1 >= 0xd800 && __c1 < 0xdc00 ) { \ -- cptr -= 2; \ -- c = (((__c1 & 0x3ff) << 10) | (c & 0x3ff)) + 0x10000; \ -+ (cptr) -= 2; \ -+ (c) = (((__c1 & 0x3ff) << 10) | ((c) & 0x3ff)) + 0x10000; \ - } \ - } \ - } \ -@@ -1997,15 +1997,15 @@ static BOOL is_word_char(uint32_t c) - #define PREV_CHAR(cptr, cbuf_start) \ - do { \ - if (cbuf_type == 0) { \ -- cptr--; \ -+ (cptr)--; \ - } else { \ -- cptr -= 2; \ -+ (cptr) -= 2; \ - if (cbuf_type == 2) { \ -- c = ((uint16_t *)cptr)[0]; \ -- if (c >= 0xdc00 && c < 0xe000 && cptr > cbuf_start) { \ -- c = ((uint16_t *)cptr)[-1]; \ -+ c = ((uint16_t *)(cptr))[0]; \ -+ if (c >= 0xdc00 && c < 0xe000 && (cptr) > (cbuf_start)) { \ -+ c = ((uint16_t *)(cptr))[-1]; \ - if (c >= 0xd800 && c < 0xdc00) \ -- cptr -= 2; \ -+ (cptr) -= 2; \ - } \ - } \ - } \ -@@ -2049,7 +2049,7 @@ typedef struct { - - static int push_state(REExecContext *s, - uint8_t **capture, -- StackInt *stack, size_t stack_len, -+ const StackInt *stack, size_t stack_len, - const uint8_t *pc, const uint8_t *cptr, - REExecStateEnum type, size_t count) - { -diff --git a/libunicode.c b/libunicode.c -index 63c12a0..112da72 100644 ---- a/libunicode.c -+++ b/libunicode.c -@@ -271,7 +271,7 @@ BOOL lre_is_case_ignorable(uint32_t c) - - /* character range */ - --static __maybe_unused void cr_dump(CharRange *cr) -+static maybe_unused void cr_dump(CharRange *cr) - { - int i; - for(i = 0; i < cr->len; i++) -@@ -1315,11 +1315,13 @@ static int unicode_prop_ops(CharRange *cr, ...) - } - } - done: -+ va_end(ap); - assert(stack_len == 1); - ret = cr_copy(cr, &stack[0]); - cr_free(&stack[0]); - return ret; - fail: -+ va_end(ap); - for(i = 0; i < stack_len; i++) - cr_free(&stack[i]); - return -1; -diff --git a/list.h b/list.h -index 0a1bc5a..e7f51a9 100644 ---- a/list.h -+++ b/list.h -@@ -46,7 +46,7 @@ static inline void init_list_head(struct list_head *head) - } - - /* insert 'el' between 'prev' and 'next' */ --static inline void __list_add(struct list_head *el, -+static inline void list_add_impl(struct list_head *el, - struct list_head *prev, struct list_head *next) - { - prev->next = el; -@@ -58,13 +58,13 @@ static inline void __list_add(struct list_head *el, - /* add 'el' at the head of the list 'head' (= after element head) */ - static inline void list_add(struct list_head *el, struct list_head *head) - { -- __list_add(el, head, head->next); -+ list_add_impl(el, head, head->next); - } - - /* add 'el' at the end of the list 'head' (= before element head) */ - static inline void list_add_tail(struct list_head *el, struct list_head *head) - { -- __list_add(el, head->prev, head); -+ list_add_impl(el, head->prev, head); - } - - static inline void list_del(struct list_head *el) -diff --git a/quickjs.c b/quickjs.c -index 7916013..f90fb9e 100644 ---- a/quickjs.c -+++ b/quickjs.c -@@ -28,7 +28,9 @@ - #include <inttypes.h> - #include <string.h> - #include <assert.h> -+#ifndef _MSC_VER - #include <sys/time.h> -+#endif - #include <time.h> - #include <fenv.h> - #include <math.h> -@@ -40,6 +42,18 @@ - #include <malloc_np.h> - #endif - -+#ifdef _MSC_VER -+#include <intrin.h> -+#include <windows.h> -+#endif -+ -+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \ -+ || defined(__APPLE__) -+#include <xlocale.h> -+#else -+#include <locale.h> -+#endif -+ - #include "cutils.h" - #include "list.h" - #include "quickjs.h" -@@ -48,9 +62,9 @@ - #include "libbf.h" - #endif - --#define OPTIMIZE 1 -+#define OPTIMIZE 0 - #define SHORT_OPCODES 1 --#if defined(EMSCRIPTEN) -+#if defined(EMSCRIPTEN) || defined(_MSC_VER) - #define DIRECT_DISPATCH 0 - #else - #define DIRECT_DISPATCH 1 -@@ -69,7 +83,7 @@ - - /* define to include Atomics.* operations which depend on the OS - threads */ --#if !defined(EMSCRIPTEN) -+#if !defined(EMSCRIPTEN) && !defined(_MSC_VER) - #define CONFIG_ATOMICS - #endif - -@@ -78,7 +92,6 @@ - #define CONFIG_STACK_CHECK - #endif - -- - /* dump object free */ - //#define DUMP_FREE - //#define DUMP_CLOSURE -@@ -115,6 +128,25 @@ - #include <errno.h> - #endif - -+static double safe_strtod(const char *restrict nptr, char **restrict endptr) -+{ -+#if defined(_MSC_VER) || defined(__MINGW32__) -+ _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); -+ setlocale(LC_NUMERIC, "C"); -+#else -+ const locale_t tempLoc = newlocale(LC_NUMERIC_MASK, "C", 0); -+ uselocale(tempLoc); -+#endif -+ double d = strtod(nptr, endptr); -+#if defined(_MSC_VER) || defined(__MINGW32__) -+ _configthreadlocale(_DISABLE_PER_THREAD_LOCALE); -+#else -+ uselocale(LC_GLOBAL_LOCALE); -+ freelocale(tempLoc); -+#endif -+ return d; -+} -+ - enum { - /* classid tag */ /* union usage | properties */ - JS_CLASS_OBJECT = 1, /* must be first */ -@@ -204,7 +236,11 @@ typedef enum JSErrorEnum { - #define JS_STACK_SIZE_MAX 65534 - #define JS_STRING_LEN_MAX ((1 << 30) - 1) - --#define __exception __attribute__((warn_unused_result)) -+#ifdef __GNUC__ -+#define warn_unused __attribute__((warn_unused_result)) -+#else -+#define warn_unused -+#endif - - typedef struct JSShape JSShape; - typedef struct JSString JSString; -@@ -362,8 +398,8 @@ typedef struct JSVarRef { - union { - JSGCObjectHeader header; /* must come first */ - struct { -- int __gc_ref_count; /* corresponds to header.ref_count */ -- uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ -+ int _gc_ref_count; /* corresponds to header.ref_count */ -+ uint8_t _gc_mark; /* corresponds to header.mark/gc_obj_type */ - - /* 0 : the JSVarRef is on the stack. header.link is an element - of JSStackFrame.var_ref_list. -@@ -455,8 +491,12 @@ struct JSContext { - /* if NULL, eval is not supported */ - JSValue (*eval_internal)(JSContext *ctx, JSValueConst this_obj, - const char *input, size_t input_len, -- const char *filename, int flags, int scope_idx); -+ const char *filename, int line, int flags, int scope_idx); - void *user_opaque; -+ ScopeLookup *scopeLookup; -+ FoundUndefinedHandler *handleUndefined; -+ FunctionEnteredHandler *handleFunctionEntered; -+ FunctionExitedHandler *handleFunctionExited; - }; - - typedef union JSFloat64Union { -@@ -867,8 +907,8 @@ struct JSObject { - union { - JSGCObjectHeader header; - struct { -- int __gc_ref_count; /* corresponds to header.ref_count */ -- uint8_t __gc_mark; /* corresponds to header.mark/gc_obj_type */ -+ int _gc_ref_count; /* corresponds to header.ref_count */ -+ uint8_t _gc_mark; /* corresponds to header.mark/gc_obj_type */ - - uint8_t extensible : 1; - uint8_t free_mark : 1; /* only used when freeing objects with cycles */ -@@ -950,7 +990,7 @@ struct JSObject { - /* byte sizes: 40/48/72 */ - }; - enum { -- __JS_ATOM_NULL = JS_ATOM_NULL, -+ JS_ATOM_NULL_ = JS_ATOM_NULL, - #define DEF(name, str) JS_ATOM_ ## name, - #include "quickjs-atom.h" - #undef DEF -@@ -996,7 +1036,7 @@ enum OPCodeEnum { - }; - - static int JS_InitAtoms(JSRuntime *rt); --static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, -+static JSAtom JS_NewAtomInitImpl(JSRuntime *rt, const char *str, int len, - int atom_type); - static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p); - static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b); -@@ -1017,24 +1057,23 @@ static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_o - int argc, JSValueConst *argv); - static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, - int argc, JSValueConst *argv); --static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, -+static warn_unused int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, - JSValue val, BOOL is_array_ctor); - static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, - JSValueConst val, int flags, int scope_idx); --JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); --static __maybe_unused void JS_DumpAtoms(JSRuntime *rt); --static __maybe_unused void JS_DumpString(JSRuntime *rt, -+static maybe_unused void JS_DumpAtoms(JSRuntime *rt); -+static maybe_unused void JS_DumpString(JSRuntime *rt, - const JSString *p); --static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt); --static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p); --static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p); --static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, -+static maybe_unused void JS_DumpObjectHeader(JSRuntime *rt); -+static maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p); -+static maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p); -+static maybe_unused void JS_DumpValueShort(JSRuntime *rt, - JSValueConst val); --static __maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val); --static __maybe_unused void JS_PrintValue(JSContext *ctx, -+static maybe_unused void JS_DumpValue(JSContext *ctx, JSValueConst val); -+static maybe_unused void JS_PrintValue(JSContext *ctx, - const char *str, - JSValueConst val); --static __maybe_unused void JS_DumpShapes(JSRuntime *rt); -+static maybe_unused void JS_DumpShapes(JSRuntime *rt); - static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int magic); - static void js_array_finalizer(JSRuntime *rt, JSValue val); -@@ -1148,7 +1187,6 @@ static JSValue JS_ToBigDecimalFree(JSContext *ctx, JSValue val, - BOOL allow_null_or_undefined); - static bfdec_t *JS_ToBigDecimal(JSContext *ctx, JSValueConst val); - #endif --JSValue JS_ThrowOutOfMemory(JSContext *ctx); - static JSValue JS_ThrowTypeErrorRevokedProxy(JSContext *ctx); - static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj); - static int js_proxy_setPrototypeOf(JSContext *ctx, JSValueConst obj, -@@ -1187,7 +1225,7 @@ static void js_async_function_resolve_mark(JSRuntime *rt, JSValueConst val, - JS_MarkFunc *mark_func); - static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, - const char *input, size_t input_len, -- const char *filename, int flags, int scope_idx); -+ const char *filename, int line, int flags, int scope_idx); - static void js_free_module_def(JSContext *ctx, JSModuleDef *m); - static void js_mark_module_def(JSRuntime *rt, JSModuleDef *m, - JS_MarkFunc *mark_func); -@@ -1197,7 +1235,7 @@ static void free_var_ref(JSRuntime *rt, JSVarRef *var_ref); - static JSValue js_new_promise_capability(JSContext *ctx, - JSValue *resolving_funcs, - JSValueConst ctor); --static __exception int perform_promise_then(JSContext *ctx, -+static warn_unused int perform_promise_then(JSContext *ctx, - JSValueConst promise, - JSValueConst *resolve_reject, - JSValueConst *cap_resolving_funcs); -@@ -1222,9 +1260,9 @@ static void js_free_shape_null(JSRuntime *rt, JSShape *sh); - static int js_shape_prepare_update(JSContext *ctx, JSObject *p, - JSShapeProperty **pprs); - static int init_shape_hash(JSRuntime *rt); --static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, -+static warn_unused int js_get_length32(JSContext *ctx, uint32_t *pres, - JSValueConst obj); --static __exception int js_get_length64(JSContext *ctx, int64_t *pres, -+static warn_unused int js_get_length64(JSContext *ctx, int64_t *pres, - JSValueConst obj); - static void free_arg_list(JSContext *ctx, JSValue *tab, uint32_t len); - static JSValue *build_arg_list(JSContext *ctx, uint32_t *plen, -@@ -1582,10 +1620,27 @@ static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) - return FALSE; - } - #else --/* Note: OS and CPU dependent */ -+// Uses code from LLVM project. - static inline uintptr_t js_get_stack_pointer(void) - { -+#ifdef _MSC_VER -+ return (uintptr_t) _AddressOfReturnAddress(); -+#elif defined __has_builtin -+#if __has_builtin(__builtin_frame_address) - return (uintptr_t) __builtin_frame_address(0); -+#endif -+#elif defined __GNUC__ -+ return (uintptr_t) __builtin_frame_address(0); -+#else -+ char CharOnStack = 0; -+ // The volatile store here is intended to escape the local variable, to -+ // prevent the compiler from optimizing CharOnStack into anything other -+ // than a char on the stack. -+ // -+ // Tested on: MSVC 2015 - 2019, GCC 4.9 - 9, Clang 3.2 - 9, ICC 13 - 19. -+ char *volatile Ptr = &CharOnStack; -+ return (uintptr_t) Ptr; -+#endif - } - - static inline BOOL js_check_stack_overflow(JSRuntime *rt, size_t alloca_size) -@@ -1980,6 +2035,7 @@ void JS_FreeRuntime(JSRuntime *rt) - printf("Secondary object leaks: %d\n", count); - } - #endif -+ fflush(stdout); - assert(list_empty(&rt->gc_obj_list)); - - /* free the classes */ -@@ -2386,7 +2442,7 @@ static inline BOOL is_math_mode(JSContext *ctx) - /* return the max count from the hash size */ - #define JS_ATOM_COUNT_RESIZE(n) ((n) * 2) - --static inline BOOL __JS_AtomIsConst(JSAtom v) -+static inline BOOL JS_AtomIsConst(JSAtom v) - { - #if defined(DUMP_LEAKS) && DUMP_LEAKS > 1 - return (int32_t)v <= 0; -@@ -2395,17 +2451,17 @@ static inline BOOL __JS_AtomIsConst(JSAtom v) - #endif - } - --static inline BOOL __JS_AtomIsTaggedInt(JSAtom v) -+static inline BOOL JS_AtomIsTaggedInt(JSAtom v) - { - return (v & JS_ATOM_TAG_INT) != 0; - } - --static inline JSAtom __JS_AtomFromUInt32(uint32_t v) -+static inline JSAtom JS_AtomFromUInt32(uint32_t v) - { - return v | JS_ATOM_TAG_INT; - } - --static inline uint32_t __JS_AtomToUInt32(JSAtom atom) -+static inline uint32_t JS_AtomToUInt32(JSAtom atom) - { - return atom & ~JS_ATOM_TAG_INT; - } -@@ -2485,7 +2541,7 @@ static uint32_t hash_string(const JSString *str, uint32_t h) - return h; - } - --static __maybe_unused void JS_DumpString(JSRuntime *rt, -+static maybe_unused void JS_DumpString(JSRuntime *rt, - const JSString *p) - { - int i, c, sep; -@@ -2517,7 +2573,7 @@ static __maybe_unused void JS_DumpString(JSRuntime *rt, - putchar(sep); - } - --static __maybe_unused void JS_DumpAtoms(JSRuntime *rt) -+static maybe_unused void JS_DumpAtoms(JSRuntime *rt) - { - JSAtomStruct *p; - int h, i; -@@ -2604,7 +2660,7 @@ static int JS_InitAtoms(JSRuntime *rt) - else - atom_type = JS_ATOM_TYPE_STRING; - len = strlen(p); -- if (__JS_NewAtomInit(rt, p, len, atom_type) == JS_ATOM_NULL) -+ if (JS_NewAtomInitImpl(rt, p, len, atom_type) == JS_ATOM_NULL) - return -1; - p = p + len + 1; - } -@@ -2615,7 +2671,7 @@ static JSAtom JS_DupAtomRT(JSRuntime *rt, JSAtom v) - { - JSAtomStruct *p; - -- if (!__JS_AtomIsConst(v)) { -+ if (!JS_AtomIsConst(v)) { - p = rt->atom_array[v]; - p->header.ref_count++; - } -@@ -2627,7 +2683,7 @@ JSAtom JS_DupAtom(JSContext *ctx, JSAtom v) - JSRuntime *rt; - JSAtomStruct *p; - -- if (!__JS_AtomIsConst(v)) { -+ if (!JS_AtomIsConst(v)) { - rt = ctx->rt; - p = rt->atom_array[v]; - p->header.ref_count++; -@@ -2641,7 +2697,7 @@ static JSAtomKindEnum JS_AtomGetKind(JSContext *ctx, JSAtom v) - JSAtomStruct *p; - - rt = ctx->rt; -- if (__JS_AtomIsTaggedInt(v)) -+ if (JS_AtomIsTaggedInt(v)) - return JS_ATOM_KIND_STRING; - p = rt->atom_array[v]; - switch(p->atom_type) { -@@ -2687,7 +2743,7 @@ static JSAtom js_get_atom_index(JSRuntime *rt, JSAtomStruct *p) - - /* string case (internal). Return JS_ATOM_NULL if error. 'str' is - freed. */ --static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) -+static JSAtom JS_NewAtomImpl(JSRuntime *rt, JSString *str, int atom_type) - { - uint32_t h, h1, i; - JSAtomStruct *p; -@@ -2702,7 +2758,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) - /* str is the atom, return its index */ - i = js_get_atom_index(rt, str); - /* reduce string refcount and increase atom's unless constant */ -- if (__JS_AtomIsConst(i)) -+ if (JS_AtomIsConst(i)) - str->header.ref_count--; - return i; - } -@@ -2718,7 +2774,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) - p->atom_type == atom_type && - p->len == len && - js_string_memcmp(p, str, len) == 0) { -- if (!__JS_AtomIsConst(i)) -+ if (!JS_AtomIsConst(i)) - p->header.ref_count++; - goto done; - } -@@ -2843,7 +2899,7 @@ static JSAtom __JS_NewAtom(JSRuntime *rt, JSString *str, int atom_type) - } - - /* only works with zero terminated 8 bit strings */ --static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, -+static JSAtom JS_NewAtomInitImpl(JSRuntime *rt, const char *str, int len, - int atom_type) - { - JSString *p; -@@ -2852,10 +2908,10 @@ static JSAtom __JS_NewAtomInit(JSRuntime *rt, const char *str, int len, - return JS_ATOM_NULL; - memcpy(p->u.str8, str, len); - p->u.str8[len] = '\0'; -- return __JS_NewAtom(rt, p, atom_type); -+ return JS_NewAtomImpl(rt, p, atom_type); - } - --static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, -+static JSAtom JS_FindAtom(JSRuntime *rt, const char *str, size_t len, - int atom_type) - { - uint32_t h, h1, i; -@@ -2872,7 +2928,7 @@ static JSAtom __JS_FindAtom(JSRuntime *rt, const char *str, size_t len, - p->len == len && - p->is_wide_char == 0 && - memcmp(p->u.str8, str, len) == 0) { -- if (!__JS_AtomIsConst(i)) -+ if (!JS_AtomIsConst(i)) - p->header.ref_count++; - return i; - } -@@ -2924,7 +2980,7 @@ static void JS_FreeAtomStruct(JSRuntime *rt, JSAtomStruct *p) - assert(rt->atom_count >= 0); - } - --static void __JS_FreeAtom(JSRuntime *rt, uint32_t i) -+static void JS_FreeAtomImpl(JSRuntime *rt, uint32_t i) - { - JSAtomStruct *p; - -@@ -2942,11 +2998,11 @@ static JSAtom JS_NewAtomStr(JSContext *ctx, JSString *p) - if (is_num_string(&n, p)) { - if (n <= JS_ATOM_MAX_INT) { - js_free_string(rt, p); -- return __JS_AtomFromUInt32(n); -+ return JS_AtomFromUInt32(n); - } - } - /* XXX: should generate an exception */ -- return __JS_NewAtom(rt, p, JS_ATOM_TYPE_STRING); -+ return JS_NewAtomImpl(rt, p, JS_ATOM_TYPE_STRING); - } - - JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len) -@@ -2954,7 +3010,7 @@ JSAtom JS_NewAtomLen(JSContext *ctx, const char *str, size_t len) - JSValue val; - - if (len == 0 || !is_digit(*str)) { -- JSAtom atom = __JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING); -+ JSAtom atom = JS_FindAtom(ctx->rt, str, len, JS_ATOM_TYPE_STRING); - if (atom) - return atom; - } -@@ -2972,7 +3028,7 @@ JSAtom JS_NewAtom(JSContext *ctx, const char *str) - JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n) - { - if (n <= JS_ATOM_MAX_INT) { -- return __JS_AtomFromUInt32(n); -+ return JS_AtomFromUInt32(n); - } else { - char buf[11]; - JSValue val; -@@ -2980,7 +3036,7 @@ JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n) - val = JS_NewString(ctx, buf); - if (JS_IsException(val)) - return JS_ATOM_NULL; -- return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), -+ return JS_NewAtomImpl(ctx->rt, JS_VALUE_GET_STRING(val), - JS_ATOM_TYPE_STRING); - } - } -@@ -2988,7 +3044,7 @@ JSAtom JS_NewAtomUInt32(JSContext *ctx, uint32_t n) - static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n) - { - if ((uint64_t)n <= JS_ATOM_MAX_INT) { -- return __JS_AtomFromUInt32((uint32_t)n); -+ return JS_AtomFromUInt32((uint32_t)n); - } else { - char buf[24]; - JSValue val; -@@ -2996,7 +3052,7 @@ static JSAtom JS_NewAtomInt64(JSContext *ctx, int64_t n) - val = JS_NewString(ctx, buf); - if (JS_IsException(val)) - return JS_ATOM_NULL; -- return __JS_NewAtom(ctx->rt, JS_VALUE_GET_STRING(val), -+ return JS_NewAtomImpl(ctx->rt, JS_VALUE_GET_STRING(val), - JS_ATOM_TYPE_STRING); - } - } -@@ -3006,7 +3062,7 @@ static JSValue JS_NewSymbol(JSContext *ctx, JSString *p, int atom_type) - { - JSRuntime *rt = ctx->rt; - JSAtom atom; -- atom = __JS_NewAtom(rt, p, atom_type); -+ atom = JS_NewAtomImpl(rt, p, atom_type); - if (atom == JS_ATOM_NULL) - return JS_ThrowOutOfMemory(ctx); - return JS_MKPTR(JS_TAG_SYMBOL, rt->atom_array[atom]); -@@ -3019,7 +3075,7 @@ static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr, - JSRuntime *rt = ctx->rt; - JSString *p; - -- assert(!__JS_AtomIsTaggedInt(descr)); -+ assert(!JS_AtomIsTaggedInt(descr)); - assert(descr < rt->atom_size); - p = rt->atom_array[descr]; - JS_DupValue(ctx, JS_MKPTR(JS_TAG_STRING, p)); -@@ -3032,8 +3088,8 @@ static JSValue JS_NewSymbolFromAtom(JSContext *ctx, JSAtom descr, - static const char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, - JSAtom atom) - { -- if (__JS_AtomIsTaggedInt(atom)) { -- snprintf(buf, buf_size, "%u", __JS_AtomToUInt32(atom)); -+ if (JS_AtomIsTaggedInt(atom)) { -+ snprintf(buf, buf_size, "%u", JS_AtomToUInt32(atom)); - } else { - JSAtomStruct *p; - assert(atom < rt->atom_size); -@@ -3083,12 +3139,12 @@ static const char *JS_AtomGetStr(JSContext *ctx, char *buf, int buf_size, JSAtom - return JS_AtomGetStrRT(ctx->rt, buf, buf_size, atom); - } - --static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string) -+static JSValue JS_AtomToValueImpl(JSContext *ctx, JSAtom atom, BOOL force_string) - { - char buf[ATOM_GET_STR_BUF_SIZE]; - -- if (__JS_AtomIsTaggedInt(atom)) { -- snprintf(buf, sizeof(buf), "%u", __JS_AtomToUInt32(atom)); -+ if (JS_AtomIsTaggedInt(atom)) { -+ snprintf(buf, sizeof(buf), "%u", JS_AtomToUInt32(atom)); - return JS_NewString(ctx, buf); - } else { - JSRuntime *rt = ctx->rt; -@@ -3112,20 +3168,20 @@ static JSValue __JS_AtomToValue(JSContext *ctx, JSAtom atom, BOOL force_string) - - JSValue JS_AtomToValue(JSContext *ctx, JSAtom atom) - { -- return __JS_AtomToValue(ctx, atom, FALSE); -+ return JS_AtomToValueImpl(ctx, atom, FALSE); - } - - JSValue JS_AtomToString(JSContext *ctx, JSAtom atom) - { -- return __JS_AtomToValue(ctx, atom, TRUE); -+ return JS_AtomToValueImpl(ctx, atom, TRUE); - } - - /* return TRUE if the atom is an array index (i.e. 0 <= index <= - 2^32-2 and return its value */ - static BOOL JS_AtomIsArrayIndex(JSContext *ctx, uint32_t *pval, JSAtom atom) - { -- if (__JS_AtomIsTaggedInt(atom)) { -- *pval = __JS_AtomToUInt32(atom); -+ if (JS_AtomIsTaggedInt(atom)) { -+ *pval = JS_AtomToUInt32(atom); - return TRUE; - } else { - JSRuntime *rt = ctx->rt; -@@ -3156,8 +3212,8 @@ static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) - int c, len, ret; - JSValue num, str; - -- if (__JS_AtomIsTaggedInt(atom)) -- return JS_NewInt32(ctx, __JS_AtomToUInt32(atom)); -+ if (JS_AtomIsTaggedInt(atom)) -+ return JS_NewInt32(ctx, JS_AtomToUInt32(atom)); - assert(atom < rt->atom_size); - p1 = rt->atom_array[atom]; - if (p1->atom_type != JS_ATOM_TYPE_STRING) -@@ -3199,7 +3255,7 @@ static JSValue JS_AtomIsNumericIndex1(JSContext *ctx, JSAtom atom) - /* -0 case is specific */ - if (c == '0' && len == 2) { - minus_zero: -- return __JS_NewFloat64(ctx, -0.0); -+ return JS_NewFloat64Impl(ctx, -0.0); - } - } - if (!is_num(c)) { -@@ -3244,14 +3300,14 @@ static int JS_AtomIsNumericIndex(JSContext *ctx, JSAtom atom) - - void JS_FreeAtom(JSContext *ctx, JSAtom v) - { -- if (!__JS_AtomIsConst(v)) -- __JS_FreeAtom(ctx->rt, v); -+ if (!JS_AtomIsConst(v)) -+ JS_FreeAtomImpl(ctx->rt, v); - } - - void JS_FreeAtomRT(JSRuntime *rt, JSAtom v) - { -- if (!__JS_AtomIsConst(v)) -- __JS_FreeAtom(rt, v); -+ if (!JS_AtomIsConst(v)) -+ JS_FreeAtomImpl(rt, v); - } - - /* return TRUE if 'v' is a symbol with a string description */ -@@ -3261,7 +3317,7 @@ static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v) - JSAtomStruct *p; - - rt = ctx->rt; -- if (__JS_AtomIsTaggedInt(v)) -+ if (JS_AtomIsTaggedInt(v)) - return FALSE; - p = rt->atom_array[v]; - return (((p->atom_type == JS_ATOM_TYPE_SYMBOL && -@@ -3270,7 +3326,7 @@ static BOOL JS_AtomSymbolHasDescription(JSContext *ctx, JSAtom v) - !(p->len == 0 && p->is_wide_char != 0)); - } - --static __maybe_unused void print_atom(JSContext *ctx, JSAtom atom) -+static maybe_unused void print_atom(JSContext *ctx, JSAtom atom) - { - char buf[ATOM_GET_STR_BUF_SIZE]; - const char *p; -@@ -3445,9 +3501,9 @@ int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def) - JSAtom name; - - len = strlen(class_def->class_name); -- name = __JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); -+ name = JS_FindAtom(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); - if (name == JS_ATOM_NULL) { -- name = __JS_NewAtomInit(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); -+ name = JS_NewAtomInitImpl(rt, class_def->class_name, len, JS_ATOM_TYPE_STRING); - if (name == JS_ATOM_NULL) - return -1; - } -@@ -4614,7 +4670,7 @@ static int add_shape_property(JSContext *ctx, JSShape **psh, - pr = &prop[sh->prop_count++]; - pr->atom = JS_DupAtom(ctx, atom); - pr->flags = prop_flags; -- sh->has_small_array_index |= __JS_AtomIsTaggedInt(atom); -+ sh->has_small_array_index |= JS_AtomIsTaggedInt(atom); - /* add in hash table */ - hash_mask = sh->prop_hash_mask; - h = atom & hash_mask; -@@ -4675,7 +4731,7 @@ static JSShape *find_hashed_shape_prop(JSRuntime *rt, JSShape *sh, - return NULL; - } - --static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh) -+static maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh) - { - char atom_buf[ATOM_GET_STR_BUF_SIZE]; - int j; -@@ -4691,7 +4747,7 @@ static __maybe_unused void JS_DumpShape(JSRuntime *rt, int i, JSShape *sh) - printf("\n"); - } - --static __maybe_unused void JS_DumpShapes(JSRuntime *rt) -+static maybe_unused void JS_DumpShapes(JSRuntime *rt) - { - int i; - JSShape *sh; -@@ -5471,7 +5527,7 @@ static void free_zero_refcount(JSRuntime *rt) - } - - /* called with the ref_count of 'v' reaches zero. */ --void __JS_FreeValueRT(JSRuntime *rt, JSValue v) -+static void JS_FreeValueRTImpl(JSRuntime *rt, JSValue v) - { - uint32_t tag = JS_VALUE_GET_TAG(v); - -@@ -5546,9 +5602,9 @@ void __JS_FreeValueRT(JSRuntime *rt, JSValue v) - } - } - --void __JS_FreeValue(JSContext *ctx, JSValue v) -+static void JS_FreeValueImpl(JSContext *ctx, JSValue v) - { -- __JS_FreeValueRT(ctx->rt, v); -+ JS_FreeValueRTImpl(ctx->rt, v); - } - - /* garbage collection */ -@@ -6469,7 +6525,7 @@ static const char *get_func_name(JSContext *ctx, JSValueConst func) - - /* if filename != NULL, an additional level is added with the filename - and line number information (used for parse error). */ --static void build_backtrace(JSContext *ctx, JSValueConst error_obj, -+void build_backtrace(JSContext *ctx, JSValueConst error_obj, - const char *filename, int line_num, - int backtrace_flags) - { -@@ -6569,7 +6625,7 @@ JSValue JS_NewError(JSContext *ctx) - static JSValue JS_ThrowError2(JSContext *ctx, JSErrorEnum error_num, - const char *fmt, va_list ap, BOOL add_backtrace) - { -- char buf[256]; -+ char buf[8192]; - JSValue obj, ret; - - vsnprintf(buf, sizeof(buf), fmt, ap); -@@ -6604,7 +6660,7 @@ static JSValue JS_ThrowError(JSContext *ctx, JSErrorEnum error_num, - return JS_ThrowError2(ctx, error_num, fmt, ap, add_backtrace); - } - --JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...) -+JSValue FORMAT_ATTR(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...) - { - JSValue val; - va_list ap; -@@ -6615,7 +6671,7 @@ JSValue __attribute__((format(printf, 2, 3))) JS_ThrowSyntaxError(JSContext *ctx - return val; - } - --JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...) -+JSValue FORMAT_ATTR(2, 3) JS_ThrowTypeError(JSContext *ctx, const char *fmt, ...) - { - JSValue val; - va_list ap; -@@ -6626,7 +6682,7 @@ JSValue __attribute__((format(printf, 2, 3))) JS_ThrowTypeError(JSContext *ctx, - return val; - } - --static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...) -+static int FORMAT_ATTR(3, 4) JS_ThrowTypeErrorOrFalse(JSContext *ctx, int flags, const char *fmt, ...) - { - va_list ap; - -@@ -6642,7 +6698,7 @@ static int __attribute__((format(printf, 3, 4))) JS_ThrowTypeErrorOrFalse(JSCont - } - - /* never use it directly */ --static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) -+static JSValue FORMAT_ATTR(3, 4) JS_ThrowTypeErrorAtomImpl(JSContext *ctx, JSAtom atom, const char *fmt, ...) - { - char buf[ATOM_GET_STR_BUF_SIZE]; - return JS_ThrowTypeError(ctx, fmt, -@@ -6650,7 +6706,7 @@ static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowTypeErrorAtom(JSC - } - - /* never use it directly */ --static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(JSContext *ctx, JSAtom atom, const char *fmt, ...) -+static JSValue FORMAT_ATTR(3, 4) JS_ThrowSyntaxErrorAtomImpl(JSContext *ctx, JSAtom atom, const char *fmt, ...) - { - char buf[ATOM_GET_STR_BUF_SIZE]; - return JS_ThrowSyntaxError(ctx, fmt, -@@ -6659,8 +6715,8 @@ static JSValue __attribute__((format(printf, 3, 4))) __JS_ThrowSyntaxErrorAtom(J - - /* %s is replaced by 'atom'. The macro is used so that gcc can check - the format string. */ --#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) __JS_ThrowTypeErrorAtom(ctx, atom, fmt, "") --#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) __JS_ThrowSyntaxErrorAtom(ctx, atom, fmt, "") -+#define JS_ThrowTypeErrorAtom(ctx, fmt, atom) JS_ThrowTypeErrorAtomImpl(ctx, atom, fmt, "") -+#define JS_ThrowSyntaxErrorAtom(ctx, fmt, atom) JS_ThrowSyntaxErrorAtomImpl(ctx, atom, fmt, "") - - static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom) - { -@@ -6673,7 +6729,7 @@ static int JS_ThrowTypeErrorReadOnly(JSContext *ctx, int flags, JSAtom atom) - } - } - --JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...) -+JSValue FORMAT_ATTR(2, 3) JS_ThrowReferenceError(JSContext *ctx, const char *fmt, ...) - { - JSValue val; - va_list ap; -@@ -6684,7 +6740,7 @@ JSValue __attribute__((format(printf, 2, 3))) JS_ThrowReferenceError(JSContext * - return val; - } - --JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...) -+JSValue FORMAT_ATTR(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fmt, ...) - { - JSValue val; - va_list ap; -@@ -6695,7 +6751,7 @@ JSValue __attribute__((format(printf, 2, 3))) JS_ThrowRangeError(JSContext *ctx, - return val; - } - --JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...) -+JSValue FORMAT_ATTR(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...) - { - JSValue val; - va_list ap; -@@ -6770,7 +6826,7 @@ static JSValue JS_ThrowTypeErrorInvalidClass(JSContext *ctx, int class_id) - return JS_ThrowTypeErrorAtom(ctx, "%s object expected", name); - } - --static no_inline __exception int __js_poll_interrupts(JSContext *ctx) -+static no_inline warn_unused int js_poll_interrupts_impl(JSContext *ctx) - { - JSRuntime *rt = ctx->rt; - ctx->interrupt_counter = JS_INTERRUPT_COUNTER_INIT; -@@ -6785,10 +6841,10 @@ static no_inline __exception int __js_poll_interrupts(JSContext *ctx) - return 0; - } - --static inline __exception int js_poll_interrupts(JSContext *ctx) -+static inline warn_unused int js_poll_interrupts(JSContext *ctx) - { - if (unlikely(--ctx->interrupt_counter <= 0)) { -- return __js_poll_interrupts(ctx); -+ return js_poll_interrupts_impl(ctx); - } else { - return 0; - } -@@ -7092,9 +7148,9 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, - case JS_TAG_STRING: - { - JSString *p1 = JS_VALUE_GET_STRING(obj); -- if (__JS_AtomIsTaggedInt(prop)) { -+ if (JS_AtomIsTaggedInt(prop)) { - uint32_t idx, ch; -- idx = __JS_AtomToUInt32(prop); -+ idx = JS_AtomToUInt32(prop); - if (idx < p1->len) { - if (p1->is_wide_char) - ch = p1->u.str16[idx]; -@@ -7144,14 +7200,16 @@ JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, - continue; - } - } else { -+ if (JS_IsUndefined(pr->u.value) && ctx->handleUndefined) -+ ctx->handleUndefined(ctx); - return JS_DupValue(ctx, pr->u.value); - } - } - if (unlikely(p->is_exotic)) { - /* exotic behaviors */ - if (p->fast_array) { -- if (__JS_AtomIsTaggedInt(prop)) { -- uint32_t idx = __JS_AtomToUInt32(prop); -+ if (JS_AtomIsTaggedInt(prop)) { -+ uint32_t idx = JS_AtomToUInt32(prop); - if (idx < p->u.array.count) { - /* we avoid duplicating the code */ - return JS_GetPropertyUint32(ctx, JS_MKPTR(JS_TAG_OBJECT, p), idx); -@@ -7242,7 +7300,7 @@ static int JS_DefinePrivateField(JSContext *ctx, JSValueConst obj, - JS_ThrowTypeErrorNotASymbol(ctx); - goto fail; - } -- prop = js_symbol_to_atom(ctx, (JSValue)name); -+ prop = js_symbol_to_atom(ctx, name); - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, prop); - if (prs) { -@@ -7273,7 +7331,7 @@ static JSValue JS_GetPrivateField(JSContext *ctx, JSValueConst obj, - /* safety check */ - if (unlikely(JS_VALUE_GET_TAG(name) != JS_TAG_SYMBOL)) - return JS_ThrowTypeErrorNotASymbol(ctx); -- prop = js_symbol_to_atom(ctx, (JSValue)name); -+ prop = js_symbol_to_atom(ctx, name); - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, prop); - if (!prs) { -@@ -7300,7 +7358,7 @@ static int JS_SetPrivateField(JSContext *ctx, JSValueConst obj, - JS_ThrowTypeErrorNotASymbol(ctx); - goto fail; - } -- prop = js_symbol_to_atom(ctx, (JSValue)name); -+ prop = js_symbol_to_atom(ctx, name); - p = JS_VALUE_GET_OBJ(obj); - prs = find_own_property(&pr, p, prop); - if (!prs) { -@@ -7390,7 +7448,7 @@ static int JS_CheckBrand(JSContext *ctx, JSValueConst obj, JSValueConst func) - if (unlikely(JS_VALUE_GET_TAG(obj) != JS_TAG_OBJECT)) - goto not_obj; - p = JS_VALUE_GET_OBJ(obj); -- prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, (JSValue)brand)); -+ prs = find_own_property(&pr, p, js_symbol_to_atom(ctx, brand)); - if (!prs) { - JS_ThrowTypeError(ctx, "invalid brand on object"); - return -1; -@@ -7445,7 +7503,7 @@ static void js_free_prop_enum(JSContext *ctx, JSPropertyEnum *tab, uint32_t len) - - /* return < 0 in case if exception, 0 if OK. ptab and its atoms must - be freed by the user. */ --static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, -+static int warn_unused JS_GetOwnPropertyNamesInternal(JSContext *ctx, - JSPropertyEnum **ptab, - uint32_t *plen, - JSObject *p, int flags) -@@ -7595,7 +7653,7 @@ static int __exception JS_GetOwnPropertyNamesInternal(JSContext *ctx, - len = js_string_obj_get_length(ctx, JS_MKPTR(JS_TAG_OBJECT, p)); - add_array_keys: - for(i = 0; i < len; i++) { -- tab_atom[num_index].atom = __JS_AtomFromUInt32(i); -+ tab_atom[num_index].atom = JS_AtomFromUInt32(i); - if (tab_atom[num_index].atom == JS_ATOM_NULL) { - js_free_prop_enum(ctx, tab_atom, num_index); - return -1; -@@ -7703,9 +7761,9 @@ retry: - if (p->is_exotic) { - if (p->fast_array) { - /* specific case for fast arrays */ -- if (__JS_AtomIsTaggedInt(prop)) { -+ if (JS_AtomIsTaggedInt(prop)) { - uint32_t idx; -- idx = __JS_AtomToUInt32(prop); -+ idx = JS_AtomToUInt32(prop); - if (idx < p->u.array.count) { - if (desc) { - desc->flags = JS_PROP_WRITABLE | JS_PROP_ENUMERABLE | -@@ -7825,7 +7883,7 @@ JSAtom JS_ValueToAtom(JSContext *ctx, JSValueConst val) - if (tag == JS_TAG_INT && - (uint32_t)JS_VALUE_GET_INT(val) <= JS_ATOM_MAX_INT) { - /* fast path for integer values */ -- atom = __JS_AtomFromUInt32(JS_VALUE_GET_INT(val)); -+ atom = JS_AtomFromUInt32(JS_VALUE_GET_INT(val)); - } else if (tag == JS_TAG_SYMBOL) { - JSAtomStruct *p = JS_VALUE_GET_PTR(val); - atom = JS_DupAtom(ctx, js_get_atom_index(ctx->rt, p)); -@@ -7856,7 +7914,7 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, - /* fast path for array access */ - p = JS_VALUE_GET_OBJ(this_obj); - idx = JS_VALUE_GET_INT(prop); -- len = (uint32_t)p->u.array.count; -+ len = p->u.array.count; - if (unlikely(idx >= len)) - goto slow_path; - switch(p->class_id) { -@@ -7883,9 +7941,9 @@ static JSValue JS_GetPropertyValue(JSContext *ctx, JSValueConst this_obj, - return JS_NewBigUint64(ctx, p->u.array.u.uint64_ptr[idx]); - #endif - case JS_CLASS_FLOAT32_ARRAY: -- return __JS_NewFloat64(ctx, p->u.array.u.float_ptr[idx]); -+ return JS_NewFloat64Impl(ctx, p->u.array.u.float_ptr[idx]); - case JS_CLASS_FLOAT64_ARRAY: -- return __JS_NewFloat64(ctx, p->u.array.u.double_ptr[idx]); -+ return JS_NewFloat64Impl(ctx, p->u.array.u.double_ptr[idx]); - default: - goto slow_path; - } -@@ -7920,7 +7978,7 @@ static int JS_TryGetPropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, - - if (likely((uint64_t)idx <= JS_ATOM_MAX_INT)) { - /* fast path */ -- present = JS_HasProperty(ctx, obj, __JS_AtomFromUInt32(idx)); -+ present = JS_HasProperty(ctx, obj, JS_AtomFromUInt32(idx)); - if (present > 0) { - val = JS_GetPropertyValue(ctx, obj, JS_NewInt32(ctx, idx)); - if (unlikely(JS_IsException(val))) -@@ -8017,7 +8075,7 @@ static JSProperty *add_property(JSContext *ctx, - - /* can be called on Array or Arguments objects. return < 0 if - memory alloc error. */ --static no_inline __exception int convert_fast_array_to_array(JSContext *ctx, -+static no_inline warn_unused int convert_fast_array_to_array(JSContext *ctx, - JSObject *p) - { - JSProperty *pr; -@@ -8039,8 +8097,8 @@ static no_inline __exception int convert_fast_array_to_array(JSContext *ctx, - tab = p->u.array.u.values; - for(i = 0; i < len; i++) { - /* add_property cannot fail here but -- __JS_AtomFromUInt32(i) fails for i > INT32_MAX */ -- pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E); -+ JS_AtomFromUInt32(i) fails for i > INT32_MAX */ -+ pr = add_property(ctx, p, JS_AtomFromUInt32(i), JS_PROP_C_W_E); - pr->u.value = *tab++; - } - js_free(ctx, p->u.array.u.values); -@@ -8145,7 +8203,7 @@ static int call_setter(JSContext *ctx, JSObject *setter, - func = JS_MKPTR(JS_TAG_OBJECT, setter); - /* Note: the field could be removed in the setter */ - func = JS_DupValue(ctx, func); -- ret = JS_CallFree(ctx, func, this_obj, 1, (JSValueConst *)&val); -+ ret = JS_CallFree(ctx, func, this_obj, 1, &val); - JS_FreeValue(ctx, val); - if (JS_IsException(ret)) - return -1; -@@ -8489,8 +8547,8 @@ retry: - for(;;) { - if (p1->is_exotic) { - if (p1->fast_array) { -- if (__JS_AtomIsTaggedInt(prop)) { -- uint32_t idx = __JS_AtomToUInt32(prop); -+ if (JS_AtomIsTaggedInt(prop)) { -+ uint32_t idx = JS_AtomToUInt32(prop); - if (idx < p1->u.array.count) { - if (unlikely(p == p1)) - return JS_SetPropertyValue(ctx, this_obj, JS_NewInt32(ctx, idx), val, flags); -@@ -8610,8 +8668,8 @@ retry: - - if (p->is_exotic) { - if (p->class_id == JS_CLASS_ARRAY && p->fast_array && -- __JS_AtomIsTaggedInt(prop)) { -- uint32_t idx = __JS_AtomToUInt32(prop); -+ JS_AtomIsTaggedInt(prop)) { -+ uint32_t idx = JS_AtomToUInt32(prop); - if (idx == p->u.array.count) { - /* fast case */ - return add_fast_array_element(ctx, p, val, flags); -@@ -8662,7 +8720,7 @@ static int JS_SetPropertyValue(JSContext *ctx, JSValueConst this_obj, - JSShape *sh1; - - /* fast path to add an element to the array */ -- if (idx != (uint32_t)p->u.array.count || -+ if (idx != p->u.array.count || - !p->fast_array || !p->extensible) - goto slow_path; - /* check if prototype chain has a numeric property */ -@@ -8837,8 +8895,8 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p, - uint32_t idx, len; - - if (p->fast_array) { -- if (__JS_AtomIsTaggedInt(prop)) { -- idx = __JS_AtomToUInt32(prop); -+ if (JS_AtomIsTaggedInt(prop)) { -+ idx = JS_AtomToUInt32(prop); - if (idx == p->u.array.count) { - if (!p->extensible) - goto not_extensible; -@@ -9042,7 +9100,7 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, - return -1; - } - /* this code relies on the fact that Uint32 are never allocated */ -- val = (JSValueConst)JS_NewUint32(ctx, array_length); -+ val = JS_NewUint32(ctx, array_length); - /* prs may have been modified */ - prs = find_own_property(&pr, p, prop); - assert(prs != NULL); -@@ -9214,8 +9272,8 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, - uint32_t idx; - uint32_t prop_flags; - if (p->class_id == JS_CLASS_ARRAY) { -- if (__JS_AtomIsTaggedInt(prop)) { -- idx = __JS_AtomToUInt32(prop); -+ if (JS_AtomIsTaggedInt(prop)) { -+ idx = JS_AtomToUInt32(prop); - if (idx < p->u.array.count) { - prop_flags = get_prop_flags(flags, JS_PROP_C_W_E); - if (prop_flags != JS_PROP_C_W_E) -@@ -9238,7 +9296,7 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, - JSValue num; - int ret; - -- if (!__JS_AtomIsTaggedInt(prop)) { -+ if (!JS_AtomIsTaggedInt(prop)) { - /* slow path with to handle all numeric indexes */ - num = JS_AtomIsNumericIndex1(ctx, prop); - if (JS_IsUndefined(num)) -@@ -9259,10 +9317,10 @@ int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, - if (ret) { - return JS_ThrowTypeErrorOrFalse(ctx, flags, "negative index in typed array"); - } -- if (!__JS_AtomIsTaggedInt(prop)) -+ if (!JS_AtomIsTaggedInt(prop)) - goto typed_array_oob; - } -- idx = __JS_AtomToUInt32(prop); -+ idx = JS_AtomToUInt32(prop); - /* if the typed array is detached, p->u.array.count = 0 */ - if (idx >= typed_array_get_length(ctx, p)) { - typed_array_oob: -@@ -9563,6 +9621,11 @@ static JSValue JS_GetGlobalVar(JSContext *ctx, JSAtom prop, - return JS_ThrowReferenceErrorUninitialized(ctx, prs->atom); - return JS_DupValue(ctx, pr->u.value); - } -+ if (ctx->scopeLookup) { -+ struct LookupResult result = ctx->scopeLookup(ctx, prop); -+ if (result.useResult) -+ return result.value; -+ } - return JS_GetPropertyInternal(ctx, ctx->global_obj, prop, - ctx->global_obj, throw_ref_error); - } -@@ -9658,6 +9721,15 @@ static int JS_SetGlobalVar(JSContext *ctx, JSAtom prop, JSValue val, - flags = JS_PROP_THROW_STRICT; - if (is_strict_mode(ctx)) - flags |= JS_PROP_NO_ADD; -+ -+ if (ctx->scopeLookup) { -+ struct LookupResult result = ctx->scopeLookup(ctx, prop); -+ if (result.useResult) { -+ JS_FreeValue(ctx, result.value); -+ return JS_SetPropertyInternal(ctx, result.scope, prop, val, flags); -+ } -+ } -+ - return JS_SetPropertyInternal(ctx, ctx->global_obj, prop, val, flags); - } - -@@ -9693,7 +9765,7 @@ int JS_DeletePropertyInt64(JSContext *ctx, JSValueConst obj, int64_t idx, int fl - - if ((uint64_t)idx <= JS_ATOM_MAX_INT) { - /* fast path for fast arrays */ -- return JS_DeleteProperty(ctx, obj, __JS_AtomFromUInt32(idx), flags); -+ return JS_DeleteProperty(ctx, obj, JS_AtomFromUInt32(idx), flags); - } - prop = JS_NewAtomInt64(ctx, idx); - if (prop == JS_ATOM_NULL) -@@ -9853,7 +9925,7 @@ static JSValue JS_ToPrimitiveFree(JSContext *ctx, JSValue val, int hint) - break; - } - arg = JS_AtomToString(ctx, atom); -- ret = JS_CallFree(ctx, method, val, 1, (JSValueConst *)&arg); -+ ret = JS_CallFree(ctx, method, val, 1, &arg); - JS_FreeValue(ctx, arg); - if (JS_IsException(ret)) - goto exception; -@@ -10057,7 +10129,7 @@ static double js_strtod(const char *p, int radix, BOOL is_float) - if (is_neg) - d = -d; - } else { -- d = strtod(p, NULL); -+ d = safe_strtod(p, NULL); - } - return d; - } -@@ -10237,7 +10309,7 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, - } else - #endif - { -- double d = 1.0 / 0.0; -+ double d = INFINITY; - if (is_neg) - d = -d; - val = JS_NewFloat64(ctx, d); -@@ -10270,11 +10342,8 @@ static JSValue js_atof(JSContext *ctx, const char *str, const char **pp, - ((*p == 'p' || *p == 'P') && (radix == 2 || radix == 8 || radix == 16)))) { - const char *p1 = p + 1; - is_float = TRUE; -- if (*p1 == '+') { -- p1++; -- } else if (*p1 == '-') { -+ if (*p1 == '+' || *p1 == '-') - p1++; -- } - if (is_digit((uint8_t)*p1)) { - p = p1 + 1; - while (is_digit((uint8_t)*p) || (*p == sep && is_digit((uint8_t)p[1]))) -@@ -10509,7 +10578,7 @@ static JSValue JS_ToNumeric(JSContext *ctx, JSValueConst val) - return JS_ToNumericFree(ctx, JS_DupValue(ctx, val)); - } - --static __exception int __JS_ToFloat64Free(JSContext *ctx, double *pres, -+static warn_unused int JS_ToFloat64FreeImpl(JSContext *ctx, double *pres, - JSValue val) - { - double d; -@@ -10560,7 +10629,7 @@ static inline int JS_ToFloat64Free(JSContext *ctx, double *pres, JSValue val) - *pres = JS_VALUE_GET_FLOAT64(val); - return 0; - } else { -- return __JS_ToFloat64Free(ctx, pres, val); -+ return JS_ToFloat64FreeImpl(ctx, pres, val); - } - } - -@@ -10575,7 +10644,7 @@ static JSValue JS_ToNumber(JSContext *ctx, JSValueConst val) - } - - /* same as JS_ToNumber() but return 0 in case of NaN/Undefined */ --static __maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) -+static maybe_unused JSValue JS_ToIntegerFree(JSContext *ctx, JSValue val) - { - uint32_t tag; - JSValue ret; -@@ -10990,7 +11059,7 @@ static int JS_ToUint8ClampFree(JSContext *ctx, int32_t *pres, JSValue val) - return 0; - } - --static __exception int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, -+static warn_unused int JS_ToArrayLengthFree(JSContext *ctx, uint32_t *plen, - JSValue val, BOOL is_array_ctor) - { - uint32_t tag, len; -@@ -11092,7 +11161,7 @@ int JS_ToIndex(JSContext *ctx, uint64_t *plen, JSValueConst val) - - /* convert a value to a length between 0 and MAX_SAFE_INTEGER. - return -1 for exception */ --static __exception int JS_ToLengthFree(JSContext *ctx, int64_t *plen, -+static warn_unused int JS_ToLengthFree(JSContext *ctx, int64_t *plen, - JSValue val) - { - int res = JS_ToInt64Clamp(ctx, plen, val, 0, MAX_SAFE_INTEGER, 0); -@@ -11338,7 +11407,7 @@ static int js_ecvt(double d, int n_digits, int *decpt, int *sign, char *buf, - n_digits = (n_digits_min + n_digits_max) / 2; - js_ecvt1(d, n_digits, decpt, sign, buf, FE_TONEAREST, - buf_tmp, sizeof(buf_tmp)); -- if (strtod(buf_tmp, NULL) == d) { -+ if (safe_strtod(buf_tmp, NULL) == d) { - /* no need to keep the trailing zeros */ - while (n_digits >= 2 && buf[n_digits - 1] == '0') - n_digits--; -@@ -11700,14 +11769,14 @@ static JSValue JS_ToQuotedString(JSContext *ctx, JSValueConst val1) - return JS_EXCEPTION; - } - --static __maybe_unused void JS_DumpObjectHeader(JSRuntime *rt) -+static maybe_unused void JS_DumpObjectHeader(JSRuntime *rt) - { - printf("%14s %4s %4s %14s %10s %s\n", - "ADDRESS", "REFS", "SHRF", "PROTO", "CLASS", "PROPS"); - } - - /* for debug only: dump an object without side effect */ --static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) -+static maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) - { - uint32_t i; - char atom_buf[ATOM_GET_STR_BUF_SIZE]; -@@ -11784,7 +11853,7 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) - printf("[autoinit %p %d %p]", - (void *)js_autoinit_get_realm(pr), - js_autoinit_get_id(pr), -- (void *)pr->u.init.opaque); -+ pr->u.init.opaque); - } else { - JS_DumpValueShort(rt, pr->u.value); - } -@@ -11813,7 +11882,7 @@ static __maybe_unused void JS_DumpObject(JSRuntime *rt, JSObject *p) - printf("\n"); - } - --static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) -+static maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) - { - if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT) { - JS_DumpObject(rt, (JSObject *)p); -@@ -11845,7 +11914,7 @@ static __maybe_unused void JS_DumpGCObject(JSRuntime *rt, JSGCObjectHeader *p) - } - } - --static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, -+static maybe_unused void JS_DumpValueShort(JSRuntime *rt, - JSValueConst val) - { - uint32_t tag = JS_VALUE_GET_NORM_TAG(val); -@@ -11950,13 +12019,13 @@ static __maybe_unused void JS_DumpValueShort(JSRuntime *rt, - } - } - --static __maybe_unused void JS_DumpValue(JSContext *ctx, -+static maybe_unused void JS_DumpValue(JSContext *ctx, - JSValueConst val) - { - JS_DumpValueShort(ctx->rt, val); - } - --static __maybe_unused void JS_PrintValue(JSContext *ctx, -+static maybe_unused void JS_PrintValue(JSContext *ctx, - const char *str, - JSValueConst val) - { -@@ -12439,7 +12508,7 @@ static JSObject *find_binary_op(JSBinaryOperatorDef *def, - - /* return -1 if exception, 0 if no operator overloading, 1 if - overloaded operator called */ --static __exception int js_call_binary_op_fallback(JSContext *ctx, -+static WARN_UNUSED int js_call_binary_op_fallback(JSContext *ctx, - JSValue *pret, - JSValueConst op1, - JSValueConst op2, -@@ -12568,7 +12637,7 @@ static __exception int js_call_binary_op_fallback(JSContext *ctx, - - /* try to call the operation on the operatorSet field of 'obj'. Only - used for "/" and "**" on the BigInt prototype in math mode */ --static __exception int js_call_binary_op_simple(JSContext *ctx, -+static WARN_UNUSED int js_call_binary_op_simple(JSContext *ctx, - JSValue *pret, - JSValueConst obj, - JSValueConst op1, -@@ -12625,7 +12694,7 @@ static __exception int js_call_binary_op_simple(JSContext *ctx, - - /* return -1 if exception, 0 if no operator overloading, 1 if - overloaded operator called */ --static __exception int js_call_unary_op_fallback(JSContext *ctx, -+static WARN_UNUSED int js_call_unary_op_fallback(JSContext *ctx, - JSValue *pret, - JSValueConst op1, - OPCodeEnum op) -@@ -12835,7 +12904,7 @@ static int js_unary_arith_bigdecimal(JSContext *ctx, - return 0; - } - --static no_inline __exception int js_unary_arith_slow(JSContext *ctx, -+static no_inline WARN_UNUSED int js_unary_arith_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op) - { -@@ -12933,7 +13002,7 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx, - return -1; - } - --static __exception int js_post_inc_slow(JSContext *ctx, -+static WARN_UNUSED int js_post_inc_slow(JSContext *ctx, - JSValue *sp, OPCodeEnum op) - { - JSValue op1; -@@ -13288,7 +13357,7 @@ static int js_binary_arith_bigdecimal(JSContext *ctx, OPCodeEnum op, - return -1; - } - --static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, -+static no_inline WARN_UNUSED int js_binary_arith_slow(JSContext *ctx, JSValue *sp, - OPCodeEnum op) - { - JSValue op1, op2, res; -@@ -13452,7 +13521,7 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s - return -1; - } - --static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) -+static no_inline WARN_UNUSED int js_add_slow(JSContext *ctx, JSValue *sp) - { - JSValue op1, op2, res; - uint32_t tag1, tag2; -@@ -13566,7 +13635,7 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) - return -1; - } - --static no_inline __exception int js_binary_logic_slow(JSContext *ctx, -+static no_inline WARN_UNUSED int js_binary_logic_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op) - { -@@ -13916,7 +13985,7 @@ static BOOL tag_is_number(uint32_t tag) - tag == JS_TAG_BIG_DECIMAL); - } - --static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, -+static no_inline WARN_UNUSED int js_eq_slow(JSContext *ctx, JSValue *sp, - BOOL is_neq) - { - JSValue op1, op2, ret; -@@ -14192,7 +14261,7 @@ int JS_ToBigInt64(JSContext *ctx, int64_t *pres, JSValueConst val) - return -1; - } - --static no_inline __exception int js_unary_arith_slow(JSContext *ctx, -+static no_inline warn_unused int js_unary_arith_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op) - { -@@ -14224,7 +14293,7 @@ static no_inline __exception int js_unary_arith_slow(JSContext *ctx, - } - - /* specific case necessary for correct return value semantics */ --static __exception int js_post_inc_slow(JSContext *ctx, -+static warn_unused int js_post_inc_slow(JSContext *ctx, - JSValue *sp, OPCodeEnum op) - { - JSValue op1; -@@ -14241,7 +14310,7 @@ static __exception int js_post_inc_slow(JSContext *ctx, - return 0; - } - --static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *sp, -+static no_inline warn_unused int js_binary_arith_slow(JSContext *ctx, JSValue *sp, - OPCodeEnum op) - { - JSValue op1, op2; -@@ -14283,7 +14352,7 @@ static no_inline __exception int js_binary_arith_slow(JSContext *ctx, JSValue *s - return -1; - } - --static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) -+static no_inline warn_unused int js_add_slow(JSContext *ctx, JSValue *sp) - { - JSValue op1, op2; - uint32_t tag1, tag2; -@@ -14331,7 +14400,7 @@ static no_inline __exception int js_add_slow(JSContext *ctx, JSValue *sp) - return -1; - } - --static no_inline __exception int js_binary_logic_slow(JSContext *ctx, -+static no_inline warn_unused int js_binary_logic_slow(JSContext *ctx, - JSValue *sp, - OPCodeEnum op) - { -@@ -14458,7 +14527,7 @@ static no_inline int js_relational_slow(JSContext *ctx, JSValue *sp, - return -1; - } - --static no_inline __exception int js_eq_slow(JSContext *ctx, JSValue *sp, -+static no_inline warn_unused int js_eq_slow(JSContext *ctx, JSValue *sp, - BOOL is_neq) - { - JSValue op1, op2; -@@ -14744,7 +14813,7 @@ static no_inline int js_strict_eq_slow(JSContext *ctx, JSValue *sp, - return 0; - } - --static __exception int js_operator_in(JSContext *ctx, JSValue *sp) -+static warn_unused int js_operator_in(JSContext *ctx, JSValue *sp) - { - JSValue op1, op2; - JSAtom atom; -@@ -14770,7 +14839,7 @@ static __exception int js_operator_in(JSContext *ctx, JSValue *sp) - return 0; - } - --static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj, -+static warn_unused int js_has_unscopable(JSContext *ctx, JSValueConst obj, - JSAtom atom) - { - JSValue arr, val; -@@ -14788,7 +14857,7 @@ static __exception int js_has_unscopable(JSContext *ctx, JSValueConst obj, - return ret; - } - --static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp) -+static warn_unused int js_operator_instanceof(JSContext *ctx, JSValue *sp) - { - JSValue op1, op2; - BOOL ret; -@@ -14804,7 +14873,7 @@ static __exception int js_operator_instanceof(JSContext *ctx, JSValue *sp) - return 0; - } - --static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1) -+static warn_unused int js_operator_typeof(JSContext *ctx, JSValueConst op1) - { - JSAtom atom; - uint32_t tag; -@@ -14861,7 +14930,7 @@ static __exception int js_operator_typeof(JSContext *ctx, JSValueConst op1) - return atom; - } - --static __exception int js_operator_delete(JSContext *ctx, JSValue *sp) -+static warn_unused int js_operator_delete(JSContext *ctx, JSValue *sp) - { - JSValue op1, op2; - JSAtom atom; -@@ -15016,7 +15085,7 @@ static JSValue js_build_mapped_arguments(JSContext *ctx, int argc, - var_ref = get_var_ref(ctx, sf, i, TRUE); - if (!var_ref) - goto fail; -- pr = add_property(ctx, p, __JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF); -+ pr = add_property(ctx, p, JS_AtomFromUInt32(i), JS_PROP_C_W_E | JS_PROP_VARREF); - if (!pr) { - free_var_ref(ctx->rt, var_ref); - goto fail; -@@ -15188,7 +15257,7 @@ static JSValue build_for_in_iterator(JSContext *ctx, JSValue obj) - } - - /* obj -> enum_obj */ --static __exception int js_for_in_start(JSContext *ctx, JSValue *sp) -+static warn_unused int js_for_in_start(JSContext *ctx, JSValue *sp) - { - sp[-1] = build_for_in_iterator(ctx, sp[-1]); - if (JS_IsException(sp[-1])) -@@ -15197,7 +15266,7 @@ static __exception int js_for_in_start(JSContext *ctx, JSValue *sp) - } - - /* enum_obj -> enum_obj value done */ --static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) -+static warn_unused int js_for_in_next(JSContext *ctx, JSValue *sp) - { - JSValueConst enum_obj; - JSObject *p; -@@ -15218,7 +15287,7 @@ static __exception int js_for_in_next(JSContext *ctx, JSValue *sp) - if (it->is_array) { - if (it->idx >= it->array_length) - goto done; -- prop = __JS_AtomFromUInt32(it->idx); -+ prop = JS_AtomFromUInt32(it->idx); - it->idx++; - } else { - JSShape *sh = p->shape; -@@ -15411,7 +15480,7 @@ static int JS_IteratorClose(JSContext *ctx, JSValueConst enum_obj, - } - - /* obj -> enum_rec (3 slots) */ --static __exception int js_for_of_start(JSContext *ctx, JSValue *sp, -+static warn_unused int js_for_of_start(JSContext *ctx, JSValue *sp, - BOOL is_async) - { - JSValue op1, obj, method; -@@ -15432,7 +15501,7 @@ static __exception int js_for_of_start(JSContext *ctx, JSValue *sp, - objs. If 'done' is true or in case of exception, 'enum_rec' is set - to undefined. If 'done' is true, 'value' is always set to - undefined. */ --static __exception int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) -+static warn_unused int js_for_of_next(JSContext *ctx, JSValue *sp, int offset) - { - JSValue value = JS_UNDEFINED; - int done = 1; -@@ -15478,7 +15547,7 @@ static JSValue JS_IteratorGetCompleteValue(JSContext *ctx, JSValueConst obj, - return JS_EXCEPTION; - } - --static __exception int js_iterator_get_value_done(JSContext *ctx, JSValue *sp) -+static warn_unused int js_iterator_get_value_done(JSContext *ctx, JSValue *sp) - { - JSValue obj, value; - BOOL done; -@@ -15554,7 +15623,7 @@ static BOOL js_get_fast_array(JSContext *ctx, JSValueConst obj, - return FALSE; - } - --static __exception int js_append_enumerate(JSContext *ctx, JSValue *sp) -+static warn_unused int js_append_enumerate(JSContext *ctx, JSValue *sp) - { - JSValue iterator, enumobj, method, value; - int is_array_iterator; -@@ -15634,7 +15703,7 @@ exception: - return -1; - } - --static __exception int JS_CopyDataProperties(JSContext *ctx, -+static warn_unused int JS_CopyDataProperties(JSContext *ctx, - JSValueConst target, - JSValueConst source, - JSValueConst excluded, -@@ -16043,7 +16112,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, - #else - sf->js_mode = 0; - #endif -- sf->cur_func = (JSValue)func_obj; -+ sf->cur_func = func_obj; - sf->arg_count = argc; - arg_buf = argv; - -@@ -16056,7 +16125,7 @@ static JSValue js_call_c_function(JSContext *ctx, JSValueConst func_obj, - arg_buf[i] = JS_UNDEFINED; - sf->arg_count = arg_count; - } -- sf->arg_buf = (JSValue*)arg_buf; -+ sf->arg_buf = arg_buf; - - func = p->u.cfunc.c_function; - switch(cproto) { -@@ -16226,7 +16295,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - #include "quickjs-opcode.h" - [ OP_COUNT ... 255 ] = &&case_default - }; --#define SWITCH(pc) goto *dispatch_table[opcode = *pc++]; -+#define SWITCH(pc) goto *dispatch_table[opcode = *(pc)++]; - #define CASE(op) case_ ## op - #define DEFAULT case_default - #define BREAK SWITCH(pc) -@@ -16269,7 +16338,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - return JS_ThrowTypeError(caller_ctx, "not a function"); - } - return call_func(caller_ctx, func_obj, this_obj, argc, -- (JSValueConst *)argv, flags); -+ argv, flags); - } - b = p->u.func.function_bytecode; - -@@ -16287,7 +16356,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - sf->js_mode = b->js_mode; - arg_buf = argv; - sf->arg_count = argc; -- sf->cur_func = (JSValue)func_obj; -+ sf->cur_func = func_obj; - init_list_head(&sf->var_ref_list); - var_refs = p->u.func.var_refs; - -@@ -16315,6 +16384,9 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - rt->current_stack_frame = sf; - ctx = b->realm; /* set the current realm */ - -+ if (ctx->handleFunctionEntered) -+ ctx->handleFunctionEntered(ctx, this_obj); -+ - restart: - for(;;) { - int call_argc; -@@ -16420,12 +16492,12 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - int arg = *pc++; - switch(arg) { - case OP_SPECIAL_OBJECT_ARGUMENTS: -- *sp++ = js_build_arguments(ctx, argc, (JSValueConst *)argv); -+ *sp++ = js_build_arguments(ctx, argc, argv); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - break; - case OP_SPECIAL_OBJECT_MAPPED_ARGUMENTS: -- *sp++ = js_build_mapped_arguments(ctx, argc, (JSValueConst *)argv, -+ *sp++ = js_build_mapped_arguments(ctx, argc, argv, - sf, min_int(argc, b->arg_count)); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; -@@ -16465,7 +16537,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - { - int first = get_u16(pc); - pc += 2; -- *sp++ = js_build_rest(ctx, first, argc, (JSValueConst *)argv); -+ *sp++ = js_build_rest(ctx, first, argc, argv); - if (unlikely(JS_IsException(sp[-1]))) - goto exception; - } -@@ -16698,7 +16770,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - goto exception; - call_argv = sp - call_argc; - for(i = 0; i < call_argc; i++) { -- ret = JS_DefinePropertyValue(ctx, ret_val, __JS_AtomFromUInt32(i), call_argv[i], -+ ret = JS_DefinePropertyValue(ctx, ret_val, JS_AtomFromUInt32(i), call_argv[i], - JS_PROP_C_W_E | JS_PROP_THROW); - call_argv[i] = JS_UNDEFINED; - if (ret < 0) { -@@ -16717,7 +16789,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - magic = get_u16(pc); - pc += 2; - -- ret_val = js_function_apply(ctx, sp[-3], 2, (JSValueConst *)&sp[-2], magic); -+ ret_val = js_function_apply(ctx, sp[-3], 2, &sp[-2], magic); - if (unlikely(JS_IsException(ret_val))) - goto exception; - JS_FreeValue(ctx, sp[-3]); -@@ -16850,7 +16922,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - JS_EVAL_TYPE_DIRECT, scope_idx); - } else { - ret_val = JS_Call(ctx, sp[-2], JS_UNDEFINED, len, -- (JSValueConst *)tab); -+ tab); - } - free_arg_list(ctx, tab, len); - if (unlikely(JS_IsException(ret_val))) -@@ -17474,7 +17546,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - { - JSValue ret; - ret = JS_Call(ctx, sp[-3], sp[-4], -- 1, (JSValueConst *)(sp - 1)); -+ 1, (sp - 1)); - if (JS_IsException(ret)) - goto exception; - JS_FreeValue(ctx, sp[-1]); -@@ -17502,7 +17574,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - 0, NULL); - } else { - ret = JS_CallFree(ctx, method, sp[-4], -- 1, (JSValueConst *)(sp - 1)); -+ 1, (sp - 1)); - } - if (JS_IsException(ret)) - goto exception; -@@ -17925,7 +17997,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - sp[-2] = JS_NewInt32(ctx, r); - sp--; - } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { -- sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) + -+ sp[-2] = JS_NewFloat64Impl(ctx, JS_VALUE_GET_FLOAT64(op1) + - JS_VALUE_GET_FLOAT64(op2)); - sp--; - } else { -@@ -17990,7 +18062,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - sp[-2] = JS_NewInt32(ctx, r); - sp--; - } else if (JS_VALUE_IS_BOTH_FLOAT(op1, op2)) { -- sp[-2] = __JS_NewFloat64(ctx, JS_VALUE_GET_FLOAT64(op1) - -+ sp[-2] = JS_NewFloat64Impl(ctx, JS_VALUE_GET_FLOAT64(op1) - - JS_VALUE_GET_FLOAT64(op2)); - sp--; - } else { -@@ -18033,7 +18105,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - #endif - d = JS_VALUE_GET_FLOAT64(op1) * JS_VALUE_GET_FLOAT64(op2); - mul_fp_res: -- sp[-2] = __JS_NewFloat64(ctx, d); -+ sp[-2] = JS_NewFloat64Impl(ctx, d); - sp--; - } else { - goto binary_arith_slow; -@@ -18125,7 +18197,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - } else if (JS_TAG_IS_FLOAT64(tag)) { - d = -JS_VALUE_GET_FLOAT64(op1); - neg_fp_res: -- sp[-1] = __JS_NewFloat64(ctx, d); -+ sp[-1] = JS_NewFloat64Impl(ctx, d); - } else { - if (js_unary_arith_slow(ctx, sp, opcode)) - goto exception; -@@ -18659,6 +18731,13 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - } - } - exception: -+ if (JS_IsString(rt->current_exception)) { -+ JSValue error_obj = JS_NewError(ctx); -+ JSAtom msgProp = JS_NewAtom(ctx, "message"); -+ JS_DefinePropertyValue(ctx, error_obj, msgProp, rt->current_exception, 0); -+ rt->current_exception = error_obj; -+ JS_FreeAtom(ctx, msgProp); -+ } - if (is_backtrace_needed(ctx, rt->current_exception)) { - /* add the backtrace information now (it is not done - before if the exception happens in a bytecode -@@ -18706,6 +18785,8 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValueConst func_obj, - } - } - rt->current_stack_frame = sf->prev_frame; -+ if (ctx->handleFunctionExited) -+ ctx->handleFunctionExited(ctx); - return ret_val; - } - -@@ -18713,14 +18794,14 @@ JSValue JS_Call(JSContext *ctx, JSValueConst func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv) - { - return JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, -- argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV); -+ argc, argv, JS_CALL_FLAG_COPY_ARGV); - } - - static JSValue JS_CallFree(JSContext *ctx, JSValue func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv) - { - JSValue res = JS_CallInternal(ctx, func_obj, this_obj, JS_UNDEFINED, -- argc, (JSValue *)argv, JS_CALL_FLAG_COPY_ARGV); -+ argc, argv, JS_CALL_FLAG_COPY_ARGV); - JS_FreeValue(ctx, func_obj); - return res; - } -@@ -18825,7 +18906,7 @@ static JSValue JS_CallConstructorInternal(JSContext *ctx, - return JS_ThrowTypeError(ctx, "not a function"); - } - return call_func(ctx, func_obj, new_target, argc, -- (JSValueConst *)argv, flags); -+ argv, flags); - } - - b = p->u.func.function_bytecode; -@@ -18854,7 +18935,7 @@ JSValue JS_CallConstructor2(JSContext *ctx, JSValueConst func_obj, - int argc, JSValueConst *argv) - { - return JS_CallConstructorInternal(ctx, func_obj, new_target, -- argc, (JSValue *)argv, -+ argc, argv, - JS_CALL_FLAG_COPY_ARGV); - } - -@@ -18862,7 +18943,7 @@ JSValue JS_CallConstructor(JSContext *ctx, JSValueConst func_obj, - int argc, JSValueConst *argv) - { - return JS_CallConstructorInternal(ctx, func_obj, func_obj, -- argc, (JSValue *)argv, -+ argc, argv, - JS_CALL_FLAG_COPY_ARGV); - } - -@@ -18885,7 +18966,7 @@ static JSValue JS_InvokeFree(JSContext *ctx, JSValue this_val, JSAtom atom, - } - - /* JSAsyncFunctionState (used by generator and async functions) */ --static __exception int async_func_init(JSContext *ctx, JSAsyncFunctionState *s, -+static warn_unused int async_func_init(JSContext *ctx, JSAsyncFunctionState *s, - JSValueConst func_obj, JSValueConst this_obj, - int argc, JSValueConst *argv) - { -@@ -19227,7 +19308,7 @@ static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) - fail: - error = JS_GetException(ctx); - ret2 = JS_Call(ctx, s->resolving_funcs[1], JS_UNDEFINED, -- 1, (JSValueConst *)&error); -+ 1, &error); - JS_FreeValue(ctx, error); - js_async_function_terminate(ctx->rt, s); - JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ -@@ -19238,7 +19319,7 @@ static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) - if (JS_IsUndefined(func_ret)) { - /* function returned */ - ret2 = JS_Call(ctx, s->resolving_funcs[0], JS_UNDEFINED, -- 1, (JSValueConst *)&value); -+ 1, &value); - JS_FreeValue(ctx, ret2); /* XXX: what to do if exception ? */ - JS_FreeValue(ctx, value); - js_async_function_terminate(ctx->rt, s); -@@ -19249,7 +19330,7 @@ static void js_async_function_resume(JSContext *ctx, JSAsyncFunctionData *s) - /* await */ - JS_FreeValue(ctx, func_ret); /* not used */ - promise = js_promise_resolve(ctx, ctx->promise_ctor, -- 1, (JSValueConst *)&value, 0); -+ 1, &value, 0); - JS_FreeValue(ctx, value); - if (JS_IsException(promise)) - goto fail; -@@ -19531,7 +19612,7 @@ static int js_async_generator_completed_return(JSContext *ctx, - int res; - - promise = js_promise_resolve(ctx, ctx->promise_ctor, -- 1, (JSValueConst *)&value, 0); -+ 1, &value, 0); - if (JS_IsException(promise)) - return -1; - if (js_async_generator_resolve_function_create(ctx, -@@ -19703,7 +19784,7 @@ static JSValue js_async_generator_next(JSContext *ctx, JSValueConst this_val, - JS_ThrowTypeError(ctx, "not an AsyncGenerator object"); - err = JS_GetException(ctx); - res2 = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, -- 1, (JSValueConst *)&err); -+ 1, &err); - JS_FreeValue(ctx, err); - JS_FreeValue(ctx, res2); - JS_FreeValue(ctx, resolving_funcs[0]); -@@ -20138,7 +20219,7 @@ static const JSOpCode opcode_info[OP_COUNT + (OP_TEMP_END - OP_TEMP_START)] = { - #define short_opcode_info(op) opcode_info[op] - #endif - --static __exception int next_token(JSParseState *s); -+static warn_unused int next_token(JSParseState *s); - - static void free_token(JSParseState *s, JSToken *token) - { -@@ -20169,7 +20250,7 @@ static void free_token(JSParseState *s, JSToken *token) - } - } - --static void __attribute((unused)) dump_token(JSParseState *s, -+static void maybe_unused dump_token(JSParseState *s, - const JSToken *token) - { - switch(token->val) { -@@ -20230,7 +20311,7 @@ static void __attribute((unused)) dump_token(JSParseState *s, - } - } - --int __attribute__((format(printf, 2, 3))) js_parse_error(JSParseState *s, const char *fmt, ...) -+int FORMAT_ATTR(2, 3) js_parse_error(JSParseState *s, const char *fmt, ...) - { - JSContext *ctx = s->ctx; - va_list ap; -@@ -20276,7 +20357,7 @@ static int js_parse_error_reserved_identifier(JSParseState *s) - s->token.u.ident.atom)); - } - --static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p) -+static warn_unused int js_parse_template_part(JSParseState *s, const uint8_t *p) - { - uint32_t c; - StringBuffer b_s, *b = &b_s; -@@ -20337,7 +20418,7 @@ static __exception int js_parse_template_part(JSParseState *s, const uint8_t *p) - return -1; - } - --static __exception int js_parse_string(JSParseState *s, int sep, -+static warn_unused int js_parse_string(JSParseState *s, int sep, - BOOL do_throw, const uint8_t *p, - JSToken *token, const uint8_t **pp) - { -@@ -20482,7 +20563,7 @@ static inline BOOL token_is_pseudo_keyword(JSParseState *s, JSAtom atom) { - !s->token.u.ident.has_escape; - } - --static __exception int js_parse_regexp(JSParseState *s) -+static warn_unused int js_parse_regexp(JSParseState *s) - { - const uint8_t *p; - BOOL in_class; -@@ -20580,8 +20661,8 @@ static __exception int js_parse_regexp(JSParseState *s) - return -1; - } - --static __exception int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, -- char *static_buf) -+static warn_unused int ident_realloc(JSContext *ctx, char **pbuf, size_t *psize, -+ const char *static_buf) - { - char *buf, *new_buf; - size_t size, new_size; -@@ -20656,7 +20737,7 @@ static JSAtom parse_ident(JSParseState *s, const uint8_t **pp, - } - - --static __exception int next_token(JSParseState *s) -+static warn_unused int next_token(JSParseState *s) - { - const uint8_t *p; - int c; -@@ -21204,7 +21285,7 @@ static JSAtom json_parse_ident(JSParseState *s, const uint8_t **pp, int c) - return atom; - } - --static __exception int json_next_token(JSParseState *s) -+static warn_unused int json_next_token(JSParseState *s) - { - const uint8_t *p; - int c; -@@ -21644,7 +21725,7 @@ static int cpool_add(JSParseState *s, JSValue val) - return fd->cpool_count - 1; - } - --static __exception int emit_push_const(JSParseState *s, JSValueConst val, -+static warn_unused int emit_push_const(JSParseState *s, JSValueConst val, - BOOL as_atom) - { - int idx; -@@ -21654,7 +21735,7 @@ static __exception int emit_push_const(JSParseState *s, JSValueConst val, - /* warning: JS_NewAtomStr frees the string value */ - JS_DupValue(s->ctx, val); - atom = JS_NewAtomStr(s->ctx, JS_VALUE_GET_STRING(val)); -- if (atom != JS_ATOM_NULL && !__JS_AtomIsTaggedInt(atom)) { -+ if (atom != JS_ATOM_NULL && !JS_AtomIsTaggedInt(atom)) { - emit_op(s, OP_push_atom_value); - emit_u32(s, atom); - return 0; -@@ -22118,14 +22199,14 @@ static int add_private_class_field(JSParseState *s, JSFunctionDef *fd, - return idx; - } - --static __exception int js_parse_expr(JSParseState *s); --static __exception int js_parse_function_decl(JSParseState *s, -+static warn_unused int js_parse_expr(JSParseState *s); -+static warn_unused int js_parse_function_decl(JSParseState *s, - JSParseFunctionEnum func_type, - JSFunctionKindEnum func_kind, - JSAtom func_name, const uint8_t *ptr, - int start_line); - static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s); --static __exception int js_parse_function_decl2(JSParseState *s, -+static warn_unused int js_parse_function_decl2(JSParseState *s, - JSParseFunctionEnum func_type, - JSFunctionKindEnum func_kind, - JSAtom func_name, -@@ -22133,9 +22214,9 @@ static __exception int js_parse_function_decl2(JSParseState *s, - int function_line_num, - JSParseExportEnum export_flag, - JSFunctionDef **pfd); --static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags); --static __exception int js_parse_assign_expr(JSParseState *s); --static __exception int js_parse_unary(JSParseState *s, int parse_flags); -+static warn_unused int js_parse_assign_expr2(JSParseState *s, int parse_flags); -+static warn_unused int js_parse_assign_expr(JSParseState *s); -+static warn_unused int js_parse_unary(JSParseState *s, int parse_flags); - static void push_break_entry(JSFunctionDef *fd, BlockEnv *be, - JSAtom label_name, - int label_break, int label_cont, -@@ -22162,7 +22243,7 @@ static int seal_template_obj(JSContext *ctx, JSValueConst obj) - return 0; - } - --static __exception int js_parse_template(JSParseState *s, int call, int *argc) -+static warn_unused int js_parse_template(JSParseState *s, int call, int *argc) - { - JSContext *ctx = s->ctx; - JSValue raw_array, template_object; -@@ -22293,7 +22374,7 @@ static BOOL token_is_ident(int tok) - } - - /* if the property is an expression, name = JS_ATOM_NULL */ --static int __exception js_parse_property_name(JSParseState *s, -+static int warn_unused js_parse_property_name(JSParseState *s, - JSAtom *pname, - BOOL allow_method, BOOL allow_var, - BOOL allow_private) -@@ -22435,7 +22516,7 @@ static int js_parse_get_pos(JSParseState *s, JSParsePos *sp) - return 0; - } - --static __exception int js_parse_seek_token(JSParseState *s, const JSParsePos *sp) -+static warn_unused int js_parse_seek_token(JSParseState *s, const JSParsePos *sp) - { - s->token.line_num = sp->last_line_num; - s->line_num = sp->line_num; -@@ -22480,7 +22561,8 @@ static int js_parse_skip_parens_token(JSParseState *s, int *pbits, BOOL no_line_ - size_t level = 0; - JSParsePos pos; - int last_tok, tok = TOK_EOF; -- int c, tok_len, bits = 0; -+ int tok_len, bits = 0; -+ char c; - - /* protect from underflow */ - state[level++] = 0; -@@ -22640,7 +22722,7 @@ static void set_object_name_computed(JSParseState *s) - } - } - --static __exception int js_parse_object_literal(JSParseState *s) -+static warn_unused int js_parse_object_literal(JSParseState *s) - { - JSAtom name = JS_ATOM_NULL; - const uint8_t *start_ptr; -@@ -22765,15 +22847,15 @@ static __exception int js_parse_object_literal(JSParseState *s) - /* forbid the exponentiation operator in js_parse_unary() */ - #define PF_POW_FORBIDDEN (1 << 4) - --static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags); -+static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags); - --static __exception int js_parse_left_hand_side_expr(JSParseState *s) -+static warn_unused int js_parse_left_hand_side_expr(JSParseState *s) - { - return js_parse_postfix_expr(s, PF_POSTFIX_CALL); - } - - /* XXX: could generate specific bytecode */ --static __exception int js_parse_class_default_ctor(JSParseState *s, -+static warn_unused int js_parse_class_default_ctor(JSParseState *s, - BOOL has_super, - JSFunctionDef **pfd) - { -@@ -22864,7 +22946,7 @@ typedef struct { - int brand_push_pos; - } ClassFieldsDef; - --static __exception int emit_class_init_start(JSParseState *s, -+static warn_unused int emit_class_init_start(JSParseState *s, - ClassFieldsDef *cf) - { - int label_add_brand; -@@ -22897,7 +22979,7 @@ static __exception int emit_class_init_start(JSParseState *s, - return 0; - } - --static __exception int add_brand(JSParseState *s, ClassFieldsDef *cf) -+static warn_unused int add_brand(JSParseState *s, ClassFieldsDef *cf) - { - if (!cf->has_brand) { - /* define the brand field in 'this' of the initializer */ -@@ -22929,7 +23011,7 @@ static void emit_class_init_end(JSParseState *s, ClassFieldsDef *cf) - } - - --static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, -+static warn_unused int js_parse_class(JSParseState *s, BOOL is_class_expr, - JSParseExportEnum export_flag) - { - JSContext *ctx = s->ctx; -@@ -23374,7 +23456,7 @@ static __exception int js_parse_class(JSParseState *s, BOOL is_class_expr, - return -1; - } - --static __exception int js_parse_array_literal(JSParseState *s) -+static warn_unused int js_parse_array_literal(JSParseState *s) - { - uint32_t idx; - BOOL need_length; -@@ -23410,7 +23492,7 @@ static __exception int js_parse_array_literal(JSParseState *s) - if (js_parse_assign_expr(s)) - return -1; - emit_op(s, OP_define_field); -- emit_u32(s, __JS_AtomFromUInt32(idx)); -+ emit_u32(s, JS_AtomFromUInt32(idx)); - need_length = FALSE; - } - idx++; -@@ -23520,7 +23602,7 @@ static BOOL has_with_scope(JSFunctionDef *s, int scope_level) - return FALSE; - } - --static __exception int get_lvalue(JSParseState *s, int *popcode, int *pscope, -+static warn_unused int get_lvalue(JSParseState *s, int *popcode, int *pscope, - JSAtom *pname, int *plabel, int *pdepth, BOOL keep, - int tok) - { -@@ -23760,7 +23842,7 @@ static void put_lvalue(JSParseState *s, int opcode, int scope, - } - } - --static __exception int js_parse_expr_paren(JSParseState *s) -+static warn_unused int js_parse_expr_paren(JSParseState *s) - { - if (js_parse_expect(s, '(')) - return -1; -@@ -23778,7 +23860,7 @@ static int js_unsupported_keyword(JSParseState *s, JSAtom atom) - JS_AtomGetStr(s->ctx, buf, sizeof(buf), atom)); - } - --static __exception int js_define_var(JSParseState *s, JSAtom name, int tok) -+static warn_unused int js_define_var(JSParseState *s, JSAtom name, int tok) - { - JSFunctionDef *fd = s->cur_func; - JSVarDefEnum var_def_type; -@@ -24327,7 +24409,7 @@ static void optional_chain_test(JSParseState *s, int *poptional_chaining_label, - } - - /* allowed parse_flags: PF_POSTFIX_CALL, PF_ARROW_FUNC */ --static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) -+static warn_unused int js_parse_postfix_expr(JSParseState *s, int parse_flags) - { - FuncCallType call_type; - int optional_chaining_label; -@@ -24963,17 +25045,16 @@ static __exception int js_parse_postfix_expr(JSParseState *s, int parse_flags) - return 0; - } - --static __exception int js_parse_delete(JSParseState *s) -+static warn_unused int js_parse_delete(JSParseState *s) - { - JSFunctionDef *fd = s->cur_func; - JSAtom name; -- int opcode; - - if (next_token(s)) - return -1; - if (js_parse_unary(s, PF_POW_FORBIDDEN)) - return -1; -- switch(opcode = get_prev_opcode(fd)) { -+ switch (get_prev_opcode(fd)) { - case OP_get_field: - { - JSValue val; -@@ -25024,7 +25105,7 @@ static __exception int js_parse_delete(JSParseState *s) - } - - /* allowed parse_flags: PF_ARROW_FUNC, PF_POW_ALLOWED, PF_POW_FORBIDDEN */ --static __exception int js_parse_unary(JSParseState *s, int parse_flags) -+static warn_unused int js_parse_unary(JSParseState *s, int parse_flags) - { - int op; - -@@ -25172,7 +25253,7 @@ static __exception int js_parse_unary(JSParseState *s, int parse_flags) - } - - /* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ --static __exception int js_parse_expr_binary(JSParseState *s, int level, -+static warn_unused int js_parse_expr_binary(JSParseState *s, int level, - int parse_flags) - { - int op, opcode; -@@ -25319,7 +25400,7 @@ static __exception int js_parse_expr_binary(JSParseState *s, int level, - } - - /* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ --static __exception int js_parse_logical_and_or(JSParseState *s, int op, -+static warn_unused int js_parse_logical_and_or(JSParseState *s, int op, - int parse_flags) - { - int label1; -@@ -25361,7 +25442,7 @@ static __exception int js_parse_logical_and_or(JSParseState *s, int op, - return 0; - } - --static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags) -+static warn_unused int js_parse_coalesce_expr(JSParseState *s, int parse_flags) - { - int label1; - -@@ -25389,7 +25470,7 @@ static __exception int js_parse_coalesce_expr(JSParseState *s, int parse_flags) - } - - /* allowed parse_flags: PF_ARROW_FUNC, PF_IN_ACCEPTED */ --static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags) -+static warn_unused int js_parse_cond_expr(JSParseState *s, int parse_flags) - { - int label1, label2; - -@@ -25420,7 +25501,7 @@ static __exception int js_parse_cond_expr(JSParseState *s, int parse_flags) - static void emit_return(JSParseState *s, BOOL hasval); - - /* allowed parse_flags: PF_IN_ACCEPTED */ --static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) -+static warn_unused int js_parse_assign_expr2(JSParseState *s, int parse_flags) - { - int opcode, op, scope; - JSAtom name0 = JS_ATOM_NULL; -@@ -25666,13 +25747,13 @@ static __exception int js_parse_assign_expr2(JSParseState *s, int parse_flags) - return 0; - } - --static __exception int js_parse_assign_expr(JSParseState *s) -+static warn_unused int js_parse_assign_expr(JSParseState *s) - { - return js_parse_assign_expr2(s, PF_IN_ACCEPTED); - } - - /* allowed parse_flags: PF_IN_ACCEPTED */ --static __exception int js_parse_expr2(JSParseState *s, int parse_flags) -+static warn_unused int js_parse_expr2(JSParseState *s, int parse_flags) - { - BOOL comma = FALSE; - for(;;) { -@@ -25696,7 +25777,7 @@ static __exception int js_parse_expr2(JSParseState *s, int parse_flags) - return 0; - } - --static __exception int js_parse_expr(JSParseState *s) -+static warn_unused int js_parse_expr(JSParseState *s) - { - return js_parse_expr2(s, PF_IN_ACCEPTED); - } -@@ -25724,7 +25805,7 @@ static void pop_break_entry(JSFunctionDef *fd) - fd->top_break = be->prev; - } - --static __exception int emit_break(JSParseState *s, JSAtom name, int is_cont) -+static warn_unused int emit_break(JSParseState *s, JSAtom name, int is_cont) - { - BlockEnv *top; - int i, scope_level; -@@ -25873,15 +25954,15 @@ static void emit_return(JSParseState *s, BOOL hasval) - #define DECL_MASK_OTHER (1 << 2) /* all other declarations */ - #define DECL_MASK_ALL (DECL_MASK_FUNC | DECL_MASK_FUNC_WITH_LABEL | DECL_MASK_OTHER) - --static __exception int js_parse_statement_or_decl(JSParseState *s, -+static warn_unused int js_parse_statement_or_decl(JSParseState *s, - int decl_mask); - --static __exception int js_parse_statement(JSParseState *s) -+static warn_unused int js_parse_statement(JSParseState *s) - { - return js_parse_statement_or_decl(s, 0); - } - --static __exception int js_parse_block(JSParseState *s) -+static warn_unused int js_parse_block(JSParseState *s) - { - if (js_parse_expect(s, '{')) - return -1; -@@ -25901,7 +25982,7 @@ static __exception int js_parse_block(JSParseState *s) - } - - /* allowed parse_flags: PF_IN_ACCEPTED */ --static __exception int js_parse_var(JSParseState *s, int parse_flags, int tok, -+static warn_unused int js_parse_var(JSParseState *s, int parse_flags, int tok, - BOOL export_flag) - { - JSContext *ctx = s->ctx; -@@ -26052,7 +26133,7 @@ static int is_let(JSParseState *s, int decl_mask) - - /* XXX: handle IteratorClose when exiting the loop before the - enumeration is done */ --static __exception int js_parse_for_in_of(JSParseState *s, int label_name, -+static warn_unused int js_parse_for_in_of(JSParseState *s, int label_name, - BOOL is_async) - { - JSContext *ctx = s->ctx; -@@ -26283,7 +26364,7 @@ static void set_eval_ret_undefined(JSParseState *s) - } - } - --static __exception int js_parse_statement_or_decl(JSParseState *s, -+static warn_unused int js_parse_statement_or_decl(JSParseState *s, - int decl_mask) - { - JSContext *ctx = s->ctx; -@@ -27603,7 +27684,7 @@ static int find_exported_name(GetExportNamesState *s, JSAtom name) - return -1; - } - --static __exception int get_exported_names(JSContext *ctx, -+static warn_unused int get_exported_names(JSContext *ctx, - GetExportNamesState *s, - JSModuleDef *m, BOOL from_star) - { -@@ -27744,7 +27825,6 @@ static JSValue js_build_module_ns(JSContext *ctx, JSModuleDef *m) - en->u.var_ref = res_me->u.local.var_ref; - } else { - JSObject *p1 = JS_VALUE_GET_OBJ(res_m->func_obj); -- p1 = JS_VALUE_GET_OBJ(res_m->func_obj); - en->u.var_ref = p1->u.func.var_refs[res_me->u.local.var_idx]; - } - } -@@ -28237,7 +28317,7 @@ static JSValue js_dynamic_import_job(JSContext *ctx, - goto exception; - - ret = JS_Call(ctx, resolving_funcs[0], JS_UNDEFINED, -- 1, (JSValueConst *)&ns); -+ 1, &ns); - JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ - JS_FreeValue(ctx, ns); - JS_FreeCString(ctx, basename); -@@ -28246,7 +28326,7 @@ static JSValue js_dynamic_import_job(JSContext *ctx, - - err = JS_GetException(ctx); - ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, -- 1, (JSValueConst *)&err); -+ 1, &err); - JS_FreeValue(ctx, ret); /* XXX: what to do if exception ? */ - JS_FreeValue(ctx, err); - JS_FreeCString(ctx, basename); -@@ -28343,7 +28423,7 @@ static JSValue js_evaluate_module(JSContext *ctx, JSModuleDef *m) - return ret_val; - } - --static __exception JSAtom js_parse_from_clause(JSParseState *s) -+static warn_unused JSAtom js_parse_from_clause(JSParseState *s) - { - JSAtom module_name; - if (!token_is_pseudo_keyword(s, JS_ATOM_from)) { -@@ -28366,7 +28446,7 @@ static __exception JSAtom js_parse_from_clause(JSParseState *s) - return module_name; - } - --static __exception int js_parse_export(JSParseState *s) -+static warn_unused int js_parse_export(JSParseState *s) - { - JSContext *ctx = s->ctx; - JSModuleDef *m = s->cur_func->module; -@@ -28569,7 +28649,7 @@ static int add_import(JSParseState *s, JSModuleDef *m, - return 0; - } - --static __exception int js_parse_import(JSParseState *s) -+static warn_unused int js_parse_import(JSParseState *s) - { - JSContext *ctx = s->ctx; - JSModuleDef *m = s->cur_func->module; -@@ -28684,7 +28764,7 @@ static __exception int js_parse_import(JSParseState *s) - return js_parse_expect_semi(s); - } - --static __exception int js_parse_source_element(JSParseState *s) -+static warn_unused int js_parse_source_element(JSParseState *s) - { - JSFunctionDef *fd = s->cur_func; - int tok; -@@ -29608,8 +29688,7 @@ static int resolve_scope_var(JSContext *ctx, JSFunctionDef *s, - } - var_idx = idx; - break; -- } else -- if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) { -+ } else if (vd->var_name == JS_ATOM__with_ && !is_pseudo_var) { - dbuf_putc(bc, OP_get_loc); - dbuf_put_u16(bc, idx); - var_object_test(ctx, s, var_name, op, bc, &label_done, 1); -@@ -30359,7 +30438,7 @@ static void set_closure_from_var(JSContext *ctx, JSClosureVar *cv, - - /* for direct eval compilation: add references to the variables of the - calling function */ --static __exception int add_closure_variables(JSContext *ctx, JSFunctionDef *s, -+static warn_unused int add_closure_variables(JSContext *ctx, JSFunctionDef *s, - JSFunctionBytecode *b, int scope_idx) - { - int i, count; -@@ -30779,7 +30858,7 @@ static int get_label_pos(JSFunctionDef *s, int label) - - /* convert global variable accesses to local variables or closure - variables when necessary */ --static __exception int resolve_variables(JSContext *ctx, JSFunctionDef *s) -+static warn_unused int resolve_variables(JSContext *ctx, JSFunctionDef *s) - { - int pos, pos_next, bc_len, op, len, i, idx, line_num; - uint8_t *bc_buf; -@@ -31343,7 +31422,7 @@ static void put_short_code(DynBuf *bc_out, int op, int idx) - } - - /* peephole optimizations and resolve goto/labels */ --static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) -+static warn_unused int resolve_labels(JSContext *ctx, JSFunctionDef *s) - { - int pos, pos_next, bc_len, op, op1, len, i, line_num; - const uint8_t *bc_buf; -@@ -32152,8 +32231,7 @@ static __exception int resolve_labels(JSContext *ctx, JSFunctionDef *s) - bc_out.buf[pos - 1] = jp->op = OP_if_false8 + (op - OP_if_false); - } - goto shrink; -- } else -- if (diff == (int16_t)diff && op == OP_goto) { -+ } else if (diff == (int16_t)diff && op == OP_goto) { - //put_u16(bc_out.buf + pos, diff); - jp->size = 2; - delta = 2; -@@ -32236,7 +32314,7 @@ typedef struct StackSizeState { - } StackSizeState; - - /* 'op' is only used for error indication */ --static __exception int ss_check(JSContext *ctx, StackSizeState *s, -+static warn_unused int ss_check(JSContext *ctx, StackSizeState *s, - int pos, int op, int stack_len) - { - if ((unsigned)pos >= s->bc_len) { -@@ -32272,7 +32350,7 @@ static __exception int ss_check(JSContext *ctx, StackSizeState *s, - return 0; - } - --static __exception int compute_stack_size(JSContext *ctx, -+static warn_unused int compute_stack_size(JSContext *ctx, - JSFunctionDef *fd, - int *pstack_size) - { -@@ -32593,7 +32671,9 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd) - } - } else { - b->vardefs = (void *)((uint8_t*)b + vardefs_offset); -+ if (fd->arg_count) - memcpy(b->vardefs, fd->args, fd->arg_count * sizeof(fd->args[0])); -+ if (fd->var_count) - memcpy(b->vardefs + fd->arg_count, fd->vars, fd->var_count * sizeof(fd->vars[0])); - } - b->var_count = fd->var_count; -@@ -32721,7 +32801,7 @@ static void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b) - } - } - --static __exception int js_parse_directives(JSParseState *s) -+static warn_unused int js_parse_directives(JSParseState *s) - { - char str[20]; - JSParsePos pos; -@@ -32895,7 +32975,7 @@ static JSFunctionDef *js_parse_function_class_fields_init(JSParseState *s) - - /* func_name must be JS_ATOM_NULL for JS_PARSE_FUNC_STATEMENT and - JS_PARSE_FUNC_EXPR, JS_PARSE_FUNC_ARROW and JS_PARSE_FUNC_VAR */ --static __exception int js_parse_function_decl2(JSParseState *s, -+static warn_unused int js_parse_function_decl2(JSParseState *s, - JSParseFunctionEnum func_type, - JSFunctionKindEnum func_kind, - JSAtom func_name, -@@ -33443,7 +33523,7 @@ done: - return -1; - } - --static __exception int js_parse_function_decl(JSParseState *s, -+static warn_unused int js_parse_function_decl(JSParseState *s, - JSParseFunctionEnum func_type, - JSFunctionKindEnum func_kind, - JSAtom func_name, -@@ -33455,7 +33535,7 @@ static __exception int js_parse_function_decl(JSParseState *s, - NULL); - } - --static __exception int js_parse_program(JSParseState *s) -+static warn_unused int js_parse_program(JSParseState *s) - { - JSFunctionDef *fd = s->cur_func; - int idx; -@@ -33497,12 +33577,12 @@ static __exception int js_parse_program(JSParseState *s) - - static void js_parse_init(JSContext *ctx, JSParseState *s, - const char *input, size_t input_len, -- const char *filename) -+ const char *filename, int line) - { - memset(s, 0, sizeof(*s)); - s->ctx = ctx; - s->filename = filename; -- s->line_num = 1; -+ s->line_num = line; - s->buf_ptr = (const uint8_t *)input; - s->buf_end = s->buf_ptr + input_len; - s->token.val = ' '; -@@ -33573,9 +33653,9 @@ static void skip_shebang(JSParseState *s) - } - - /* 'input' must be zero terminated i.e. input[input_len] = '\0'. */ --static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, -+static JSValue JS_EvalInternalImpl(JSContext *ctx, JSValueConst this_obj, - const char *input, size_t input_len, -- const char *filename, int flags, int scope_idx) -+ const char *filename, int line, int flags, int scope_idx) - { - JSParseState s1, *s = &s1; - int err, js_mode, eval_type; -@@ -33586,7 +33666,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, - JSFunctionDef *fd; - JSModuleDef *m; - -- js_parse_init(ctx, s, input, input_len, filename); -+ js_parse_init(ctx, s, input, input_len, filename, line); - skip_shebang(s); - - eval_type = flags & JS_EVAL_TYPE_MASK; -@@ -33620,7 +33700,7 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, - js_mode |= JS_MODE_STRICT; - } - } -- fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, 1); -+ fd = js_new_function_def(ctx, NULL, TRUE, FALSE, filename, line); - if (!fd) - goto fail1; - s->cur_func = fd; -@@ -33686,12 +33766,12 @@ static JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, - /* the indirection is needed to make 'eval' optional */ - static JSValue JS_EvalInternal(JSContext *ctx, JSValueConst this_obj, - const char *input, size_t input_len, -- const char *filename, int flags, int scope_idx) -+ const char *filename, int line, int flags, int scope_idx) - { - if (unlikely(!ctx->eval_internal)) { - return JS_ThrowTypeError(ctx, "eval is not supported"); - } -- return ctx->eval_internal(ctx, this_obj, input, input_len, filename, -+ return ctx->eval_internal(ctx, this_obj, input, input_len, filename, line, - flags, scope_idx); - } - -@@ -33707,7 +33787,7 @@ static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, - str = JS_ToCStringLen(ctx, &len, val); - if (!str) - return JS_EXCEPTION; -- ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", flags, scope_idx); -+ ret = JS_EvalInternal(ctx, this_obj, str, len, "<input>", 1, flags, scope_idx); - JS_FreeCString(ctx, str); - return ret; - -@@ -33715,14 +33795,14 @@ static JSValue JS_EvalObject(JSContext *ctx, JSValueConst this_obj, - - JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj, - const char *input, size_t input_len, -- const char *filename, int eval_flags) -+ const char *filename, int line, int eval_flags) - { - int eval_type = eval_flags & JS_EVAL_TYPE_MASK; - JSValue ret; - - assert(eval_type == JS_EVAL_TYPE_GLOBAL || - eval_type == JS_EVAL_TYPE_MODULE); -- ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename, -+ ret = JS_EvalInternal(ctx, this_obj, input, input_len, filename, line, - eval_flags, -1); - return ret; - } -@@ -33730,7 +33810,7 @@ JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj, - JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, - const char *filename, int eval_flags) - { -- return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename, -+ return JS_EvalThis(ctx, ctx->global_obj, input, input_len, filename, 1, - eval_flags); - } - -@@ -33948,7 +34028,7 @@ static void bc_put_u16(BCWriterState *s, uint16_t v) - dbuf_put_u16(&s->dbuf, v); - } - --static __maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v) -+static maybe_unused void bc_put_u32(BCWriterState *s, uint32_t v) - { - if (s->byte_swap) - v = bswap32(v); -@@ -33982,7 +34062,7 @@ static int bc_atom_to_idx(BCWriterState *s, uint32_t *pres, JSAtom atom) - { - uint32_t v; - -- if (atom < s->first_atom || __JS_AtomIsTaggedInt(atom)) { -+ if (atom < s->first_atom || JS_AtomIsTaggedInt(atom)) { - *pres = atom; - return 0; - } -@@ -34022,8 +34102,8 @@ static int bc_put_atom(BCWriterState *s, JSAtom atom) - { - uint32_t v; - -- if (__JS_AtomIsTaggedInt(atom)) { -- v = (__JS_AtomToUInt32(atom) << 1) | 1; -+ if (JS_AtomIsTaggedInt(atom)) { -+ v = (JS_AtomToUInt32(atom) << 1) | 1; - } else { - if (bc_atom_to_idx(s, &v, atom)) - return -1; -@@ -34142,7 +34222,7 @@ static int JS_WriteFunctionBytecode(BCWriterState *s, - static void JS_WriteString(BCWriterState *s, JSString *p) - { - int i; -- bc_put_leb128(s, ((uint32_t)p->len << 1) | p->is_wide_char); -+ bc_put_leb128(s, (p->len << 1) | p->is_wide_char); - if (p->is_wide_char) { - for(i = 0; i < p->len; i++) - bc_put_u16(s, p->u.str16[i]); -@@ -34800,7 +34880,7 @@ typedef struct BCReaderState { - } BCReaderState; - - #ifdef DUMP_READ_OBJECT --static void __attribute__((format(printf, 2, 3))) bc_read_trace(BCReaderState *s, const char *fmt, ...) { -+static void FORMAT_ATTR(2, 3) bc_read_trace(BCReaderState *s, const char *fmt, ...) { - va_list ap; - int i, n, n0; - -@@ -34863,7 +34943,7 @@ static int bc_get_u16(BCReaderState *s, uint16_t *pval) - return 0; - } - --static __maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval) -+static maybe_unused int bc_get_u32(BCReaderState *s, uint32_t *pval) - { - if (unlikely(s->buf_end - s->ptr < 4)) { - *pval = 0; /* avoid warning */ -@@ -34937,7 +35017,7 @@ static int bc_idx_to_atom(BCReaderState *s, JSAtom *patom, uint32_t idx) - { - JSAtom atom; - -- if (__JS_AtomIsTaggedInt(idx)) { -+ if (JS_AtomIsTaggedInt(idx)) { - atom = idx; - } else if (idx < s->first_atom) { - atom = JS_DupAtom(s->ctx, idx); -@@ -34961,7 +35041,7 @@ static int bc_get_atom(BCReaderState *s, JSAtom *patom) - if (bc_get_leb128(s, &v)) - return -1; - if (v & 1) { -- *patom = __JS_AtomFromUInt32(v >> 1); -+ *patom = JS_AtomFromUInt32(v >> 1); - return 0; - } else { - return bc_idx_to_atom(s, patom, v >> 1); -@@ -35776,7 +35856,7 @@ static JSValue JS_ReadObjectRec(BCReaderState *s) - if (bc_get_u64(s, &u.u64)) - return JS_EXCEPTION; - bc_read_trace(s, "%g\n", u.d); -- obj = __JS_NewFloat64(ctx, u.d); -+ obj = JS_NewFloat64Impl(ctx, u.d); - } - break; - case BC_TAG_STRING: -@@ -36084,7 +36164,7 @@ static int JS_InstantiateFunctionListItem(JSContext *ctx, JSValueConst obj, - val = JS_NewInt64(ctx, e->u.i64); - break; - case JS_DEF_PROP_DOUBLE: -- val = __JS_NewFloat64(ctx, e->u.f64); -+ val = JS_NewFloat64Impl(ctx, e->u.f64); - break; - case JS_DEF_PROP_UNDEFINED: - val = JS_UNDEFINED; -@@ -36148,7 +36228,7 @@ int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, - val = JS_NewInt64(ctx, e->u.i64); - break; - case JS_DEF_PROP_DOUBLE: -- val = __JS_NewFloat64(ctx, e->u.f64); -+ val = JS_NewFloat64Impl(ctx, e->u.f64); - break; - case JS_DEF_OBJECT: - val = JS_NewObject(ctx); -@@ -36381,7 +36461,7 @@ static int js_obj_to_desc(JSContext *ctx, JSPropertyDescriptor *d, - return -1; - } - --static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj, -+static warn_unused int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj, - JSAtom prop, JSValueConst desc, - int flags) - { -@@ -36397,7 +36477,7 @@ static __exception int JS_DefinePropertyDesc(JSContext *ctx, JSValueConst obj, - return ret; - } - --static __exception int JS_ObjectDefineProperties(JSContext *ctx, -+static warn_unused int JS_ObjectDefineProperties(JSContext *ctx, - JSValueConst obj, - JSValueConst properties) - { -@@ -36882,32 +36962,6 @@ static JSValue js_object_hasOwnProperty(JSContext *ctx, JSValueConst this_val, - return JS_NewBool(ctx, ret); - } - --static JSValue js_object_hasOwn(JSContext *ctx, JSValueConst this_val, -- int argc, JSValueConst *argv) --{ -- JSValue obj; -- JSAtom atom; -- JSObject *p; -- BOOL ret; -- -- obj = JS_ToObject(ctx, argv[0]); -- if (JS_IsException(obj)) -- return obj; -- atom = JS_ValueToAtom(ctx, argv[1]); -- if (unlikely(atom == JS_ATOM_NULL)) { -- JS_FreeValue(ctx, obj); -- return JS_EXCEPTION; -- } -- p = JS_VALUE_GET_OBJ(obj); -- ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom); -- JS_FreeAtom(ctx, atom); -- JS_FreeValue(ctx, obj); -- if (ret < 0) -- return JS_EXCEPTION; -- else -- return JS_NewBool(ctx, ret); --} -- - static JSValue js_object_valueOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) - { -@@ -37447,6 +37501,32 @@ exception: - return res; - } - -+static JSValue js_object_hasOwn(JSContext *ctx, JSValueConst this_val, -+ int argc, JSValueConst *argv) -+{ -+ JSValue obj; -+ JSAtom atom; -+ JSObject *p; -+ BOOL ret; -+ -+ obj = JS_ToObject(ctx, argv[0]); -+ if (JS_IsException(obj)) -+ return obj; -+ atom = JS_ValueToAtom(ctx, argv[1]); -+ if (unlikely(atom == JS_ATOM_NULL)) { -+ JS_FreeValue(ctx, obj); -+ return JS_EXCEPTION; -+ } -+ p = JS_VALUE_GET_OBJ(obj); -+ ret = JS_GetOwnPropertyInternal(ctx, NULL, p, atom); -+ JS_FreeAtom(ctx, atom); -+ JS_FreeValue(ctx, obj); -+ if (ret < 0) -+ return JS_EXCEPTION; -+ else -+ return JS_NewBool(ctx, ret); -+} -+ - static const JSCFunctionListEntry js_object_funcs[] = { - JS_CFUNC_DEF("create", 2, js_object_create ), - JS_CFUNC_MAGIC_DEF("getPrototypeOf", 1, js_object_getPrototypeOf, 0 ), -@@ -37576,7 +37656,7 @@ static JSValue js_function_constructor(JSContext *ctx, JSValueConst new_target, - return JS_EXCEPTION; - } - --static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, -+static warn_unused int js_get_length32(JSContext *ctx, uint32_t *pres, - JSValueConst obj) - { - JSValue len_val; -@@ -37588,7 +37668,7 @@ static __exception int js_get_length32(JSContext *ctx, uint32_t *pres, - return JS_ToUint32Free(ctx, pres, len_val); - } - --static __exception int js_get_length64(JSContext *ctx, int64_t *pres, -+static warn_unused int js_get_length64(JSContext *ctx, int64_t *pres, - JSValueConst obj) - { - JSValue len_val; -@@ -37673,9 +37753,9 @@ static JSValue js_function_apply(JSContext *ctx, JSValueConst this_val, - if (!tab) - return JS_EXCEPTION; - if (magic & 1) { -- ret = JS_CallConstructor2(ctx, this_val, this_arg, len, (JSValueConst *)tab); -+ ret = JS_CallConstructor2(ctx, this_val, this_arg, len, tab); - } else { -- ret = JS_Call(ctx, this_val, this_arg, len, (JSValueConst *)tab); -+ ret = JS_Call(ctx, this_val, this_arg, len, tab); - } - free_arg_list(ctx, tab, len); - return ret; -@@ -38889,8 +38969,7 @@ static JSValue js_array_toString(JSContext *ctx, JSValueConst this_val, - method = JS_GetProperty(ctx, obj, JS_ATOM_join); - if (JS_IsException(method)) { - ret = JS_EXCEPTION; -- } else -- if (!JS_IsFunction(ctx, method)) { -+ } else if (!JS_IsFunction(ctx, method)) { - /* Use intrinsic Object.prototype.toString */ - JS_FreeValue(ctx, method); - ret = js_object_toString(ctx, obj, 0, NULL); -@@ -39138,8 +39217,7 @@ static JSValue js_array_slice(JSContext *ctx, JSValueConst this_val, - if (argc == 0) { - item_count = 0; - del_count = 0; -- } else -- if (argc == 1) { -+ } else if (argc == 1) { - item_count = 0; - del_count = len - start; - } else { -@@ -39283,8 +39361,8 @@ static int64_t JS_FlattenIntoArray(JSContext *ctx, JSValueConst target, - if (!JS_IsUndefined(mapperFunction)) { - JSValueConst args[3] = { element, JS_NewInt64(ctx, sourceIndex), source }; - element = JS_Call(ctx, mapperFunction, thisArg, 3, args); -- JS_FreeValue(ctx, (JSValue)args[0]); -- JS_FreeValue(ctx, (JSValue)args[1]); -+ JS_FreeValue(ctx, args[0]); -+ JS_FreeValue(ctx, args[1]); - if (JS_IsException(element)) - return -1; - } -@@ -39915,7 +39993,7 @@ static JSValue js_number_toFixed(JSContext *ctx, JSValueConst this_val, - if (f < 0 || f > 100) - return JS_ThrowRangeError(ctx, "invalid number of digits"); - if (fabs(d) >= 1e21) { -- return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); -+ return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d)); - } else { - return js_dtoa(ctx, d, 10, f, JS_DTOA_FRAC_FORMAT); - } -@@ -39936,7 +40014,7 @@ static JSValue js_number_toExponential(JSContext *ctx, JSValueConst this_val, - if (JS_ToInt32Sat(ctx, &f, argv[0])) - return JS_EXCEPTION; - if (!isfinite(d)) { -- return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); -+ return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d)); - } - if (JS_IsUndefined(argv[0])) { - flags = 0; -@@ -39968,7 +40046,7 @@ static JSValue js_number_toPrecision(JSContext *ctx, JSValueConst this_val, - return JS_EXCEPTION; - if (!isfinite(d)) { - to_string: -- return JS_ToStringFree(ctx, __JS_NewFloat64(ctx, d)); -+ return JS_ToStringFree(ctx, JS_NewFloat64Impl(ctx, d)); - } - if (p < 1 || p > 100) - return JS_ThrowRangeError(ctx, "invalid number of digits"); -@@ -40089,11 +40167,11 @@ static int js_string_get_own_property(JSContext *ctx, - uint32_t idx, ch; - - /* This is a class exotic method: obj class_id is JS_CLASS_STRING */ -- if (__JS_AtomIsTaggedInt(prop)) { -+ if (JS_AtomIsTaggedInt(prop)) { - p = JS_VALUE_GET_OBJ(obj); - if (JS_VALUE_GET_TAG(p->u.object_data) == JS_TAG_STRING) { - p1 = JS_VALUE_GET_STRING(p->u.object_data); -- idx = __JS_AtomToUInt32(prop); -+ idx = JS_AtomToUInt32(prop); - if (idx < p1->len) { - if (desc) { - if (p1->is_wide_char) -@@ -40122,8 +40200,8 @@ static int js_string_define_own_property(JSContext *ctx, - JSObject *p; - JSString *p1, *p2; - -- if (__JS_AtomIsTaggedInt(prop)) { -- idx = __JS_AtomToUInt32(prop); -+ if (JS_AtomIsTaggedInt(prop)) { -+ idx = JS_AtomToUInt32(prop); - p = JS_VALUE_GET_OBJ(this_obj); - if (JS_VALUE_GET_TAG(p->u.object_data) != JS_TAG_STRING) - goto def; -@@ -40157,8 +40235,8 @@ static int js_string_delete_property(JSContext *ctx, - { - uint32_t idx; - -- if (__JS_AtomIsTaggedInt(prop)) { -- idx = __JS_AtomToUInt32(prop); -+ if (JS_AtomIsTaggedInt(prop)) { -+ idx = JS_AtomToUInt32(prop); - if (idx < js_string_obj_get_length(ctx, obj)) { - return FALSE; - } -@@ -40701,7 +40779,7 @@ static JSValue js_string_match(JSContext *ctx, JSValueConst this_val, - str = JS_NewString(ctx, "g"); - if (JS_IsException(str)) - goto fail; -- args[args_len++] = (JSValueConst)str; -+ args[args_len++] = str; - } - rx = JS_CallConstructor(ctx, ctx->regexp_ctor, args_len, args); - JS_FreeValue(ctx, str); -@@ -40710,7 +40788,7 @@ static JSValue js_string_match(JSContext *ctx, JSValueConst this_val, - JS_FreeValue(ctx, S); - return JS_EXCEPTION; - } -- result = JS_InvokeFree(ctx, rx, atom, 1, (JSValueConst *)&S); -+ result = JS_InvokeFree(ctx, rx, atom, 1, &S); - JS_FreeValue(ctx, S); - return result; - } -@@ -41759,7 +41837,7 @@ static JSValue js_math_min_max(JSContext *ctx, JSValueConst this_val, - uint32_t tag; - - if (unlikely(argc == 0)) { -- return __JS_NewFloat64(ctx, is_max ? -1.0 / 0.0 : 1.0 / 0.0); -+ return JS_NewFloat64Impl(ctx, is_max ? INFINITY : -INFINITY); - } - - tag = JS_VALUE_GET_TAG(argv[0]); -@@ -41911,9 +41989,13 @@ static uint64_t xorshift64star(uint64_t *pstate) - - static void js_random_init(JSContext *ctx) - { -+#ifdef _MSC_VER -+ ctx->random_state = time(NULL); -+#else - struct timeval tv; - gettimeofday(&tv, NULL); - ctx->random_state = ((int64_t)tv.tv_sec * 1000000) + tv.tv_usec; -+#endif - /* the state must be non zero */ - if (ctx->random_state == 0) - ctx->random_state = 1; -@@ -41928,15 +42010,21 @@ static JSValue js_math_random(JSContext *ctx, JSValueConst this_val, - v = xorshift64star(&ctx->random_state); - /* 1.0 <= u.d < 2 */ - u.u64 = ((uint64_t)0x3ff << 52) | (v >> 12); -- return __JS_NewFloat64(ctx, u.d - 1.0); -+ return JS_NewFloat64Impl(ctx, u.d - 1.0); - } - -+// MSVC inexplicably refuses to initialize the array below with -+// these functions, so use wrappers. -+static double floorWrapper(double x) { return floor(x); } -+static double ceilWrapper(double x) { return ceil(x); } -+static double log2Wrapper(double x) { return log2(x); } -+ - static const JSCFunctionListEntry js_math_funcs[] = { - JS_CFUNC_MAGIC_DEF("min", 2, js_math_min_max, 0 ), - JS_CFUNC_MAGIC_DEF("max", 2, js_math_min_max, 1 ), - JS_CFUNC_SPECIAL_DEF("abs", 1, f_f, fabs ), -- JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floor ), -- JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceil ), -+ JS_CFUNC_SPECIAL_DEF("floor", 1, f_f, floorWrapper ), -+ JS_CFUNC_SPECIAL_DEF("ceil", 1, f_f, ceilWrapper ), - JS_CFUNC_SPECIAL_DEF("round", 1, f_f, js_math_round ), - JS_CFUNC_SPECIAL_DEF("sqrt", 1, f_f, sqrt ), - -@@ -41961,7 +42049,7 @@ static const JSCFunctionListEntry js_math_funcs[] = { - JS_CFUNC_SPECIAL_DEF("atanh", 1, f_f, atanh ), - JS_CFUNC_SPECIAL_DEF("expm1", 1, f_f, expm1 ), - JS_CFUNC_SPECIAL_DEF("log1p", 1, f_f, log1p ), -- JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2 ), -+ JS_CFUNC_SPECIAL_DEF("log2", 1, f_f, log2Wrapper ), - JS_CFUNC_SPECIAL_DEF("log10", 1, f_f, log10 ), - JS_CFUNC_SPECIAL_DEF("cbrt", 1, f_f, cbrt ), - JS_CFUNC_DEF("hypot", 2, js_math_hypot ), -@@ -42004,9 +42092,16 @@ static JSValue js___date_clock(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv) - { - int64_t d; -+#ifdef _MSC_VER -+ SYSTEMTIME st; -+ GetSystemTime(&st); -+ SystemTimeToFileTime(&st, (FILETIME *) &d); -+ d /= 10; -+#else - struct timeval tv; - gettimeofday(&tv, NULL); - d = (int64_t)tv.tv_sec * 1000000 + tv.tv_usec; -+#endif - return JS_NewInt64(ctx, d); - } - -@@ -43686,7 +43781,7 @@ JSValue JS_ParseJSON2(JSContext *ctx, const char *buf, size_t buf_len, - JSParseState s1, *s = &s1; - JSValue val = JS_UNDEFINED; - -- js_parse_init(ctx, s, buf, buf_len, filename); -+ js_parse_init(ctx, s, buf, buf_len, filename, 1); - s->ext_json = ((flags & JS_PARSE_JSON_EXT) != 0); - if (json_next_token(s)) - goto fail; -@@ -43937,7 +44032,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, - goto exception; - } - #endif -- v = js_array_includes(ctx, jsc->stack, 1, (JSValueConst *)&val); -+ v = js_array_includes(ctx, jsc->stack, 1, &val); - if (JS_IsException(v)) - goto exception; - if (JS_ToBoolFree(ctx, v)) { -@@ -43958,7 +44053,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, - sep = JS_DupValue(ctx, jsc->empty); - sep1 = JS_DupValue(ctx, jsc->empty); - } -- v = js_array_push(ctx, jsc->stack, 1, (JSValueConst *)&val, 0); -+ v = js_array_push(ctx, jsc->stack, 1, &val, 0); - if (check_exception_free(ctx, v)) - goto exception; - ret = JS_IsArray(ctx, val); -@@ -43998,7 +44093,7 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc, - if (!JS_IsUndefined(jsc->property_list)) - tab = JS_DupValue(ctx, jsc->property_list); - else -- tab = js_object_keys(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val, JS_ITERATOR_KIND_KEY); -+ tab = js_object_keys(ctx, JS_UNDEFINED, 1, &val, JS_ITERATOR_KIND_KEY); - if (JS_IsException(tab)) - goto exception; - if (js_get_length64(ctx, &len, tab)) -@@ -44146,7 +44241,7 @@ JSValue JS_JSONStringify(JSContext *ctx, JSValueConst obj, - continue; - } - present = js_array_includes(ctx, jsc->property_list, -- 1, (JSValueConst *)&v); -+ 1, &v); - if (JS_IsException(present)) { - JS_FreeValue(ctx, v); - goto exception; -@@ -44270,7 +44365,7 @@ static JSValue js_reflect_construct(JSContext *ctx, JSValueConst this_val, - tab = build_arg_list(ctx, &len, array_arg); - if (!tab) - return JS_EXCEPTION; -- ret = JS_CallConstructor2(ctx, func, new_target, len, (JSValueConst *)tab); -+ ret = JS_CallConstructor2(ctx, func, new_target, len, tab); - free_arg_list(ctx, tab, len); - return ret; - } -@@ -44475,7 +44570,7 @@ static JSValue js_proxy_getPrototypeOf(JSContext *ctx, JSValueConst obj) - return JS_EXCEPTION; - if (JS_IsUndefined(method)) - return JS_GetPrototype(ctx, s->target); -- ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); -+ ret = JS_CallFree(ctx, method, s->handler, 1, &s->target); - if (JS_IsException(ret)) - return ret; - if (JS_VALUE_GET_TAG(ret) != JS_TAG_NULL && -@@ -44562,7 +44657,7 @@ static int js_proxy_isExtensible(JSContext *ctx, JSValueConst obj) - return -1; - if (JS_IsUndefined(method)) - return JS_IsExtensible(ctx, s->target); -- ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); -+ ret = JS_CallFree(ctx, method, s->handler, 1, &s->target); - if (JS_IsException(ret)) - return -1; - res = JS_ToBoolFree(ctx, ret); -@@ -44588,7 +44683,7 @@ static int js_proxy_preventExtensions(JSContext *ctx, JSValueConst obj) - return -1; - if (JS_IsUndefined(method)) - return JS_PreventExtensions(ctx, s->target); -- ret = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); -+ ret = JS_CallFree(ctx, method, s->handler, 1, &s->target); - if (JS_IsException(ret)) - return -1; - res = JS_ToBoolFree(ctx, ret); -@@ -45072,7 +45167,7 @@ static int js_proxy_get_own_property_names(JSContext *ctx, - JS_VALUE_GET_OBJ(s->target), - JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK); - } -- prop_array = JS_CallFree(ctx, method, s->handler, 1, (JSValueConst *)&s->target); -+ prop_array = JS_CallFree(ctx, method, s->handler, 1, &s->target); - if (JS_IsException(prop_array)) - return -1; - tab = NULL; -@@ -45408,7 +45503,7 @@ static JSValue js_symbol_toString(JSContext *ctx, JSValueConst this_val, - if (JS_IsException(val)) - return val; - /* XXX: use JS_ToStringInternal() with a flags */ -- ret = js_string_constructor(ctx, JS_UNDEFINED, 1, (JSValueConst *)&val); -+ ret = js_string_constructor(ctx, JS_UNDEFINED, 1, &val); - JS_FreeValue(ctx, val); - return ret; - } -@@ -45558,7 +45653,7 @@ static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target, - break; - } - if (is_set) { -- ret = JS_Call(ctx, adder, obj, 1, (JSValueConst *)&item); -+ ret = JS_Call(ctx, adder, obj, 1, &item); - if (JS_IsException(ret)) { - JS_FreeValue(ctx, item); - goto fail; -@@ -45729,7 +45824,7 @@ static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s, - } else { - JS_DupValue(ctx, key); - } -- mr->key = (JSValue)key; -+ mr->key = key; - h = map_hash_key(ctx, key) & (s->hash_size - 1); - list_add_tail(&mr->hash_link, &s->hash_table[h]); - list_add_tail(&mr->link, &s->records); -@@ -45951,7 +46046,7 @@ static JSValue js_map_forEach(JSContext *ctx, JSValueConst this_val, - args[0] = args[1]; - else - args[0] = JS_DupValue(ctx, mr->value); -- args[2] = (JSValue)this_val; -+ args[2] = this_val; - ret = JS_Call(ctx, func, this_arg, 3, (JSValueConst *)args); - JS_FreeValue(ctx, args[0]); - if (!magic) -@@ -46343,7 +46438,7 @@ static JSValue promise_reaction_job(JSContext *ctx, int argc, - functions */ - if (!JS_IsUndefined(func)) { - res2 = JS_Call(ctx, func, JS_UNDEFINED, -- 1, (JSValueConst *)&res); -+ 1, &res); - } else { - res2 = JS_UNDEFINED; - } -@@ -46426,7 +46521,7 @@ static JSValue js_promise_resolve_thenable_job(JSContext *ctx, - res = JS_Call(ctx, then, thenable, 2, (JSValueConst *)args); - if (JS_IsException(res)) { - JSValue error = JS_GetException(ctx); -- res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); -+ res = JS_Call(ctx, args[1], JS_UNDEFINED, 1, &error); - JS_FreeValue(ctx, error); - } - JS_FreeValue(ctx, args[0]); -@@ -46626,7 +46721,7 @@ static JSValue js_promise_constructor(JSContext *ctx, JSValueConst new_target, - if (JS_IsException(ret)) { - JSValue ret2, error; - error = JS_GetException(ctx); -- ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, (JSValueConst *)&error); -+ ret2 = JS_Call(ctx, args[1], JS_UNDEFINED, 1, &error); - JS_FreeValue(ctx, error); - if (JS_IsException(ret2)) - goto fail1; -@@ -46683,10 +46778,10 @@ static JSValue js_new_promise_capability(JSContext *ctx, - - if (JS_IsUndefined(ctor)) { - result_promise = js_promise_constructor(ctx, ctor, 1, -- (JSValueConst *)&executor); -+ &executor); - } else { - result_promise = JS_CallConstructor(ctx, ctor, 1, -- (JSValueConst *)&executor); -+ &executor); - } - if (JS_IsException(result_promise)) - goto fail; -@@ -46770,7 +46865,7 @@ static JSValue js_promise___newPromiseCapability(JSContext *ctx, - } - #endif - --static __exception int remainingElementsCount_add(JSContext *ctx, -+static warn_unused int remainingElementsCount_add(JSContext *ctx, - JSValueConst resolve_element_env, - int addend) - { -@@ -46851,10 +46946,10 @@ static JSValue js_promise_all_resolve_element(JSContext *ctx, - error = js_aggregate_error_constructor(ctx, values); - if (JS_IsException(error)) - return JS_EXCEPTION; -- ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&error); -+ ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, &error); - JS_FreeValue(ctx, error); - } else { -- ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, (JSValueConst *)&values); -+ ret = JS_Call(ctx, resolve, JS_UNDEFINED, 1, &values); - } - if (JS_IsException(ret)) - return ret; -@@ -46890,7 +46985,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, - fail_reject: - error = JS_GetException(ctx); - ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, -- (JSValueConst *)&error); -+ &error); - JS_FreeValue(ctx, error); - if (JS_IsException(ret)) - goto fail; -@@ -46921,7 +47016,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, - if (done) - break; - next_promise = JS_Call(ctx, promise_resolve, -- this_val, 1, (JSValueConst *)&item); -+ this_val, 1, &item); - JS_FreeValue(ctx, item); - if (JS_IsException(next_promise)) { - fail_reject1: -@@ -46929,7 +47024,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, - goto fail_reject; - } - resolve_element_data[0] = JS_NewBool(ctx, FALSE); -- resolve_element_data[1] = (JSValueConst)JS_NewInt32(ctx, index); -+ resolve_element_data[1] = JS_NewInt32(ctx, index); - resolve_element_data[2] = values; - resolve_element_data[3] = resolving_funcs[is_promise_any]; - resolve_element_data[4] = resolve_element_env; -@@ -46989,7 +47084,7 @@ static JSValue js_promise_all(JSContext *ctx, JSValueConst this_val, - values = error; - } - ret = JS_Call(ctx, resolving_funcs[is_promise_any], JS_UNDEFINED, -- 1, (JSValueConst *)&values); -+ 1, &values); - if (check_exception_free(ctx, ret)) - goto fail_reject; - } -@@ -47032,7 +47127,7 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, - fail_reject: - error = JS_GetException(ctx); - ret = JS_Call(ctx, resolving_funcs[1], JS_UNDEFINED, 1, -- (JSValueConst *)&error); -+ &error); - JS_FreeValue(ctx, error); - if (JS_IsException(ret)) - goto fail; -@@ -47051,7 +47146,7 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, - if (done) - break; - next_promise = JS_Call(ctx, promise_resolve, -- this_val, 1, (JSValueConst *)&item); -+ this_val, 1, &item); - JS_FreeValue(ctx, item); - if (JS_IsException(next_promise)) { - fail_reject1: -@@ -47078,7 +47173,7 @@ static JSValue js_promise_race(JSContext *ctx, JSValueConst this_val, - goto done; - } - --static __exception int perform_promise_then(JSContext *ctx, -+static warn_unused int perform_promise_then(JSContext *ctx, - JSValueConst promise, - JSValueConst *resolve_reject, - JSValueConst *cap_resolving_funcs) -@@ -47196,7 +47291,7 @@ static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_va - res = JS_Call(ctx, onFinally, JS_UNDEFINED, 0, NULL); - if (JS_IsException(res)) - return res; -- promise = js_promise_resolve(ctx, ctor, 1, (JSValueConst *)&res, 0); -+ promise = js_promise_resolve(ctx, ctor, 1, &res, 0); - JS_FreeValue(ctx, res); - if (JS_IsException(promise)) - return promise; -@@ -47211,7 +47306,7 @@ static JSValue js_promise_then_finally_func(JSContext *ctx, JSValueConst this_va - JS_FreeValue(ctx, promise); - return then_func; - } -- ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, (JSValueConst *)&then_func); -+ ret = JS_InvokeFree(ctx, promise, JS_ATOM_then, 1, &then_func); - JS_FreeValue(ctx, then_func); - return ret; - } -@@ -47288,7 +47383,7 @@ static JSValue js_async_from_sync_iterator_unwrap_func_create(JSContext *ctx, - { - JSValueConst func_data[1]; - -- func_data[0] = (JSValueConst)JS_NewBool(ctx, done); -+ func_data[0] = JS_NewBool(ctx, done); - return JS_NewCFunctionData(ctx, js_async_from_sync_iterator_unwrap, - 1, 0, 1, func_data); - } -@@ -47411,7 +47506,7 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst thi - is_reject = 1; - done_resolve: - res2 = JS_Call(ctx, resolving_funcs[is_reject], JS_UNDEFINED, -- 1, (JSValueConst *)&err); -+ 1, &err); - JS_FreeValue(ctx, err); - JS_FreeValue(ctx, res2); - JS_FreeValue(ctx, resolving_funcs[0]); -@@ -47423,7 +47518,7 @@ static JSValue js_async_from_sync_iterator_next(JSContext *ctx, JSValueConst thi - int res; - - value_wrapper_promise = js_promise_resolve(ctx, ctx->promise_ctor, -- 1, (JSValueConst *)&value, 0); -+ 1, &value, 0); - if (JS_IsException(value_wrapper_promise)) { - JS_FreeValue(ctx, value); - goto reject; -@@ -47593,7 +47688,7 @@ static int isURIReserved(int c) { - return c < 0x100 && memchr(";/?:@&=+$,#", c, sizeof(";/?:@&=+$,#") - 1) != NULL; - } - --static int __attribute__((format(printf, 2, 3))) js_throw_URIError(JSContext *ctx, const char *fmt, ...) -+static int FORMAT_ATTR(2, 3) js_throw_URIError(JSContext *ctx, const char *fmt, ...) - { - va_list ap; - -@@ -47894,7 +47989,7 @@ static int64_t floor_div(int64_t a, int64_t b) { - static JSValue js_Date_parse(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv); - --static __exception int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val) -+static warn_unused int JS_ThisTimeValue(JSContext *ctx, double *valp, JSValueConst this_val) - { - if (JS_VALUE_GET_TAG(this_val) == JS_TAG_OBJECT) { - JSObject *p = JS_VALUE_GET_OBJ(this_val); -@@ -47954,7 +48049,7 @@ static int const month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 - static char const month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; - static char const day_names[] = "SunMonTueWedThuFriSat"; - --static __exception int get_date_fields(JSContext *ctx, JSValueConst obj, -+static warn_unused int get_date_fields(JSContext *ctx, JSValueConst obj, - double fields[9], int is_local, int force) - { - double dval; -@@ -48016,7 +48111,7 @@ static double time_clip(double t) { - - /* The spec mandates the use of 'double' and it fixes the order - of the operations */ --static double set_date_fields(double fields[], int is_local) { -+static double set_date_fields(const double fields[], int is_local) { - int64_t y; - double days, d, h, m1; - int i, m, md; -@@ -48213,9 +48308,17 @@ static JSValue get_date_string(JSContext *ctx, JSValueConst this_val, - - /* OS dependent: return the UTC time in ms since 1970. */ - static int64_t date_now(void) { -+#ifdef _MSC_VER -+ SYSTEMTIME st; -+ GetSystemTime(&st); -+ int64_t d; -+ SystemTimeToFileTime(&st, (FILETIME *) &d); -+ return d /= 10000; -+#else - struct timeval tv; - gettimeofday(&tv, NULL); - return (int64_t)tv.tv_sec * 1000 + (tv.tv_usec / 1000); -+#endif - } - - static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target, -@@ -48246,7 +48349,7 @@ static JSValue js_date_constructor(JSContext *ctx, JSValueConst new_target, - } - v = JS_ToPrimitive(ctx, argv[0], HINT_NONE); - if (JS_IsString(v)) { -- dv = js_Date_parse(ctx, JS_UNDEFINED, 1, (JSValueConst *)&v); -+ dv = js_Date_parse(ctx, JS_UNDEFINED, 1, &v); - JS_FreeValue(ctx, v); - if (JS_IsException(dv)) - return JS_EXCEPTION; -@@ -48814,7 +48917,7 @@ void JS_AddIntrinsicDate(JSContext *ctx) - - void JS_AddIntrinsicEval(JSContext *ctx) - { -- ctx->eval_internal = __JS_EvalInternal; -+ ctx->eval_internal = JS_EvalInternalImpl; - } - - #ifdef CONFIG_BIGNUM -@@ -50934,7 +51037,7 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx) - JS_PROP_HAS_GET | JS_PROP_HAS_SET | - JS_PROP_HAS_CONFIGURABLE | JS_PROP_CONFIGURABLE); - JS_FreeValue(ctx, obj1); -- JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, (JSValueConst *)&ctx->throw_type_error, 1)); -+ JS_FreeValue(ctx, js_object_seal(ctx, JS_UNDEFINED, 1, &ctx->throw_type_error, 1)); - - ctx->global_obj = JS_NewObject(ctx); - ctx->global_var_obj = JS_NewObjectProto(ctx, JS_NULL); -@@ -52090,7 +52193,7 @@ exception: - - #define special_indexOf 0 - #define special_lastIndexOf 1 --#define special_includes -1 -+#define special_includes (-1) - - static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, - int argc, JSValueConst *argv, int special) -@@ -52157,8 +52260,7 @@ static JSValue js_typed_array_indexOf(JSContext *ctx, JSValueConst this_val, - is_int = 1; - v64 = JS_VALUE_GET_INT(argv[0]); - d = v64; -- } else -- if (tag == JS_TAG_FLOAT64) { -+ } else if (tag == JS_TAG_FLOAT64) { - d = JS_VALUE_GET_FLOAT64(argv[0]); - v64 = d; - is_int = (v64 == d); -@@ -52659,11 +52761,11 @@ static JSValue js_TA_get_uint64(JSContext *ctx, const void *a) { - #endif - - static JSValue js_TA_get_float32(JSContext *ctx, const void *a) { -- return __JS_NewFloat64(ctx, *(const float *)a); -+ return JS_NewFloat64Impl(ctx, *(const float *)a); - } - - static JSValue js_TA_get_float64(JSContext *ctx, const void *a) { -- return __JS_NewFloat64(ctx, *(const double *)a); -+ return JS_NewFloat64Impl(ctx, *(const double *)a); - } - - struct TA_sort_context { -@@ -52717,8 +52819,8 @@ static int js_TA_cmp_generic(const void *a, const void *b, void *opaque) { - psc->exception = 1; - } - done: -- JS_FreeValue(ctx, (JSValue)argv[0]); -- JS_FreeValue(ctx, (JSValue)argv[1]); -+ JS_FreeValue(ctx, argv[0]); -+ JS_FreeValue(ctx, argv[1]); - } - return cmp; - } -@@ -53285,7 +53387,7 @@ static JSValue js_dataview_getValue(JSContext *ctx, - case JS_CLASS_INT8_ARRAY: - return JS_NewInt32(ctx, *(int8_t *)ptr); - case JS_CLASS_UINT8_ARRAY: -- return JS_NewInt32(ctx, *(uint8_t *)ptr); -+ return JS_NewInt32(ctx, *ptr); - case JS_CLASS_INT16_ARRAY: - v = get_u16(ptr); - if (is_swap) -@@ -53336,7 +53438,7 @@ static JSValue js_dataview_getValue(JSContext *ctx, - if (is_swap) - v = bswap32(v); - u.i = v; -- return __JS_NewFloat64(ctx, u.f); -+ return JS_NewFloat64Impl(ctx, u.f); - } - case JS_CLASS_FLOAT64_ARRAY: - { -@@ -53347,7 +53449,7 @@ static JSValue js_dataview_getValue(JSContext *ctx, - u.i = get_u64(ptr); - if (is_swap) - u.i = bswap64(u.i); -- return __JS_NewFloat64(ctx, u.f); -+ return JS_NewFloat64Impl(ctx, u.f); - } - default: - abort(); -@@ -54059,3 +54161,155 @@ void JS_AddIntrinsicTypedArrays(JSContext *ctx) - JS_AddIntrinsicAtomics(ctx); - #endif - } -+ -+#ifndef NDEBUG -+static void *watchedRefCount = NULL; -+ -+void notifyRefCountIncrease(JSRefCountHeader *p) -+{ -+ if (p == watchedRefCount) -+ fprintf(stderr, "increasing ref count %d for %p\n", p->ref_count, watchedRefCount); -+} -+ -+void notifyRefCountDecrease(JSRefCountHeader *p) -+{ -+ if (p == watchedRefCount) -+ fprintf(stderr, "decreasing ref count %d for %p\n", p->ref_count, watchedRefCount); -+} -+ -+void watchRefCount(void *p) -+{ -+ watchedRefCount = p; -+} -+#endif -+ -+void setScopeLookup(JSContext *ctx, ScopeLookup *scopeLookup) -+{ -+ ctx->scopeLookup = scopeLookup; -+} -+ -+void setFoundUndefinedHandler(JSContext *ctx, FoundUndefinedHandler *handler) -+{ -+ ctx->handleUndefined = handler; -+} -+ -+void setFunctionEnteredHandler(JSContext *ctx, FunctionEnteredHandler *handler) -+{ -+ ctx->handleFunctionEntered = handler; -+} -+ -+void setFunctionExitedHandler(JSContext *ctx, FunctionExitedHandler *handler) -+{ -+ ctx->handleFunctionExited = handler; -+} -+ -+int isSimpleValue(JSValue v) -+{ -+ JSObject *p; -+ if (JS_VALUE_GET_TAG(v) != JS_TAG_OBJECT) -+ return 1; -+ p = JS_VALUE_GET_OBJ(v); -+ return p->class_id >= JS_CLASS_OBJECT && p->class_id <= JS_CLASS_BOOLEAN; -+} -+ -+JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func, -+ const char *name, int length, -+ JSCFunctionEnum cproto, int magic) -+{ -+ return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto, -+ magic); -+} -+ -+JSValue mkVal(int32_t tag, int32_t val) -+{ -+ return (JSValue){ (JSValueUnion){ .int32 = val }, tag }; -+} -+ -+JSValue mkPtr(int32_t tag, void *p) -+{ -+ return (JSValue){ (JSValueUnion){ .ptr = p }, tag }; -+} -+ -+void JS_FreeValue(JSContext *ctx, JSValue v) { -+ if (JS_VALUE_HAS_REF_COUNT(v)) { -+ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); -+#ifndef NDEBUG -+ notifyRefCountDecrease(p); -+#endif -+ if (--p->ref_count <= 0) { -+ JS_FreeValueImpl(ctx, v); -+ } -+ } -+} -+void JS_FreeValueRT(JSRuntime *rt, JSValue v) { -+ if (JS_VALUE_HAS_REF_COUNT(v)) { -+ JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); -+#ifndef NDEBUG -+ notifyRefCountDecrease(p); -+#endif -+ if (--p->ref_count <= 0) { -+ JS_FreeValueRTImpl(rt, v); -+ } -+ } -+} -+JSValue JS_NewBool(JSContext *ctx, JS_BOOL val) { -+ (void)ctx; -+ return JS_MKVAL(JS_TAG_BOOL, (val != 0)); -+} -+JSValue JS_NewInt32(JSContext *ctx, int32_t val) { -+ (void)ctx; -+ return JS_MKVAL(JS_TAG_INT, val); -+} -+JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val) { -+ (void)ctx; -+ return JS_MKVAL(JS_TAG_CATCH_OFFSET, val); -+} -+JSValue JS_NewInt64(JSContext *ctx, int64_t val) { -+ JSValue v; -+ if (val == (int32_t)val) { -+ v = JS_NewInt32(ctx, (int32_t)val); -+ } else { -+ v = JS_NewFloat64Impl(ctx, (double)val); -+ } -+ return v; -+} -+JSValue JS_NewUint32(JSContext *ctx, uint32_t val) { -+ JSValue v; -+ if (val <= 0x7fffffff) { -+ v = JS_NewInt32(ctx, val); -+ } else { -+ v = JS_NewFloat64Impl(ctx, val); -+ } -+ return v; -+} -+JSValue JS_NewFloat64(JSContext *ctx, double d) { -+ JSValue v; -+ int32_t val; -+ union { -+ double d; -+ uint64_t u; -+ } u, t; -+ u.d = d; -+ val = (int32_t)d; -+ t.d = val; -+ /* -0 cannot be represented as integer, so we compare the bit -+ representation */ -+ if (u.u == t.u) { -+ v = JS_MKVAL(JS_TAG_INT, val); -+ } else { -+ v = JS_NewFloat64Impl(ctx, d); -+ } -+ return v; -+} -+JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name, -+ int length) { -+ return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0); -+} -+ -+JS_BOOL JS_IsRegExp(JSContext *ctx, JSValue val) -+{ -+ JSObject *p; -+ if (JS_VALUE_GET_TAG(val) != JS_TAG_OBJECT) -+ return FALSE; -+ return JS_VALUE_GET_OBJ(val)->class_id == JS_CLASS_REGEXP; -+} -diff --git a/quickjs.h b/quickjs.h -index d4a5cd3..a5adf0c 100644 ---- a/quickjs.h -+++ b/quickjs.h -@@ -215,15 +215,19 @@ typedef struct JSValue { - #define JS_VALUE_GET_FLOAT64(v) ((v).u.float64) - #define JS_VALUE_GET_PTR(v) ((v).u.ptr) - --#define JS_MKVAL(tag, val) (JSValue){ (JSValueUnion){ .int32 = val }, tag } --#define JS_MKPTR(tag, p) (JSValue){ (JSValueUnion){ .ptr = p }, tag } -+JSValue mkVal(int32_t tag, int32_t val); -+JSValue mkPtr(int32_t tag, void *p); -+ -+#define JS_MKVAL(tag, val) mkVal(tag, val) -+#define JS_MKPTR(tag, p) mkPtr(tag, p) - - #define JS_TAG_IS_FLOAT64(tag) ((unsigned)(tag) == JS_TAG_FLOAT64) - - #define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 } - --static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) -+static inline JSValue JS_NewFloat64Impl(JSContext *ctx, double d) - { -+ (void) ctx; - JSValue v; - v.tag = JS_TAG_FLOAT64; - v.u.float64 = d; -@@ -502,66 +506,14 @@ int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id); - - /* value handling */ - --static js_force_inline JSValue JS_NewBool(JSContext *ctx, JS_BOOL val) --{ -- return JS_MKVAL(JS_TAG_BOOL, (val != 0)); --} -- --static js_force_inline JSValue JS_NewInt32(JSContext *ctx, int32_t val) --{ -- return JS_MKVAL(JS_TAG_INT, val); --} -- --static js_force_inline JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val) --{ -- return JS_MKVAL(JS_TAG_CATCH_OFFSET, val); --} -- --static js_force_inline JSValue JS_NewInt64(JSContext *ctx, int64_t val) --{ -- JSValue v; -- if (val == (int32_t)val) { -- v = JS_NewInt32(ctx, val); -- } else { -- v = __JS_NewFloat64(ctx, val); -- } -- return v; --} -- --static js_force_inline JSValue JS_NewUint32(JSContext *ctx, uint32_t val) --{ -- JSValue v; -- if (val <= 0x7fffffff) { -- v = JS_NewInt32(ctx, val); -- } else { -- v = __JS_NewFloat64(ctx, val); -- } -- return v; --} -- -+JSValue JS_NewBool(JSContext *ctx, JS_BOOL val); -+JSValue JS_NewInt32(JSContext *ctx, int32_t val); -+JSValue JS_NewCatchOffset(JSContext *ctx, int32_t val); -+JSValue JS_NewInt64(JSContext *ctx, int64_t val); -+JSValue JS_NewUint32(JSContext *ctx, uint32_t val); - JSValue JS_NewBigInt64(JSContext *ctx, int64_t v); - JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v); -- --static js_force_inline JSValue JS_NewFloat64(JSContext *ctx, double d) --{ -- JSValue v; -- int32_t val; -- union { -- double d; -- uint64_t u; -- } u, t; -- u.d = d; -- val = (int32_t)d; -- t.d = val; -- /* -0 cannot be represented as integer, so we compare the bit -- representation */ -- if (u.u == t.u) { -- v = JS_MKVAL(JS_TAG_INT, val); -- } else { -- v = __JS_NewFloat64(ctx, d); -- } -- return v; --} -+JSValue JS_NewFloat64(JSContext *ctx, double d); - - static inline JS_BOOL JS_IsNumber(JSValueConst v) - { -@@ -571,6 +523,7 @@ static inline JS_BOOL JS_IsNumber(JSValueConst v) - - static inline JS_BOOL JS_IsBigInt(JSContext *ctx, JSValueConst v) - { -+ (void) ctx; - int tag = JS_VALUE_GET_TAG(v); - return tag == JS_TAG_BIG_INT; - } -@@ -639,43 +592,38 @@ JSValue __js_printf_like(2, 3) JS_ThrowRangeError(JSContext *ctx, const char *fm - JSValue __js_printf_like(2, 3) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); - JSValue JS_ThrowOutOfMemory(JSContext *ctx); - --void __JS_FreeValue(JSContext *ctx, JSValue v); --static inline void JS_FreeValue(JSContext *ctx, JSValue v) --{ -- if (JS_VALUE_HAS_REF_COUNT(v)) { -- JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); -- if (--p->ref_count <= 0) { -- __JS_FreeValue(ctx, v); -- } -- } --} --void __JS_FreeValueRT(JSRuntime *rt, JSValue v); --static inline void JS_FreeValueRT(JSRuntime *rt, JSValue v) --{ -- if (JS_VALUE_HAS_REF_COUNT(v)) { -- JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); -- if (--p->ref_count <= 0) { -- __JS_FreeValueRT(rt, v); -- } -- } --} -+#ifndef NDEBUG -+void notifyRefCountIncrease(JSRefCountHeader *p); -+void notifyRefCountDecrease(JSRefCountHeader *p); -+#endif -+ -+void JS_FreeValue(JSContext *ctx, JSValue v); -+void JS_FreeValueRT(JSRuntime *rt, JSValue v); - - static inline JSValue JS_DupValue(JSContext *ctx, JSValueConst v) - { -+ (void) ctx; - if (JS_VALUE_HAS_REF_COUNT(v)) { - JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); -+#ifndef NDEBUG -+ notifyRefCountIncrease(p); -+#endif - p->ref_count++; - } -- return (JSValue)v; -+ return v; - } - - static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v) - { -+ (void) rt; - if (JS_VALUE_HAS_REF_COUNT(v)) { - JSRefCountHeader *p = (JSRefCountHeader *)JS_VALUE_GET_PTR(v); -+#ifndef NDEBUG -+ notifyRefCountIncrease(p); -+#endif - p->ref_count++; - } -- return (JSValue)v; -+ return v; - } - - int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */ -@@ -714,6 +662,7 @@ JSValue JS_NewObjectProto(JSContext *ctx, JSValueConst proto); - JSValue JS_NewObject(JSContext *ctx); - - JS_BOOL JS_IsFunction(JSContext* ctx, JSValueConst val); -+JS_BOOL JS_IsRegExp(JSContext* ctx, JSValueConst val); - JS_BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val); - JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val); - -@@ -783,7 +732,7 @@ JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len, - /* same as JS_Eval() but with an explicit 'this_obj' parameter */ - JSValue JS_EvalThis(JSContext *ctx, JSValueConst this_obj, - const char *input, size_t input_len, -- const char *filename, int eval_flags); -+ const char *filename, int line, int eval_flags); - JSValue JS_GetGlobalObject(JSContext *ctx); - int JS_IsInstanceOf(JSContext *ctx, JSValueConst val, JSValueConst obj); - int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj, -@@ -945,18 +894,11 @@ JSValue JS_NewCFunctionData(JSContext *ctx, JSCFunctionData *func, - int length, int magic, int data_len, - JSValueConst *data); - --static inline JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name, -- int length) --{ -- return JS_NewCFunction2(ctx, func, name, length, JS_CFUNC_generic, 0); --} -+JSValue JS_NewCFunction(JSContext *ctx, JSCFunction *func, const char *name, -+ int length); - --static inline JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func, -- const char *name, -- int length, JSCFunctionEnum cproto, int magic) --{ -- return JS_NewCFunction2(ctx, (JSCFunction *)func, name, length, cproto, magic); --} -+JSValue JS_NewCFunctionMagic(JSContext *ctx, JSCFunctionMagic *func, -+ const char *name, int length, JSCFunctionEnum cproto, int magic); - void JS_SetConstructor(JSContext *ctx, JSValueConst func_obj, - JSValueConst proto); - -@@ -1039,6 +981,35 @@ int JS_SetModuleExport(JSContext *ctx, JSModuleDef *m, const char *export_name, - int JS_SetModuleExportList(JSContext *ctx, JSModuleDef *m, - const JSCFunctionListEntry *tab, int len); - -+ -+/* Qbs extensions */ -+struct LookupResult -+{ -+ JSValue value; -+ JSValue scope; -+ int useResult; -+}; -+typedef struct LookupResult ScopeLookup(JSContext *ctx, JSAtom prop); -+void setScopeLookup(JSContext *ctx, ScopeLookup *scopeLookup); -+ -+// Alternative: Request with throw in script engine -+typedef void FoundUndefinedHandler(JSContext *ctx); -+void setFoundUndefinedHandler(JSContext *ctx, FoundUndefinedHandler *handler); -+ -+typedef void FunctionEnteredHandler(JSContext *ctx, JSValue this_val); -+typedef void FunctionExitedHandler(JSContext *ctx); -+void setFunctionEnteredHandler(JSContext *ctx, FunctionEnteredHandler *handler); -+void setFunctionExitedHandler(JSContext *ctx, FunctionExitedHandler *handler); -+int isSimpleValue(JSValue v); -+ -+#ifndef NDEBUG -+void watchRefCount(void *p); -+#endif -+ -+void build_backtrace(JSContext *ctx, JSValueConst error_obj, -+ const char *filename, int line_num, -+ int backtrace_flags); -+ - #undef js_unlikely - #undef js_force_inline - diff --git a/src/shared/quickjs/quickjs.h b/src/shared/quickjs/quickjs.h index aa4a74c9c..3a22f5b7b 100644 --- a/src/shared/quickjs/quickjs.h +++ b/src/shared/quickjs/quickjs.h @@ -156,7 +156,7 @@ static inline double JS_VALUE_GET_FLOAT64(JSValue v) #define JS_NAN (0x7ff8000000000000 - ((uint64_t)JS_FLOAT64_TAG_ADDEND << 32)) -static inline JSValue JS_NewFloat64Impl(JSContext *ctx, double d) +static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) { (void) ctx; union { @@ -226,7 +226,7 @@ JSValue mkPtr(int32_t tag, void *p); #define JS_NAN (JSValue){ .u.float64 = JS_FLOAT64_NAN, JS_TAG_FLOAT64 } -static inline JSValue JS_NewFloat64Impl(JSContext *ctx, double d) +static inline JSValue __JS_NewFloat64(JSContext *ctx, double d) { (void) ctx; JSValue v; @@ -312,6 +312,9 @@ static inline JS_BOOL JS_VALUE_IS_NAN(JSValue v) #define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5) /* don't include the stack frames before this eval in the Error() backtraces */ #define JS_EVAL_FLAG_BACKTRACE_BARRIER (1 << 6) +/* allow top-level await in normal script. JS_Eval() returns a + promise. Only allowed with JS_EVAL_TYPE_GLOBAL */ +#define JS_EVAL_FLAG_ASYNC (1 << 7) typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv); typedef JSValue JSCFunctionMagic(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic); @@ -501,7 +504,10 @@ typedef struct JSClassDef { JSClassExoticMethods *exotic; } JSClassDef; +#define JS_INVALID_CLASS_ID 0 JSClassID JS_NewClassID(JSClassID *pclass_id); +/* Returns the class ID if `v` is an object, otherwise returns JS_INVALID_CLASS_ID. */ +JSClassID JS_GetClassID(JSValue v); int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def); int JS_IsRegisteredClass(JSRuntime *rt, JSClassID class_id); @@ -514,6 +520,7 @@ JSValue JS_NewInt64(JSContext *ctx, int64_t val); JSValue JS_NewUint32(JSContext *ctx, uint32_t val); JSValue JS_NewBigInt64(JSContext *ctx, int64_t v); JSValue JS_NewBigUint64(JSContext *ctx, uint64_t v); + JSValue JS_NewFloat64(JSContext *ctx, double d); static inline JS_BOOL JS_IsNumber(JSValueConst v) @@ -583,7 +590,9 @@ static inline JS_BOOL JS_IsObject(JSValueConst v) JSValue JS_Throw(JSContext *ctx, JSValue obj); JSValue JS_GetException(JSContext *ctx); +JS_BOOL JS_HasException(JSContext *ctx); JS_BOOL JS_IsError(JSContext *ctx, JSValueConst val); +void JS_SetUncatchableError(JSContext *ctx, JSValueConst val, JS_BOOL flag); void JS_ResetUncatchableError(JSContext *ctx); JSValue JS_NewError(JSContext *ctx); JSValue __js_printf_like(2, 3) JS_ThrowSyntaxError(JSContext *ctx, const char *fmt, ...); @@ -627,6 +636,10 @@ static inline JSValue JS_DupValueRT(JSRuntime *rt, JSValueConst v) return v; } +JS_BOOL JS_StrictEq(JSContext *ctx, JSValueConst op1, JSValueConst op2); +JS_BOOL JS_SameValue(JSContext *ctx, JSValueConst op1, JSValueConst op2); +JS_BOOL JS_SameValueZero(JSContext *ctx, JSValueConst op1, JSValueConst op2); + int JS_ToBool(JSContext *ctx, JSValueConst val); /* return -1 for JS_EXCEPTION */ int JS_ToInt32(JSContext *ctx, int32_t *pres, JSValueConst val); static inline int JS_ToUint32(JSContext *ctx, uint32_t *pres, JSValueConst val) @@ -668,12 +681,12 @@ JS_BOOL JS_IsConstructor(JSContext* ctx, JSValueConst val); JS_BOOL JS_SetConstructorBit(JSContext *ctx, JSValueConst func_obj, JS_BOOL val); JS_BOOL JS_IsArrayBuffer(JSValueConst v); -JSValue JS_NewDate(JSContext *ctx, const char *s); -JS_BOOL JS_IsDate(JSValueConst v); - JSValue JS_NewArray(JSContext *ctx); int JS_IsArray(JSContext *ctx, JSValueConst val); +JSValue JS_NewDate(JSContext *ctx, double epoch_ms); +JS_BOOL JS_IsDate(JSValueConst v); + JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj, JSAtom prop, JSValueConst receiver, JS_BOOL throw_ref_error); @@ -687,13 +700,13 @@ JSValue JS_GetPropertyStr(JSContext *ctx, JSValueConst this_obj, JSValue JS_GetPropertyUint32(JSContext *ctx, JSValueConst this_obj, uint32_t idx); -int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj, - JSAtom prop, JSValue val, +int JS_SetPropertyInternal(JSContext *ctx, JSValueConst obj, + JSAtom prop, JSValue val, JSValueConst this_obj, int flags); static inline int JS_SetProperty(JSContext *ctx, JSValueConst this_obj, JSAtom prop, JSValue val) { - return JS_SetPropertyInternal(ctx, this_obj, prop, val, JS_PROP_THROW); + return JS_SetPropertyInternal(ctx, this_obj, prop, val, this_obj, JS_PROP_THROW); } int JS_SetPropertyUint32(JSContext *ctx, JSValueConst this_obj, uint32_t idx, JSValue val); @@ -774,6 +787,23 @@ JSValue JS_NewArrayBuffer(JSContext *ctx, uint8_t *buf, size_t len, JSValue JS_NewArrayBufferCopy(JSContext *ctx, const uint8_t *buf, size_t len); void JS_DetachArrayBuffer(JSContext *ctx, JSValueConst obj); uint8_t *JS_GetArrayBuffer(JSContext *ctx, size_t *psize, JSValueConst obj); + +typedef enum JSTypedArrayEnum { + JS_TYPED_ARRAY_UINT8C = 0, + JS_TYPED_ARRAY_INT8, + JS_TYPED_ARRAY_UINT8, + JS_TYPED_ARRAY_INT16, + JS_TYPED_ARRAY_UINT16, + JS_TYPED_ARRAY_INT32, + JS_TYPED_ARRAY_UINT32, + JS_TYPED_ARRAY_BIG_INT64, + JS_TYPED_ARRAY_BIG_UINT64, + JS_TYPED_ARRAY_FLOAT32, + JS_TYPED_ARRAY_FLOAT64, +} JSTypedArrayEnum; + +JSValue JS_NewTypedArray(JSContext *ctx, int argc, JSValueConst *argv, + JSTypedArrayEnum array_type); JSValue JS_GetTypedArrayBuffer(JSContext *ctx, JSValueConst obj, size_t *pbyte_offset, size_t *pbyte_length, @@ -787,7 +817,15 @@ typedef struct { void JS_SetSharedArrayBufferFunctions(JSRuntime *rt, const JSSharedArrayBufferFunctions *sf); +typedef enum JSPromiseStateEnum { + JS_PROMISE_PENDING, + JS_PROMISE_FULFILLED, + JS_PROMISE_REJECTED, +} JSPromiseStateEnum; + JSValue JS_NewPromiseCapability(JSContext *ctx, JSValue *resolving_funcs); +JSPromiseStateEnum JS_PromiseState(JSContext *ctx, JSValue promise); +JSValue JS_PromiseResult(JSContext *ctx, JSValue promise); /* is_handled = TRUE means that the rejection is handled */ typedef void JSHostPromiseRejectionTracker(JSContext *ctx, JSValueConst promise, @@ -821,6 +859,7 @@ void JS_SetModuleLoaderFunc(JSRuntime *rt, /* return the import.meta object of a module */ JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m); JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m); +JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m); /* JS Job support */ @@ -858,8 +897,8 @@ int JS_ResolveModule(JSContext *ctx, JSValueConst obj); /* only exported for os.Worker() */ JSAtom JS_GetScriptOrModuleName(JSContext *ctx, int n_stack_levels); /* only exported for os.Worker() */ -JSModuleDef *JS_RunModule(JSContext *ctx, const char *basename, - const char *filename); +JSValue JS_LoadModule(JSContext *ctx, const char *basename, + const char *filename); /* C function definition */ typedef enum JSCFunctionEnum { /* XXX: should rename for namespace isolation */ diff --git a/src/shared/quickjs/quickjs.qbs b/src/shared/quickjs/quickjs.qbs index 4c8112ac9..bdce37979 100644 --- a/src/shared/quickjs/quickjs.qbs +++ b/src/shared/quickjs/quickjs.qbs @@ -12,6 +12,8 @@ StaticLibrary { files: [ "cutils.c", "cutils.h", + "libbf.c", + "libbf.h", "libregexp-opcode.h", "libregexp.c", "libregexp.h", @@ -22,12 +24,11 @@ StaticLibrary { "quickjs-atom.h", "quickjs-opcode.h", "quickjs.c", - "quickjs.diff", "quickjs.h", ] Export { Depends { name: "cpp" } - cpp.includePaths: [exportingProduct.sourceDirectory] + cpp.systemIncludePaths: [exportingProduct.sourceDirectory] } } diff --git a/src/shared/span/.clang-format b/src/shared/span/.clang-format new file mode 100644 index 000000000..47a38a93f --- /dev/null +++ b/src/shared/span/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: Never diff --git a/src/shared/span/CMakeLists.txt b/src/shared/span/CMakeLists.txt new file mode 100644 index 000000000..5fbbc5e46 --- /dev/null +++ b/src/shared/span/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(span INTERFACE) +target_include_directories( + span + INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>) diff --git a/src/shared/variant/LICENSE.md b/src/shared/span/LICENSE_1_0.txt index 36b7cd93c..36b7cd93c 100644 --- a/src/shared/variant/LICENSE.md +++ b/src/shared/span/LICENSE_1_0.txt diff --git a/src/shared/span/README.md b/src/shared/span/README.md new file mode 100644 index 000000000..dcc95ab6c --- /dev/null +++ b/src/shared/span/README.md @@ -0,0 +1,557 @@ +<a id="top"></a> +# span lite: A single-file header-only version of a C++20-like span for C++98, C++11 and later + +[![Language](https://img.shields.io/badge/C%2B%2B-98/11/14/17/20-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) [![License](https://img.shields.io/badge/license-BSL-blue.svg)](https://opensource.org/licenses/BSL-1.0) [![Build Status](https://github.com/martinmoene/span-lite/actions/workflows/ci.yml/badge.svg)](https://github.com/martinmoene/span-lite/actions/workflows/ci.yml) [![Build status](https://ci.appveyor.com/api/projects/status/1ha3wnxtam547m8p?svg=true)](https://ci.appveyor.com/project/martinmoene/span-lite) [![Version](https://badge.fury.io/gh/martinmoene%2Fspan-lite.svg)](https://github.com/martinmoene/span-lite/releases) [![download](https://img.shields.io/badge/latest-download-blue.svg)](https://github.com/martinmoene/span-lite/blob/master/include/nonstd/span.hpp) [![Conan](https://img.shields.io/badge/on-conan-blue.svg)](https://conan.io/center/span-lite) [![Try it on wandbox](https://img.shields.io/badge/on-wandbox-blue.svg)](https://wandbox.org/permlink/venR3Ko2Q4tlvcVk) [![Try it on godbolt online](https://img.shields.io/badge/on-godbolt-blue.svg)](https://godbolt.org/z/htwpnb) + +**Contents** + +- [Example usage](#example-usage) +- [In a nutshell](#in-a-nutshell) +- [License](#license) +- [Dependencies](#dependencies) +- [Installation and use](#installation-and-use) +- [Synopsis](#synopsis) +- [Reported to work with](#reported-to-work-with) +- [Building the tests](#building-the-tests) +- [Other implementations of span](#other-implementations-of-span) +- [Notes and references](#notes-and-references) +- [Appendix](#appendix) + +## Example usage + +```cpp +#include "nonstd/span.hpp" +#include <array> +#include <vector> +#include <iostream> + +std::ptrdiff_t size( nonstd::span<const int> spn ) +{ + return spn.size(); +} + +int main() +{ + int arr[] = { 1, }; + + std::cout << + "C-array:" << size( arr ) << + " array:" << size( std::array <int, 2>{ 1, 2, } ) << + " vector:" << size( std::vector<int >{ 1, 2, 3, } ); +} +``` + +### Compile and run + +```bash +prompt> g++ -std=c++11 -Wall -I../include -o 01-basic.exe 01-basic.cpp && 01-basic.exe +C-array:1 array:2 vector:3 +``` + +## In a nutshell + +**span lite** is a single-file header-only library to provide a bounds-safe view for sequences of objects. The library provides a [C++20-like span](http://en.cppreference.com/w/cpp/container/span) for use with C++98 and later. If available, `std::span` is used, unless [configured otherwise](#configuration). *span-lite* can detect the presence of [*byte-lite*](https://github.com/martinmoene/byte-lite) and if present, it provides `as_bytes()` and `as_writable_bytes()` also for C++14 and earlier. + +**Features and properties of span lite** are ease of installation (single header), freedom of dependencies other than the standard library. To compensate for the class template argument deduction that is missing from pre-C++17 compilers, `nonstd::span` can provide `make_span` functions. See [configuration](#configuration). + +## License + +*span lite* is distributed under the [Boost Software License](https://github.com/martinmoene/span-lite/blob/master/LICENSE.txt). + +## Dependencies + +*span lite* has no other dependencies than the [C++ standard library](http://en.cppreference.com/w/cpp/header). + +## Installation and use + +*span lite* is a single-file header-only library. Put `span.hpp` in the [include](include) folder directly into the project source tree or somewhere reachable from your project. + +## Synopsis + +**Contents** +[Documentation of `std::span`](#documentation-of-stdspan) +[Later additions](#later-additions) +[Non-standard extensions](#non-standard-extensions) +[Configuration](#configuration) + +## Documentation of `std::span` + +Depending on the compiler and C++-standard used, `nonstd::span` behaves less or more like `std::span`. To get an idea of the capabilities of `nonstd::span` with your configuration, look at the output of the [tests](test/span.t.cpp), issuing `span-main.t --pass @`. For `std::span`, see its [documentation at cppreference](http://en.cppreference.com/w/cpp/container/span). + +## Later additions + +### `back()` and `front()` + +*span lite* can provide `back()` and `front()` member functions for element access. See the table below and section [configuration](#configuration). + +## Non-standard extensions + +### Construct from std::initializer_list (p2447) + +*span lite* can provide construction from a std::initializer_list<> as a constant set of values as proposed in [p2447](https://wg21.link/p2447). See the table below and section [configuration](#configuration). + +### Construct from container + +To construct a span from a container with compilers that cannot constrain such a single-parameter constructor to containers, *span lite* provides a constructor that takes an additional parameter of type `with_container_t`. Use `with_container` as value for this parameter. See the table below and section [configuration](#configuration). + +### Construct from `std::array` with const data + +*span lite* can provide construction of a span from a `std::array` with const data. See the table below and section [configuration](#configuration). + +### `operator()` + +*span lite* can provide member function call `operator()` for element access. It is equivalent to `operator[]` and has been marked `[[deprecated]]`. Its main purpose is to provide a migration path. + +### `at()` + +*span lite* can provide member function `at()` for element access. Unless exceptions have been disabled, `at()` throws std::out_of_range if the index falls outside the span. With exceptions disabled, `at(index_t)` delegates bounds checking to `operator[](index_t)`. See the table below and sections [configuration](#configuration) and [disable exceptions](#disable-exceptions). + +### `swap()` + +*span lite* can provide a `swap()`member function. See the table below and section [configuration](#configuration). + +### `operator==()` and other comparison functions + +*span lite* can provide functions to compare the content of two spans. However, C++20's span will not provide comparison and _span lite_ will omit comparison at default in the near future. See the table below and section [configuration](#configuration). See also [Revisiting Regular Types](#regtyp). + +### `same()` + +*span lite* can provide function `same()` to determine if two spans refer as identical spans to the same data via the same type. If `same()` is enabled, `operator==()` incorporates it in its comparison. See the table below and section [configuration](#configuration). + +### `first()`, `last()` and `subspan()` + +*span lite* can provide functions `first()`, `last()` and `subspan()` to avoid having to use the *dot template* syntax when the span is a dependent type. See the table below and section [configuration](#configuration). + +### `make_span()` + +*span lite* can provide `make_span()` creator functions to compensate for the class template argument deduction that is missing from pre-C++17 compilers. See the table below and section [configuration](#configuration). + +### `byte_span()` + +*span lite* can provide `byte_span()` creator functions to represent an object as a span of bytes. This requires the C++17 type `std::byte` to be available. See the table below and section [configuration](#configuration). + +| Kind | std | Function or method | +|--------------------|------|--------------------| +| **Macro** | | macro **`span_FEATURE_WITH_INITIALIZER_LIST_P2447`** | +| **Constructor**<br> | | constexpr explicit **span**( std::initializer_list<value_type> il ) noexcept<br>explicit for non-dynamic extent | +| | | | +| **Macro**<br> | | macro **`span_FEATURE_WITH_CONTAINER`**<br>macro **`span_FEATURE_WITH_CONTAINER_TO_STD`** | +| **Types** | | **with_container_t** type to disambiguate below constructors | +| **Objects** | | **with_container** value to disambiguate below constructors | +| **Constructors** | | macro **`span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE`**| +| | | template<class Container><br>constexpr **span**(with_container_t, Container & cont) | +| | | template<class Container><br>constexpr **span**(with_container_t, Container const & cont) | +| | | | +| **Methods** | | macro **`span_FEATURE_MEMBER_CALL_OPERATOR`** | +| | | constexpr reference **operator()**(index_t idx) const<br>Equivalent to **operator[]**(), marked `[[deprecated]]` | +| | | | +| **Methods** | | macro **`span_FEATURE_MEMBER_AT`** | +| | | constexpr reference **at**(index_t idx) const<br>May throw std::out_of_range exception | +| | | | +| **Methods** | | macro **`span_FEATURE_MEMBER_BACK_FRONT`** (on since v0.5.0) | +| | | constexpr reference **back()** const noexcept | +| | | constexpr reference **front()** const noexcept | +| | | | +| **Method** | | macro **`span_FEATURE_MEMBER_SWAP`** | +| | | constexpr void **swap**(span & other) noexcept | +| | | | +| **Free functions** | | macro **`span_FEATURE_COMPARISON`** | +|<br><br>== != < > <= >= | | template<class T1, index_t E1, class T2, index_t E2><br>constexpr bool<br>**operator==**( span<T1,E1> const & l, span<T2,E2> const & r) noexcept | +| | | | +| **Free function** | | macro **`span_FEATURE_SAME`** | +| | | template<class T1, index_t E1, class T2, index_t E2><br>constexpr bool<br>**same**( span<T1,E1> const & l, span<T2,E2> const & r) noexcept | +| | | | +| **Free functions**<br> <br> | | macros **`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB`**,<br>**`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN`**,<br>**`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER`** | +| | | | +| **Free functions** | | macro **`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN`** | +| | | template<extent_t Count, class T, extent_t Extent><br>constexpr span<T,Count><br>**first**(span<T,Extent> spn) | +| | | template<class T, extent_t Extent ><br>constexpr span<T><br>**first**(span<T,Extent> spn, size_t count) | +| | | template<extent_t Count, class T, extent_t Extent><br>constexpr span<T,Count><br>**last**(span<T,Extent> spn) | +| | | template<class T, extent_t Extent ><br>constexpr span<T><br>**last**(span<T,Extent> spn, size_t count) | +| | | template<size_t Offset, extent_t Count, class T, extent_t Extent><br>constexpr span<T, Count><br>**subspan**(span<T, Extent> spn) | +| | | template<class T, extent_t Extent><br>constexpr span<T><br>**subspan**( span<T, Extent> spn, size_t offset, extent_t count = dynamic_extent) | +| | | | +| **Free functions** | | macro **`span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER`** | +| | >= C++11 | template<extent_t Count, class T><br>constexpr auto<br>**first**(T & t) ->... | +| | >= C++11 | template<class T><br>constexpr auto<br>**first**(T & t, index_t count) ->... | +| | >= C++11 | template<extent_t Count, class T><br>constexpr auto<br>**last**(T & t) ->... | +| | >= C++11 | template<class T><br>constexpr auto<br>**last**(T & t, extent_t count) ->... | +| | >= C++11 | template<index_t Offset, extent_t Count = dynamic_extent, class T><br>constexpr auto<br>**subspan**(T & t) ->... | +| | >= C++11 | template<class T><br>constexpr auto<br>**subspan**(T & t, index_t offset, extent_t count = dynamic_extent) ->... | +| | | | +| **Free functions**<br> | | macro **`span_FEATURE_MAKE_SPAN`**<br>macro **`span_FEATURE_MAKE_SPAN_TO_STD`** | +| | | template<class T><br>constexpr span<T><br>**make_span**(T \* first, T \* last) noexcept | +| | | template<class T><br>constexpr span<T><br>**make_span**(T \* ptr, index_t count) noexcept | +| | | template<class T, size_t N><br>constexpr span<T,N><br>**make_span**(T (&arr)[N]) noexcept | +| | >= C++11 | template<class T, size_t N><br>constexpr span<T,N><br>**make_span**(std::array<T,N> & arr) noexcept | +| | >= C++11 | template<class T, size_t N><br>constexpr span<const T,N><br>**make_span**(std::array<T,N > const & arr) noexcept | +| | >= C++11 | template<class T><br>constexpr span<T><br>**make_span**(std::initializer_list<T> il) noexcept | +| | >= C++11 | template<class Container><br>constexpr auto<br>**make_span**(Container & cont) -><br> span<typename Container::value_type> noexcept | +| | >= C++11 | template<class Container><br>constexpr auto<br>**make_span**(Container const & cont) -><br> span<const typename Container::value_type> noexcept | +| | | template<class Container><br>span<typename Container::value_type><br>**make_span**( with_container_t, Container & cont ) | +| | | template<class Container><br>span<const typename Container::value_type><br>**make_span**( with_container_t, Container const & cont ) | +| | < C++11 | template<class T, Allocator><br>span<T><br>**make_span**(std::vector<T, Allocator> & cont) | +| | < C++11 | template<class T, Allocator><br>span<const T><br>**make_span**(std::vector<T, Allocator> const & cont) | +| | | | +| **Free functions** | | macro **`span_FEATURE_BYTE_SPAN`** | +| | >= C++11 | template<class T><br>span<T, sizeof(T)><br>**byte_span**(T & t) | +| | >= C++11 | template<class T><br>span<const T, sizeof(T)><br>**byte_span**(T const & t) | + +## Configuration + +### Tweak header + +If the compiler supports [`__has_include()`](https://en.cppreference.com/w/cpp/preprocessor/include), *span lite* supports the [tweak header](https://vector-of-bool.github.io/2020/10/04/lib-configuration.html) mechanism. Provide your *tweak header* as `nonstd/span.tweak.hpp` in a folder in the include-search-path. In the tweak header, provide definitions as documented below, like `#define span_CONFIG_NO_EXCEPTIONS 1`. + +### Standard selection macro + +\-D<b>span\_CPLUSPLUS</b>=199711L +Define this macro to override the auto-detection of the supported C++ standard, if your compiler does not set the `__cplusplus` macro correctly. + +### Select `std::span` or `nonstd::span` + +At default, *span lite* uses `std::span` if it is available and lets you use it via namespace `nonstd`. You can however override this default and explicitly request to use `std::span` or span lite's `nonstd::span` as `nonstd::span` via the following macros. + +-D<b>span\_CONFIG\_SELECT\_SPAN</b>=span_SPAN_DEFAULT +Define this to `span_SPAN_STD` to select `std::span` as `nonstd::span`. Define this to `span_SPAN_NONSTD` to select `nonstd::span` as `nonstd::span`. Default is undefined, which has the same effect as defining to `span_SPAN_DEFAULT`. + +### Select extent type + +-D<b>span_CONFIG_EXTENT_TYPE</b>=std::size_t +Define this to `std::ptrdiff_t` to use the signed type. The default is `std::size_t`, as in C++20 (since v0.7.0). + +### Select size type + +-D<b>span_CONFIG_SIZE_TYPE</b>=std::size_t +Define this to `std::ptrdiff_t` to use the signed type. The default is `std::size_t`, as in C++20 (since v0.7.0). Note `span_CONFIG_SIZE_TYPE` replaces `span_CONFIG_INDEX_TYPE` which is deprecated. + +### Disable exceptions + +-D<b>span_CONFIG_NO_EXCEPTIONS</b>=0 +Define this to 1 if you want to compile without exceptions. If not defined, the header tries and detect if exceptions have been disabled (e.g. via `-fno-exceptions`). Disabling exceptions will force contract violation to use termination, see [contract violation macros](#contract-violation-response-macros). Default is undefined. + +### Provide construction from std::initializer_list (p2447) + +-D<b>span_FEATURE_WITH_INITIALIZER_LIST_P2447</b>=0 +Define this to 1 to enable constructing a span from a std::initializer_list<> as a constant set of values. See proposal [p2447](https://wg21.link/p2447). Default is undefined. + +### Provide construction using `with_container_t` + +-D<b>span_FEATURE_WITH_CONTAINER</b>=0 +Define this to 1 to enable constructing a span using `with_container_t`. Note that `span_FEATURE_WITH_CONTAINER` takes precedence over `span_FEATURE_WITH_CONTAINER_TO_STD`. Default is undefined. + +-D<b>span_FEATURE_WITH_CONTAINER_TO_STD</b>=*n* +Define this to the highest C++ language version for which to enable constructing a span using `with_container_t`, like 98, 03, 11, 14, 17, 20. You can use 99 for inclusion with any standard, but prefer to use `span_FEATURE_WITH_CONTAINER` for this. Note that `span_FEATURE_WITH_CONTAINER` takes precedence over `span_FEATURE_WITH_CONTAINER_TO_STD`. Default is undefined. + +### Provide construction from `std::array` with const data + +-D<b>span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE</b>=0 +Define this to 1 to enable constructing a span from a std::array with const data. Default is undefined. + +### Provide `operator()` member function + +-D<b>span_FEATURE_MEMBER_CALL_OPERATOR</b>=0 +Define this to 1 to provide member function `operator()`for element access. It is equivalent to `operator[]` and has been marked `[[deprecated]]`. Its main purpose is to provide a migration path. Default is undefined. + +### Provide `at()` member function + +-D<b>span_FEATURE_MEMBER_AT</b>=0 +Define this to 1 to provide member function `at()`. Define this to 2 to include index and size in message of std::out_of_range exception. Default is undefined. + +### Provide `back()` and `front()` member functions + +-D<b>span_FEATURE_MEMBER_BACK_FRONT</b>=1 _(on since v0.5.0)_ +Define this to 0 to omit member functions `back()` and `front()`. Default is undefined. + +### Provide `swap()` member function + +-D<b>span_FEATURE_MEMBER_SWAP</b>=0 +Define this to 1 to provide member function `swap()`. Default is undefined. + +### Provide `operator==()` and other comparison functions + +-D<b>span_FEATURE_COMPARISON</b>=0 +Define this to 1 to include the comparison functions to compare the content of two spans. C++20's span does not provide comparison and _span lite_ omits comparison from v0.7.0. Default is undefined. + +### Provide `same()` function + +-D<b>span_FEATURE_SAME</b>=0 +Define this to 1 to provide function `same()` to test if two spans refer as identical spans to the same data via the same type. If `same()` is enabled, `operator==()` incorporates it in its comparison. Default is undefined. + +### Provide `first()`, `last()` and `subspan()` functions + +-D<b>span_FEATURE_NON_MEMBER_FIRST_LAST_SUB</b>=0 +Define this to 1 to enable both `span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN` and `span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER`. Default is undefined. + +-D<b>span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN</b>=0 +Define this to 1 to provide functions `first()`, `last()` and `subspan()` that take a `span<>` (work with C++98). This implies `span_FEATURE_MAKE_SPAN` to provide functions `make_span()` that are required for this feature. Default is undefined. + +-D<b>span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER</b>=0 +Define this to 1 to provide functions `first()`, `last()` and `subspan()` that take a compatible container (requires C++11). This implies `span_FEATURE_MAKE_SPAN` to provide functions `make_span()` that are required for this feature. Default is undefined. + +### Provide `make_span()` functions + +-D<b>span_FEATURE_MAKE_SPAN</b>=0 +Define this to 1 to provide creator functions `nonstd::make_span()`. This feature is implied by using `span_FEATURE_NON_MEMBER_FIRST_LAST_SUB=1`. Note that `span_FEATURE_MAKE_SPAN` takes precedence over `span_FEATURE_MAKE_SPAN_TO_STD`. Default is undefined. + +-D<b>span_FEATURE_MAKE_SPAN_TO_STD</b>=*n* +Define this to the highest C++ language version for which to provide creator functions `nonstd::make_span()`, like 98, 03, 11, 14, 17, 20. You can use 99 for inclusion with any standard, but prefer to use `span_FEATURE_MAKE_SPAN` for this. Note that `span_FEATURE_MAKE_SPAN` takes precedence over `span_FEATURE_MAKE_SPAN_TO_STD`. Default is undefined. + +### Provide `byte_span()` functions + +-D<b>span_FEATURE_BYTE_SPAN</b>=0 +Define this to 1 to provide creator functions `nonstd::byte_span()`. Default is undefined. + +### Contract violation response macros + +*span-lite* provides contract violation response control as suggested in proposal [N4415](http://wg21.link/n4415). + +\-D<b>span\_CONFIG\_CONTRACT\_LEVEL\_ON</b> (*default*) +Define this macro to include both `span_EXPECTS` and `span_ENSURES` in the code. This is the default case. + +\-D<b>span\_CONFIG\_CONTRACT\_LEVEL\_OFF</b> +Define this macro to exclude both `span_EXPECTS` and `span_ENSURES` from the code. + +\-D<b>span\_CONFIG_CONTRACT\_LEVEL\_EXPECTS\_ONLY</b> +Define this macro to include `span_EXPECTS` in the code and exclude `span_ENSURES` from the code. + +\-D<b>span\_CONFIG\_CONTRACT\_LEVEL\_ENSURES\_ONLY</b> +Define this macro to exclude `span_EXPECTS` from the code and include `span_ENSURES` in the code. + +\-D<b>span\_CONFIG\_CONTRACT\_VIOLATION\_TERMINATES</b> (*default*) +Define this macro to call `std::terminate()` on a contract violation in `span_EXPECTS`, `span_ENSURES`. This is the default case. + +\-D<b>span\_CONFIG\_CONTRACT\_VIOLATION\_THROWS</b> +Define this macro to throw an exception of implementation-defined type that is derived from `std::runtime_exception` instead of calling `std::terminate()` on a contract violation in `span_EXPECTS` and `span_ENSURES`. See also [disable exceptions](#disable-exceptions). + +Reported to work with +-------------------- +The table below mentions the compiler versions *span lite* is reported to work with. + +OS | Compiler | Where | Versions | +------------:|:-----------|:--------|:---------| +**GNU/Linux**| Clang/LLVM | Travis | 3.5.0, 3.6.2, 3.7.1, 3.8.0, 3.9.1, 4.0.1 | + | GCC | Travis | 5.5.0, 6.4.0, 7.3.0 | +**OS X** | ? | Local | ? | +**Windows** | Clang/LLVM | Local | 6.0.0 | + | GCC | Local | 7.2.0 | + | Visual C++<br>(Visual Studio)| Local | 8 (2005), 10 (2010), 11 (2012),<br>12 (2013), 14 (2015), 15 (2017) | + | Visual C++<br>(Visual Studio)| AppVeyor | 10 (2010), 11 (2012),<br>12 (2013), 14 (2015), 15 (2017) | + +## Building the tests + +To build the tests you need: + +- [CMake](http://cmake.org), version 3.0 or later to be installed and in your PATH. +- A [suitable compiler](#reported-to-work-with). + +The [*lest* test framework](https://github.com/martinmoene/lest) is included in the [test folder](test). + +The following steps assume that the [*span lite* source code](https://github.com/martinmoene/span-lite) has been cloned into a directory named `./span-lite`. + +1. Create a directory for the build outputs. + + cd ./span-lite + md build && cd build + +2. Configure CMake to use the compiler of your choice (run `cmake --help` for a list). + + cmake -G "Unix Makefiles" -DSPAN_LITE_OPT_BUILD_TESTS=ON .. + +3. Optional. You can control above configuration through the following options: + + `-DSPAN_LITE_OPT_BUILD_TESTS=ON`: build the tests for span, default off + `-DSPAN_LITE_OPT_BUILD_EXAMPLES=OFF`: build the examples, default off + +4. Build the test suite. + + cmake --build . + +5. Run the test suite. + + ctest -V + +All tests should pass, indicating your platform is supported and you are ready to use *span lite*. + +## Other implementations of span + +- *gsl-lite* [span](https://github.com/martinmoene/gsl-lite/blob/73c4f16f2b35fc174fc2f09d44d5ab13e5c638c3/include/gsl/gsl-lite.hpp#L1221). +- Microsoft GSL [span](https://github.com/Microsoft/GSL/blob/master/include/gsl/span). +- Google Abseil [span](https://github.com/abseil/abseil-cpp/blob/master/absl/types/span.h). +- Marshall Clow's [libc++ span snippet](https://github.com/mclow/snippets/blob/master/span.cpp). +- Tristan Brindle's [Implementation of C++20's std::span for older compilers](https://github.com/tcbrindle/span). +- [Search _span c++_ on GitHub](https://github.com/search?l=C%2B%2B&q=span+c%2B%2B&type=Repositories&utf8=%E2%9C%93). + +## Notes and references + +*Interface and specification* + +- [span on cppreference](https://en.cppreference.com/w/cpp/container/span). +- [p0122 - C++20 Proposal](http://wg21.link/p0122). +- [span in C++20 Working Draft](http://eel.is/c++draft/views). + +*Presentations* + +- TBD + +*Proposals* + +- [p0122 - span: bounds-safe views for sequences of objects](http://wg21.link/p0122). +- [p1024 - Usability Enhancements for std::span](http://wg21.link/p1024). +- [p1419 - A SFINAE-friendly trait to determine the extent of statically sized containers](http://wg21.link/p1419). +- [p0805 - Comparing Containers](http://wg21.link/p0805). +- [p1085 - Should Span be Regular?](http://wg21.link/p0805). +- [p0091 - Template argument deduction for class templates](http://wg21.link/p0091). +- [p0856 - Restrict Access Property for mdspan and span](http://wg21.link/p0856). +- [p1428 - Subscripts and sizes should be signed](http://wg21.link/p1428). +- [p1089 - Sizes Should Only span Unsigned](http://wg21.link/p1089). +- [p1227 - Signed size() functions](http://wg21.link/p1227). +- [p1872 - span should have size_type, not index_type](http://wg21.link/p1872). +- [p2447 - std::span and the missing constructor](https://wg21.link/p2447). +- [lwg 3101 - span's Container constructors need another constraint](https://cplusplus.github.io/LWG/issue3101). +- [Reddit - 2018-06 Rapperswil ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/8prqzm/2018_rapperswil_iso_c_committee_trip_report/) +- [Reddit - 2018-11 San Diego ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/9vwvbz/2018_san_diego_iso_c_committee_trip_report_ranges/). +- [Reddit - 2019-02 Kona ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/au0c4x/201902_kona_iso_c_committee_trip_report_c20/). +- [Reddit - 2019-07 Cologne ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/cfk9de/201907_cologne_iso_c_committee_trip_report_the/) +- [Reddit - 2019-11 Belfast ISO C++ Committee Trip Report](https://www.reddit.com/r/cpp/comments/dtuov8/201911_belfast_iso_c_committee_trip_report/) +- <a id="regtyp"></a>Titus Winters. [Revisiting Regular Types](https://abseil.io/blog/20180531-regular-types). Abseil Blog. 31 May 2018. + +## Appendix + +### A.1 Compile-time information + +The version of *span lite* is available via tag `[.version]`. The following tags are available for information on the compiler and on the C++ standard library used: `[.compiler]`, `[.stdc++]`, `[.stdlanguage]` and `[.stdlibrary]`. + +### A.2 Span lite test specification + +<details> +<summary>click to expand</summary> +<p> + +```Text +span<>: Terminates construction from a nullptr and a non-zero size (C++11) +span<>: Terminates construction from two pointers in the wrong order +span<>: Terminates construction from a null pointer and a non-zero size +span<>: Terminates creation of a sub span of the first n elements for n exceeding the span +span<>: Terminates creation of a sub span of the last n elements for n exceeding the span +span<>: Terminates creation of a sub span outside the span +span<>: Terminates access outside the span +span<>: Throws on access outside the span via at(): std::out_of_range [span_FEATURE_MEMBER_AT>0][span_CONFIG_NO_EXCEPTIONS=0] +span<>: Termination throws std::logic_error-derived exception [span_CONFIG_CONTRACT_VIOLATION_THROWS=1] +span<>: Allows to default-construct +span<>: Allows to construct from a nullptr and a zero size (C++11) +span<>: Allows to construct from two pointers +span<>: Allows to construct from two iterators +span<>: Allows to construct from two iterators - empty range +span<>: Allows to construct from two iterators - move-only element +span<>: Allows to construct from an iterator and a size +span<>: Allows to construct from an iterator and a size - empty range +span<>: Allows to construct from an iterator and a size - move-only element +span<>: Allows to construct from two pointers to const +span<>: Allows to construct from a non-null pointer and a size +span<>: Allows to construct from a non-null pointer to const and a size +span<>: Allows to construct from a temporary pointer and a size +span<>: Allows to construct from a temporary pointer to const and a size +span<>: Allows to construct from any pointer and a zero size (C++98) +span<>: Allows to construct from a pointer and a size via a deduction guide (C++17) +span<>: Allows to construct from an iterator and a size via a deduction guide (C++17) +span<>: Allows to construct from two iterators via a deduction guide (C++17) +span<>: Allows to construct from a C-array +span<>: Allows to construct from a C-array via a deduction guide (C++17) +span<>: Allows to construct from a const C-array +span<>: Allows to construct from a C-array with size via decay to pointer (potentially dangerous) +span<>: Allows to construct from a const C-array with size via decay to pointer (potentially dangerous) +span<>: Allows to construct from a std::initializer_list<> (C++11) +span<>: Allows to construct from a std::initializer_list<> as a constant set of values (C++11, p2447) +span<>: Allows to construct from a std::array<> (C++11) +span<>: Allows to construct from a std::array via a deduction guide (C++17) +span<>: Allows to construct from a std::array<> with const data (C++11, span_FEATURE_CONSTR..._ELEMENT_TYPE=1) +span<>: Allows to construct from an empty std::array<> (C++11) +span<>: Allows to construct from a container (std::vector<>) +span<>: Allows to construct from a container via a deduction guide (std::vector<>, C++17) +span<>: Allows to tag-construct from a container (std::vector<>) +span<>: Allows to tag-construct from a const container (std::vector<>) +span<>: Allows to copy-construct from another span of the same type +span<>: Allows to copy-construct from another span of a compatible type +span<>: Allows to copy-construct from a temporary span of the same type (C++11) +span<>: Allows to copy-assign from another span of the same type +span<>: Allows to copy-assign from a temporary span of the same type (C++11) +span<>: Allows to create a sub span of the first n elements +span<>: Allows to create a sub span of the last n elements +span<>: Allows to create a sub span starting at a given offset +span<>: Allows to create a sub span starting at a given offset with a given length +span<>: Allows to observe an element via array indexing +span<>: Allows to observe an element via call indexing +span<>: Allows to observe an element via at() [span_FEATURE_MEMBER_AT>0] +span<>: Allows to observe an element via data() +span<>: Allows to observe the first element via front() [span_FEATURE_MEMBER_BACK_FRONT=1] +span<>: Allows to observe the last element via back() [span_FEATURE_MEMBER_BACK_FRONT=1] +span<>: Allows to change an element via array indexing +span<>: Allows to change an element via call indexing +span<>: Allows to change an element via at() [span_FEATURE_MEMBER_AT>0] +span<>: Allows to change an element via data() +span<>: Allows to change the first element via front() [span_FEATURE_MEMBER_BACK_FRONT=1] +span<>: Allows to change the last element via back() [span_FEATURE_MEMBER_BACK_FRONT=1] +span<>: Allows to swap with another span [span_FEATURE_MEMBER_SWAP=1] +span<>: Allows forward iteration +span<>: Allows const forward iteration +span<>: Allows reverse iteration +span<>: Allows const reverse iteration +span<>: Allows to identify if a span is the same as another span [span_FEATURE_SAME=1] +span<>: Allows to compare equal to another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare unequal to another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare less than another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare less than or equal to another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare greater than another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare greater than or equal to another span of the same type [span_FEATURE_COMPARISON=1] +span<>: Allows to compare to another span of the same type and different cv-ness [span_FEATURE_SAME=0] +span<>: Allows to compare empty spans as equal [span_FEATURE_COMPARISON=1] +span<>: Allows to test for empty span via empty(), empty case +span<>: Allows to test for empty span via empty(), non-empty case +span<>: Allows to obtain the number of elements via size() +span<>: Allows to obtain the number of elements via ssize() +span<>: Allows to obtain the number of bytes via size_bytes() +span<>: Allows to view the elements as read-only bytes +span<>: Allows to view and change the elements as writable bytes +make_span() [span_FEATURE_MAKE_SPAN_TO_STD=99] +make_span(): Allows building from two pointers +make_span(): Allows building from two const pointers +make_span(): Allows building from a non-null pointer and a size +make_span(): Allows building from a non-null const pointer and a size +make_span(): Allows building from a C-array +make_span(): Allows building from a const C-array +make_span(): Allows building from a std::initializer_list<> (C++11) +make_span(): Allows building from a std::initializer_list<> as a constant set of values (C++11) +make_span(): Allows building from a std::array<> (C++11) +make_span(): Allows building from a const std::array<> (C++11) +make_span(): Allows building from a container (std::vector<>) +make_span(): Allows building from a const container (std::vector<>) +make_span(): Allows building from a container (with_container_t, std::vector<>) +make_span(): Allows building from a const container (with_container_t, std::vector<>) +byte_span() [span_FEATURE_BYTE_SPAN=1] +byte_span(): Allows building a span of std::byte from a single object (C++17, byte-lite) +byte_span(): Allows building a span of const std::byte from a single const object (C++17, byte-lite) +first(), last(), subspan() [span_FEATURE_NON_MEMBER_FIRST_LAST_SUB=1] +first(): Allows to create a sub span of the first n elements (span, template parameter) +first(): Allows to create a sub span of the first n elements (span, function parameter) +first(): Allows to create a sub span of the first n elements (compatible container, template parameter) +first(): Allows to create a sub span of the first n elements (compatible container, function parameter) +last(): Allows to create a sub span of the last n elements (span, template parameter) +last(): Allows to create a sub span of the last n elements (span, function parameter) +last(): Allows to create a sub span of the last n elements (compatible container, template parameter) +last(): Allows to create a sub span of the last n elements (compatible container, function parameter) +subspan(): Allows to create a sub span starting at a given offset (span, template parameter) +subspan(): Allows to create a sub span starting at a given offset (span, function parameter) +subspan(): Allows to create a sub span starting at a given offset (compatible container, template parameter) +subspan(): Allows to create a sub span starting at a given offset (compatible container, function parameter) +size(): Allows to obtain the number of elements via size() +ssize(): Allows to obtain the number of elements via ssize() +tuple_size<>: Allows to obtain the number of elements via std::tuple_size<> (C++11) +tuple_element<>: Allows to obtain an element via std::tuple_element<> (C++11) +tuple_element<>: Allows to obtain an element via std::tuple_element_t<> (C++11) +get<I>(spn): Allows to access an element via std::get<>() +tweak header: reads tweak header if supported [tweak] +``` + +</p> +</details> diff --git a/src/shared/span/span.hpp b/src/shared/span/span.hpp new file mode 100644 index 000000000..9a7182f5b --- /dev/null +++ b/src/shared/span/span.hpp @@ -0,0 +1,1947 @@ +// +// span for C++98 and later. +// Based on http://wg21.link/p0122r7 +// For more information see https://github.com/martinmoene/span-lite +// +// Copyright 2018-2021 Martin Moene +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef NONSTD_SPAN_HPP_INCLUDED +#define NONSTD_SPAN_HPP_INCLUDED + +#define span_lite_MAJOR 0 +#define span_lite_MINOR 11 +#define span_lite_PATCH 0 + +#define span_lite_VERSION span_STRINGIFY(span_lite_MAJOR) "." span_STRINGIFY(span_lite_MINOR) "." span_STRINGIFY(span_lite_PATCH) + +#define span_STRINGIFY( x ) span_STRINGIFY_( x ) +#define span_STRINGIFY_( x ) #x + +// span configuration: + +#define span_SPAN_DEFAULT 0 +#define span_SPAN_NONSTD 1 +#define span_SPAN_STD 2 + +// tweak header support: + +#ifdef __has_include +# if __has_include(<nonstd/span.tweak.hpp>) +# include <nonstd/span.tweak.hpp> +# endif +#define span_HAVE_TWEAK_HEADER 1 +#else +#define span_HAVE_TWEAK_HEADER 0 +//# pragma message("span.hpp: Note: Tweak header not supported.") +#endif + +// span selection and configuration: + +#define span_HAVE( feature ) ( span_HAVE_##feature ) + +#ifndef span_CONFIG_SELECT_SPAN +# define span_CONFIG_SELECT_SPAN ( span_HAVE_STD_SPAN ? span_SPAN_STD : span_SPAN_NONSTD ) +#endif + +#ifndef span_CONFIG_EXTENT_TYPE +# define span_CONFIG_EXTENT_TYPE std::size_t +#endif + +#ifndef span_CONFIG_SIZE_TYPE +# define span_CONFIG_SIZE_TYPE std::size_t +#endif + +#ifdef span_CONFIG_INDEX_TYPE +# error `span_CONFIG_INDEX_TYPE` is deprecated since v0.7.0; it is replaced by `span_CONFIG_SIZE_TYPE`. +#endif + +// span configuration (features): + +#ifndef span_FEATURE_WITH_INITIALIZER_LIST_P2447 +# define span_FEATURE_WITH_INITIALIZER_LIST_P2447 0 +#endif + +#ifndef span_FEATURE_WITH_CONTAINER +#ifdef span_FEATURE_WITH_CONTAINER_TO_STD +# define span_FEATURE_WITH_CONTAINER span_IN_STD( span_FEATURE_WITH_CONTAINER_TO_STD ) +#else +# define span_FEATURE_WITH_CONTAINER 0 +# define span_FEATURE_WITH_CONTAINER_TO_STD 0 +#endif +#endif + +#ifndef span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE +# define span_FEATURE_CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE 0 +#endif + +#ifndef span_FEATURE_MEMBER_AT +# define span_FEATURE_MEMBER_AT 0 +#endif + +#ifndef span_FEATURE_MEMBER_BACK_FRONT +# define span_FEATURE_MEMBER_BACK_FRONT 1 +#endif + +#ifndef span_FEATURE_MEMBER_CALL_OPERATOR +# define span_FEATURE_MEMBER_CALL_OPERATOR 0 +#endif + +#ifndef span_FEATURE_MEMBER_SWAP +# define span_FEATURE_MEMBER_SWAP 0 +#endif + +#ifndef span_FEATURE_NON_MEMBER_FIRST_LAST_SUB +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB 0 +#elif span_FEATURE_NON_MEMBER_FIRST_LAST_SUB +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN 1 +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER 1 +#endif + +#ifndef span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_SPAN 0 +#endif + +#ifndef span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER +# define span_FEATURE_NON_MEMBER_FIRST_LAST_SUB_CONTAINER 0 +#endif + +#ifndef span_FEATURE_COMPARISON +# define span_FEATURE_COMPARISON 0 // Note: C++20 does not provide comparison +#endif + +#ifndef span_FEATURE_SAME +# define span_FEATURE_SAME 0 +#endif + +#if span_FEATURE_SAME && !span_FEATURE_COMPARISON +# error `span_FEATURE_SAME` requires `span_FEATURE_COMPARISON` +#endif + +#ifndef span_FEATURE_MAKE_SPAN +#ifdef span_FEATURE_MAKE_SPAN_TO_STD +# define span_FEATURE_MAKE_SPAN span_IN_STD( span_FEATURE_MAKE_SPAN_TO_STD ) +#else +# define span_FEATURE_MAKE_SPAN 0 +# define span_FEATURE_MAKE_SPAN_TO_STD 0 +#endif +#endif + +#ifndef span_FEATURE_BYTE_SPAN +# define span_FEATURE_BYTE_SPAN 0 +#endif + +// Control presence of exception handling (try and auto discover): + +#ifndef span_CONFIG_NO_EXCEPTIONS +# if defined(_MSC_VER) +# include <cstddef> // for _HAS_EXCEPTIONS +# endif +# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS) +# define span_CONFIG_NO_EXCEPTIONS 0 +# else +# define span_CONFIG_NO_EXCEPTIONS 1 +# undef span_CONFIG_CONTRACT_VIOLATION_THROWS +# undef span_CONFIG_CONTRACT_VIOLATION_TERMINATES +# define span_CONFIG_CONTRACT_VIOLATION_THROWS 0 +# define span_CONFIG_CONTRACT_VIOLATION_TERMINATES 1 +# endif +#endif + +// Control pre- and postcondition violation behaviour: + +#if defined( span_CONFIG_CONTRACT_LEVEL_ON ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x11 +#elif defined( span_CONFIG_CONTRACT_LEVEL_OFF ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x00 +#elif defined( span_CONFIG_CONTRACT_LEVEL_EXPECTS_ONLY ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x01 +#elif defined( span_CONFIG_CONTRACT_LEVEL_ENSURES_ONLY ) +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x10 +#else +# define span_CONFIG_CONTRACT_LEVEL_MASK 0x11 +#endif + +#if defined( span_CONFIG_CONTRACT_VIOLATION_THROWS ) +# define span_CONFIG_CONTRACT_VIOLATION_THROWS_V span_CONFIG_CONTRACT_VIOLATION_THROWS +#else +# define span_CONFIG_CONTRACT_VIOLATION_THROWS_V 0 +#endif + +#if defined( span_CONFIG_CONTRACT_VIOLATION_THROWS ) && span_CONFIG_CONTRACT_VIOLATION_THROWS && \ + defined( span_CONFIG_CONTRACT_VIOLATION_TERMINATES ) && span_CONFIG_CONTRACT_VIOLATION_TERMINATES +# error Please define none or one of span_CONFIG_CONTRACT_VIOLATION_THROWS and span_CONFIG_CONTRACT_VIOLATION_TERMINATES to 1, but not both. +#endif + +// C++ language version detection (C++23 is speculative): +// Note: VC14.0/1900 (VS2015) lacks too much from C++14. + +#ifndef span_CPLUSPLUS +# if defined(_MSVC_LANG ) && !defined(__clang__) +# define span_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) +# else +# define span_CPLUSPLUS __cplusplus +# endif +#endif + +#define span_CPP98_OR_GREATER ( span_CPLUSPLUS >= 199711L ) +#define span_CPP11_OR_GREATER ( span_CPLUSPLUS >= 201103L ) +#define span_CPP14_OR_GREATER ( span_CPLUSPLUS >= 201402L ) +#define span_CPP17_OR_GREATER ( span_CPLUSPLUS >= 201703L ) +#define span_CPP20_OR_GREATER ( span_CPLUSPLUS >= 202002L ) +#define span_CPP23_OR_GREATER ( span_CPLUSPLUS >= 202300L ) + +// C++ language version (represent 98 as 3): + +#define span_CPLUSPLUS_V ( span_CPLUSPLUS / 100 - (span_CPLUSPLUS > 200000 ? 2000 : 1994) ) + +#define span_IN_STD( v ) ( ((v) == 98 ? 3 : (v)) >= span_CPLUSPLUS_V ) + +#define span_CONFIG( feature ) ( span_CONFIG_##feature ) +#define span_FEATURE( feature ) ( span_FEATURE_##feature ) +#define span_FEATURE_TO_STD( feature ) ( span_IN_STD( span_FEATURE( feature##_TO_STD ) ) ) + +// Use C++20 std::span if available and requested: + +#if span_CPP20_OR_GREATER && defined(__has_include ) +# if __has_include( <span> ) +# define span_HAVE_STD_SPAN 1 +# else +# define span_HAVE_STD_SPAN 0 +# endif +#else +# define span_HAVE_STD_SPAN 0 +#endif + +#define span_USES_STD_SPAN ( (span_CONFIG_SELECT_SPAN == span_SPAN_STD) || ((span_CONFIG_SELECT_SPAN == span_SPAN_DEFAULT) && span_HAVE_STD_SPAN) ) + +// +// Use C++20 std::span: +// + +#if span_USES_STD_SPAN + +#include <span> + +namespace nonstd { + +using std::span; +using std::dynamic_extent; + +// Note: C++20 does not provide comparison +// using std::operator==; +// using std::operator!=; +// using std::operator<; +// using std::operator<=; +// using std::operator>; +// using std::operator>=; +} // namespace nonstd + +#else // span_USES_STD_SPAN + +#include <algorithm> + +// Compiler versions: +// +// MSVC++ 6.0 _MSC_VER == 1200 span_COMPILER_MSVC_VERSION == 60 (Visual Studio 6.0) +// MSVC++ 7.0 _MSC_VER == 1300 span_COMPILER_MSVC_VERSION == 70 (Visual Studio .NET 2002) +// MSVC++ 7.1 _MSC_VER == 1310 span_COMPILER_MSVC_VERSION == 71 (Visual Studio .NET 2003) +// MSVC++ 8.0 _MSC_VER == 1400 span_COMPILER_MSVC_VERSION == 80 (Visual Studio 2005) +// MSVC++ 9.0 _MSC_VER == 1500 span_COMPILER_MSVC_VERSION == 90 (Visual Studio 2008) +// MSVC++ 10.0 _MSC_VER == 1600 span_COMPILER_MSVC_VERSION == 100 (Visual Studio 2010) +// MSVC++ 11.0 _MSC_VER == 1700 span_COMPILER_MSVC_VERSION == 110 (Visual Studio 2012) +// MSVC++ 12.0 _MSC_VER == 1800 span_COMPILER_MSVC_VERSION == 120 (Visual Studio 2013) +// MSVC++ 14.0 _MSC_VER == 1900 span_COMPILER_MSVC_VERSION == 140 (Visual Studio 2015) +// MSVC++ 14.1 _MSC_VER >= 1910 span_COMPILER_MSVC_VERSION == 141 (Visual Studio 2017) +// MSVC++ 14.2 _MSC_VER >= 1920 span_COMPILER_MSVC_VERSION == 142 (Visual Studio 2019) + +#if defined(_MSC_VER ) && !defined(__clang__) +# define span_COMPILER_MSVC_VER (_MSC_VER ) +# define span_COMPILER_MSVC_VERSION (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) ) +#else +# define span_COMPILER_MSVC_VER 0 +# define span_COMPILER_MSVC_VERSION 0 +#endif + +#define span_COMPILER_VERSION( major, minor, patch ) ( 10 * ( 10 * (major) + (minor) ) + (patch) ) + +#if defined(__clang__) +# define span_COMPILER_CLANG_VERSION span_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__) +#else +# define span_COMPILER_CLANG_VERSION 0 +#endif + +#if defined(__GNUC__) && !defined(__clang__) +# define span_COMPILER_GNUC_VERSION span_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#else +# define span_COMPILER_GNUC_VERSION 0 +#endif + +// half-open range [lo..hi): +#define span_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) ) + +// Compiler warning suppression: + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wundef" +# pragma clang diagnostic ignored "-Wmismatched-tags" +# define span_RESTORE_WARNINGS() _Pragma( "clang diagnostic pop" ) + +#elif defined __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wundef" +# define span_RESTORE_WARNINGS() _Pragma( "GCC diagnostic pop" ) + +#elif span_COMPILER_MSVC_VER >= 1900 +# define span_DISABLE_MSVC_WARNINGS(codes) __pragma(warning(push)) __pragma(warning(disable: codes)) +# define span_RESTORE_WARNINGS() __pragma(warning(pop )) + +// Suppress the following MSVC GSL warnings: +// - C26439, gsl::f.6 : special function 'function' can be declared 'noexcept' +// - C26440, gsl::f.6 : function 'function' can be declared 'noexcept' +// - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions; +// use brace initialization, gsl::narrow_cast or gsl::narrow +// - C26473: gsl::t.1 : don't cast between pointer types where the source type and the target type are the same +// - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead +// - C26490: gsl::t.1 : don't use reinterpret_cast + +span_DISABLE_MSVC_WARNINGS( 26439 26440 26472 26473 26481 26490 ) + +#else +# define span_RESTORE_WARNINGS() /*empty*/ +#endif + +// Presence of language and library features: + +#ifdef _HAS_CPP0X +# define span_HAS_CPP0X _HAS_CPP0X +#else +# define span_HAS_CPP0X 0 +#endif + +#define span_CPP11_80 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1400) +#define span_CPP11_90 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1500) +#define span_CPP11_100 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1600) +#define span_CPP11_110 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1700) +#define span_CPP11_120 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1800) +#define span_CPP11_140 (span_CPP11_OR_GREATER || span_COMPILER_MSVC_VER >= 1900) + +#define span_CPP14_000 (span_CPP14_OR_GREATER) +#define span_CPP14_120 (span_CPP14_OR_GREATER || span_COMPILER_MSVC_VER >= 1800) +#define span_CPP14_140 (span_CPP14_OR_GREATER || span_COMPILER_MSVC_VER >= 1900) + +#define span_CPP17_000 (span_CPP17_OR_GREATER) + +// Presence of C++11 language features: + +#define span_HAVE_ALIAS_TEMPLATE span_CPP11_140 +#define span_HAVE_AUTO span_CPP11_100 +#define span_HAVE_CONSTEXPR_11 span_CPP11_140 +#define span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG span_CPP11_120 +#define span_HAVE_EXPLICIT_CONVERSION span_CPP11_140 +#define span_HAVE_INITIALIZER_LIST span_CPP11_120 +#define span_HAVE_IS_DEFAULT span_CPP11_140 +#define span_HAVE_IS_DELETE span_CPP11_140 +#define span_HAVE_NOEXCEPT span_CPP11_140 +#define span_HAVE_NORETURN ( span_CPP11_140 && ! span_BETWEEN( span_COMPILER_GNUC_VERSION, 1, 480 ) ) +#define span_HAVE_NULLPTR span_CPP11_100 +#define span_HAVE_STATIC_ASSERT span_CPP11_100 + +// Presence of C++14 language features: + +#define span_HAVE_CONSTEXPR_14 span_CPP14_000 + +// Presence of C++17 language features: + +#define span_HAVE_DEPRECATED span_CPP17_000 +#define span_HAVE_NODISCARD span_CPP17_000 + +// MSVC: template parameter deduction guides since Visual Studio 2017 v15.7 + +#if defined(__cpp_deduction_guides) +# define span_HAVE_DEDUCTION_GUIDES 1 +#else +# define span_HAVE_DEDUCTION_GUIDES (span_CPP17_OR_GREATER && ! span_BETWEEN( span_COMPILER_MSVC_VER, 1, 1913 )) +#endif + +// Presence of C++ library features: + +#define span_HAVE_ADDRESSOF span_CPP17_000 +#define span_HAVE_ARRAY span_CPP11_110 +#define span_HAVE_BYTE span_CPP17_000 +#define span_HAVE_CONDITIONAL span_CPP11_120 +#define span_HAVE_CONTAINER_DATA_METHOD (span_CPP11_140 || ( span_COMPILER_MSVC_VER >= 1500 && span_HAS_CPP0X )) +#define span_HAVE_DATA span_CPP17_000 +#define span_HAVE_LONGLONG span_CPP11_80 +#define span_HAVE_REMOVE_CONST span_CPP11_110 +#define span_HAVE_SNPRINTF span_CPP11_140 +#define span_HAVE_STRUCT_BINDING span_CPP11_120 +#define span_HAVE_TYPE_TRAITS span_CPP11_90 + +// Presence of byte-lite: + +#ifdef NONSTD_BYTE_LITE_HPP +# define span_HAVE_NONSTD_BYTE 1 +#else +# define span_HAVE_NONSTD_BYTE 0 +#endif + +// C++ feature usage: + +#if span_HAVE_ADDRESSOF +# define span_ADDRESSOF(x) std::addressof(x) +#else +# define span_ADDRESSOF(x) (&x) +#endif + +#if span_HAVE_CONSTEXPR_11 +# define span_constexpr constexpr +#else +# define span_constexpr /*span_constexpr*/ +#endif + +#if span_HAVE_CONSTEXPR_14 +# define span_constexpr14 constexpr +#else +# define span_constexpr14 /*span_constexpr*/ +#endif + +#if span_HAVE_EXPLICIT_CONVERSION +# define span_explicit explicit +#else +# define span_explicit /*explicit*/ +#endif + +#if span_HAVE_IS_DELETE +# define span_is_delete = delete +#else +# define span_is_delete +#endif + +#if span_HAVE_IS_DELETE +# define span_is_delete_access public +#else +# define span_is_delete_access private +#endif + +#if span_HAVE_NOEXCEPT && ! span_CONFIG_CONTRACT_VIOLATION_THROWS_V +# define span_noexcept noexcept +#else +# define span_noexcept /*noexcept*/ +#endif + +#if span_HAVE_NULLPTR +# define span_nullptr nullptr +#else +# define span_nullptr NULL +#endif + +#if span_HAVE_DEPRECATED +# define span_deprecated(msg) [[deprecated(msg)]] +#else +# define span_deprecated(msg) /*[[deprecated]]*/ +#endif + +#if span_HAVE_NODISCARD +# define span_nodiscard [[nodiscard]] +#else +# define span_nodiscard /*[[nodiscard]]*/ +#endif + +#if span_HAVE_NORETURN +# define span_noreturn [[noreturn]] +#else +# define span_noreturn /*[[noreturn]]*/ +#endif + +// Other features: + +#define span_HAVE_CONSTRAINED_SPAN_CONTAINER_CTOR span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG +#define span_HAVE_ITERATOR_CTOR span_HAVE_DEFAULT_FUNCTION_TEMPLATE_ARG + +// Additional includes: + +#if span_HAVE( ADDRESSOF ) +# include <memory> +#endif + +#if span_HAVE( ARRAY ) +# include <array> +#endif + +#if span_HAVE( BYTE ) +# include <cstddef> +#endif + +#if span_HAVE( DATA ) +# include <iterator> // for std::data(), std::size() +#endif + +#if span_HAVE( TYPE_TRAITS ) +# include <type_traits> +#endif + +#if ! span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) +# include <vector> +#endif + +#if span_FEATURE( MEMBER_AT ) > 1 +# include <cstdio> +#endif + +#if ! span_CONFIG( NO_EXCEPTIONS ) +# include <stdexcept> +#endif + +// Contract violation + +#define span_ELIDE_CONTRACT_EXPECTS ( 0 == ( span_CONFIG_CONTRACT_LEVEL_MASK & 0x01 ) ) +#define span_ELIDE_CONTRACT_ENSURES ( 0 == ( span_CONFIG_CONTRACT_LEVEL_MASK & 0x10 ) ) + +#if span_ELIDE_CONTRACT_EXPECTS +# define span_constexpr_exp span_constexpr +# define span_EXPECTS( cond ) /* Expect elided */ +#else +# define span_constexpr_exp span_constexpr14 +# define span_EXPECTS( cond ) span_CONTRACT_CHECK( "Precondition", cond ) +#endif + +#if span_ELIDE_CONTRACT_ENSURES +# define span_constexpr_ens span_constexpr +# define span_ENSURES( cond ) /* Ensures elided */ +#else +# define span_constexpr_ens span_constexpr14 +# define span_ENSURES( cond ) span_CONTRACT_CHECK( "Postcondition", cond ) +#endif + +#define span_CONTRACT_CHECK( type, cond ) \ + cond ? static_cast< void >( 0 ) \ + : nonstd::span_lite::detail::report_contract_violation( span_LOCATION( __FILE__, __LINE__ ) ": " type " violation." ) + +#ifdef __GNUG__ +# define span_LOCATION( file, line ) file ":" span_STRINGIFY( line ) +#else +# define span_LOCATION( file, line ) file "(" span_STRINGIFY( line ) ")" +#endif + +// Method enabling + +#if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + +#define span_REQUIRES_0(VA) \ + template< bool B = (VA), typename std::enable_if<B, int>::type = 0 > + +# if span_BETWEEN( span_COMPILER_MSVC_VERSION, 1, 140 ) +// VS 2013 and earlier seem to have trouble with SFINAE for default non-type arguments +# define span_REQUIRES_T(VA) \ + , typename = typename std::enable_if< ( VA ), nonstd::span_lite::detail::enabler >::type +# else +# define span_REQUIRES_T(VA) \ + , typename std::enable_if< (VA), int >::type = 0 +# endif + +#define span_REQUIRES_R(R, VA) \ + typename std::enable_if< (VA), R>::type + +#define span_REQUIRES_A(VA) \ + , typename std::enable_if< (VA), void*>::type = nullptr + +#else + +# define span_REQUIRES_0(VA) /*empty*/ +# define span_REQUIRES_T(VA) /*empty*/ +# define span_REQUIRES_R(R, VA) R +# define span_REQUIRES_A(VA) /*empty*/ + +#endif + +namespace nonstd { +namespace span_lite { + +// [views.constants], constants + +typedef span_CONFIG_EXTENT_TYPE extent_t; +typedef span_CONFIG_SIZE_TYPE size_t; + +span_constexpr const extent_t dynamic_extent = static_cast<extent_t>( -1 ); + +template< class T, extent_t Extent = dynamic_extent > +class span; + +// Tag to select span constructor taking a container (prevent ms-gsl warning C26426): + +struct with_container_t { span_constexpr with_container_t() span_noexcept {} }; +const span_constexpr with_container_t with_container; + +// C++11 emulation: + +namespace std11 { + +#if span_HAVE( REMOVE_CONST ) + +using std::remove_cv; +using std::remove_const; +using std::remove_volatile; + +#else + +template< class T > struct remove_const { typedef T type; }; +template< class T > struct remove_const< T const > { typedef T type; }; + +template< class T > struct remove_volatile { typedef T type; }; +template< class T > struct remove_volatile< T volatile > { typedef T type; }; + +template< class T > +struct remove_cv +{ + typedef typename std11::remove_volatile< typename std11::remove_const< T >::type >::type type; +}; + +#endif // span_HAVE( REMOVE_CONST ) + +#if span_HAVE( TYPE_TRAITS ) + +using std::is_same; +using std::is_signed; +using std::integral_constant; +using std::true_type; +using std::false_type; +using std::remove_reference; + +#else + +template< class T, T v > struct integral_constant { enum { value = v }; }; +typedef integral_constant< bool, true > true_type; +typedef integral_constant< bool, false > false_type; + +template< class T, class U > struct is_same : false_type{}; +template< class T > struct is_same<T, T> : true_type{}; + +template< typename T > struct is_signed : false_type {}; +template<> struct is_signed<signed char> : true_type {}; +template<> struct is_signed<signed int > : true_type {}; +template<> struct is_signed<signed long> : true_type {}; + +#endif + +} // namespace std11 + +// C++17 emulation: + +namespace std17 { + +template< bool v > struct bool_constant : std11::integral_constant<bool, v>{}; + +#if span_CPP11_120 + +template< class...> +using void_t = void; + +#endif + +#if span_HAVE( DATA ) + +using std::data; +using std::size; + +#elif span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + +template< typename T, std::size_t N > +inline span_constexpr auto size( const T(&)[N] ) span_noexcept -> size_t +{ + return N; +} + +template< typename C > +inline span_constexpr auto size( C const & cont ) -> decltype( cont.size() ) +{ + return cont.size(); +} + +template< typename T, std::size_t N > +inline span_constexpr auto data( T(&arr)[N] ) span_noexcept -> T* +{ + return &arr[0]; +} + +template< typename C > +inline span_constexpr auto data( C & cont ) -> decltype( cont.data() ) +{ + return cont.data(); +} + +template< typename C > +inline span_constexpr auto data( C const & cont ) -> decltype( cont.data() ) +{ + return cont.data(); +} + +template< typename E > +inline span_constexpr auto data( std::initializer_list<E> il ) span_noexcept -> E const * +{ + return il.begin(); +} + +#endif // span_HAVE( DATA ) + +#if span_HAVE( BYTE ) +using std::byte; +#elif span_HAVE( NONSTD_BYTE ) +using nonstd::byte; +#endif + +} // namespace std17 + +// C++20 emulation: + +namespace std20 { + +#if span_HAVE( DEDUCTION_GUIDES ) +template< class T > +using iter_reference_t = decltype( *std::declval<T&>() ); +#endif + +} // namespace std20 + +// Implementation details: + +namespace detail { + +/*enum*/ struct enabler{}; + +template< typename T > +span_constexpr bool is_positive( T x ) +{ + return std11::is_signed<T>::value ? x >= 0 : true; +} + +#if span_HAVE( TYPE_TRAITS ) + +template< class Q > +struct is_span_oracle : std::false_type{}; + +template< class T, span_CONFIG_EXTENT_TYPE Extent > +struct is_span_oracle< span<T, Extent> > : std::true_type{}; + +template< class Q > +struct is_span : is_span_oracle< typename std::remove_cv<Q>::type >{}; + +template< class Q > +struct is_std_array_oracle : std::false_type{}; + +#if span_HAVE( ARRAY ) + +template< class T, std::size_t Extent > +struct is_std_array_oracle< std::array<T, Extent> > : std::true_type{}; + +#endif + +template< class Q > +struct is_std_array : is_std_array_oracle< typename std::remove_cv<Q>::type >{}; + +template< class Q > +struct is_array : std::false_type {}; + +template< class T > +struct is_array<T[]> : std::true_type {}; + +template< class T, std::size_t N > +struct is_array<T[N]> : std::true_type {}; + +#if span_CPP11_140 && ! span_BETWEEN( span_COMPILER_GNUC_VERSION, 1, 500 ) + +template< class, class = void > +struct has_size_and_data : std::false_type{}; + +template< class C > +struct has_size_and_data +< + C, std17::void_t< + decltype( std17::size(std::declval<C>()) ), + decltype( std17::data(std::declval<C>()) ) > +> : std::true_type{}; + +template< class, class, class = void > +struct is_compatible_element : std::false_type {}; + +template< class C, class E > +struct is_compatible_element +< + C, E, std17::void_t< + decltype( std17::data(std::declval<C>()) ) > +> : std::is_convertible< typename std::remove_pointer<decltype( std17::data( std::declval<C&>() ) )>::type(*)[], E(*)[] >{}; + +template< class C > +struct is_container : std17::bool_constant +< + ! is_span< C >::value + && ! is_array< C >::value + && ! is_std_array< C >::value + && has_size_and_data< C >::value +>{}; + +template< class C, class E > +struct is_compatible_container : std17::bool_constant +< + is_container<C>::value + && is_compatible_element<C,E>::value +>{}; + +#else // span_CPP11_140 + +template< + class C, class E + span_REQUIRES_T(( + ! is_span< C >::value + && ! is_array< C >::value + && ! is_std_array< C >::value + && ( std::is_convertible< typename std::remove_pointer<decltype( std17::data( std::declval<C&>() ) )>::type(*)[], E(*)[] >::value) + // && has_size_and_data< C >::value + )) + , class = decltype( std17::size(std::declval<C>()) ) + , class = decltype( std17::data(std::declval<C>()) ) +> +struct is_compatible_container : std::true_type{}; + +#endif // span_CPP11_140 + +#endif // span_HAVE( TYPE_TRAITS ) + +#if ! span_CONFIG( NO_EXCEPTIONS ) +#if span_FEATURE( MEMBER_AT ) > 1 + +// format index and size: + +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wlong-long" +#elif defined __GNUC__ +# pragma GCC diagnostic ignored "-Wformat=ll" +# pragma GCC diagnostic ignored "-Wlong-long" +#endif + +span_noreturn inline void throw_out_of_range( size_t idx, size_t size ) +{ + const char fmt[] = "span::at(): index '%lli' is out of range [0..%lli)"; + char buffer[ 2 * 20 + sizeof fmt ]; + sprintf( buffer, fmt, static_cast<long long>(idx), static_cast<long long>(size) ); + + throw std::out_of_range( buffer ); +} + +#else // MEMBER_AT + +span_noreturn inline void throw_out_of_range( size_t /*idx*/, size_t /*size*/ ) +{ + throw std::out_of_range( "span::at(): index outside span" ); +} +#endif // MEMBER_AT +#endif // NO_EXCEPTIONS + +#if span_CONFIG( CONTRACT_VIOLATION_THROWS_V ) + +struct contract_violation : std::logic_error +{ + explicit contract_violation( char const * const message ) + : std::logic_error( message ) + {} +}; + +inline void report_contract_violation( char const * msg ) +{ + throw contract_violation( msg ); +} + +#else // span_CONFIG( CONTRACT_VIOLATION_THROWS_V ) + +span_noreturn inline void report_contract_violation( char const * /*msg*/ ) span_noexcept +{ + std::terminate(); +} + +#endif // span_CONFIG( CONTRACT_VIOLATION_THROWS_V ) + +} // namespace detail + +// Prevent signed-unsigned mismatch: + +#define span_sizeof(T) static_cast<extent_t>( sizeof(T) ) + +template< class T > +inline span_constexpr size_t to_size( T size ) +{ + return static_cast<size_t>( size ); +} + +// +// [views.span] - A view over a contiguous, single-dimension sequence of objects +// +template< class T, extent_t Extent /*= dynamic_extent*/ > +class span +{ +public: + // constants and types + + typedef T element_type; + typedef typename std11::remove_cv< T >::type value_type; + + typedef T & reference; + typedef T * pointer; + typedef T const * const_pointer; + typedef T const & const_reference; + + typedef size_t size_type; + typedef extent_t extent_type; + + typedef pointer iterator; + typedef const_pointer const_iterator; + + typedef std::ptrdiff_t difference_type; + + typedef std::reverse_iterator< iterator > reverse_iterator; + typedef std::reverse_iterator< const_iterator > const_reverse_iterator; + +// static constexpr extent_type extent = Extent; + enum { extent = Extent }; + + // 26.7.3.2 Constructors, copy, and assignment [span.cons] + + span_REQUIRES_0( + ( Extent == 0 ) || + ( Extent == dynamic_extent ) + ) + span_constexpr span() span_noexcept + : data_( span_nullptr ) + , size_( 0 ) + { + // span_EXPECTS( data() == span_nullptr ); + // span_EXPECTS( size() == 0 ); + } + +#if span_HAVE( ITERATOR_CTOR ) + // Didn't yet succeed in combining the next two constructors: + + span_constexpr_exp span( std::nullptr_t, size_type count ) + : data_( span_nullptr ) + , size_( count ) + { + span_EXPECTS( data_ == span_nullptr && count == 0 ); + } + + template< typename It + span_REQUIRES_T(( + std::is_convertible<decltype(*std::declval<It&>()), element_type &>::value + )) + > + span_constexpr_exp span( It first, size_type count ) + : data_( to_address( first ) ) + , size_( count ) + { + span_EXPECTS( + ( data_ == span_nullptr && count == 0 ) || + ( data_ != span_nullptr && detail::is_positive( count ) ) + ); + } +#else + span_constexpr_exp span( pointer ptr, size_type count ) + : data_( ptr ) + , size_( count ) + { + span_EXPECTS( + ( ptr == span_nullptr && count == 0 ) || + ( ptr != span_nullptr && detail::is_positive( count ) ) + ); + } +#endif + +#if span_HAVE( ITERATOR_CTOR ) + template< typename It, typename End + span_REQUIRES_T(( + std::is_convertible<decltype(&*std::declval<It&>()), element_type *>::value + && ! std::is_convertible<End, std::size_t>::value + )) + > + span_constexpr_exp span( It first, End last ) + : data_( to_address( first ) ) + , size_( to_size( last - first ) ) + { + span_EXPECTS( + last - first >= 0 + ); + } +#else + span_constexpr_exp span( pointer first, pointer last ) + : data_( first ) + , size_( to_size( last - first ) ) + { + span_EXPECTS( + last - first >= 0 + ); + } +#endif + + template< std::size_t N + span_REQUIRES_T(( + (Extent == dynamic_extent || Extent == static_cast<extent_t>(N)) + && std::is_convertible< value_type(*)[], element_type(*)[] >::value + )) + > + span_constexpr span( element_type ( &arr )[ N ] ) span_noexcept + : data_( span_ADDRESSOF( arr[0] ) ) + , size_( N ) + {} + +#if span_HAVE( ARRAY ) + + template< std::size_t N + span_REQUIRES_T(( + (Extent == dynamic_extent || Extent == static_cast<extent_t>(N)) + && std::is_convertible< value_type(*)[], element_type(*)[] >::value + )) + > +# if span_FEATURE( CONSTRUCTION_FROM_STDARRAY_ELEMENT_TYPE ) + span_constexpr span( std::array< element_type, N > & arr ) span_noexcept +# else + span_constexpr span( std::array< value_type, N > & arr ) span_noexcept +# endif + : data_( arr.data() ) + , size_( to_size( arr.size() ) ) + {} + + template< std::size_t N +# if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + span_REQUIRES_T(( + (Extent == dynamic_extent || Extent == static_cast<extent_t>(N)) + && std::is_convertible< value_type(*)[], element_type(*)[] >::value + )) +# endif + > + span_constexpr span( std::array< value_type, N> const & arr ) span_noexcept + : data_( arr.data() ) + , size_( to_size( arr.size() ) ) + {} + +#endif // span_HAVE( ARRAY ) + +#if span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + template< class Container + span_REQUIRES_T(( + detail::is_compatible_container< Container, element_type >::value + )) + > + span_constexpr span( Container & cont ) + : data_( std17::data( cont ) ) + , size_( to_size( std17::size( cont ) ) ) + {} + + template< class Container + span_REQUIRES_T(( + std::is_const< element_type >::value + && detail::is_compatible_container< Container, element_type >::value + )) + > + span_constexpr span( Container const & cont ) + : data_( std17::data( cont ) ) + , size_( to_size( std17::size( cont ) ) ) + {} + +#endif // span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + +#if span_FEATURE( WITH_CONTAINER ) + + template< class Container > + span_constexpr span( with_container_t, Container & cont ) + : data_( cont.size() == 0 ? span_nullptr : span_ADDRESSOF( cont[0] ) ) + , size_( to_size( cont.size() ) ) + {} + + template< class Container > + span_constexpr span( with_container_t, Container const & cont ) + : data_( cont.size() == 0 ? span_nullptr : const_cast<pointer>( span_ADDRESSOF( cont[0] ) ) ) + , size_( to_size( cont.size() ) ) + {} +#endif + +#if span_FEATURE( WITH_INITIALIZER_LIST_P2447 ) && span_HAVE( INITIALIZER_LIST ) + + // constexpr explicit(extent != dynamic_extent) span(std::initializer_list<value_type> il) noexcept; + +#if !span_BETWEEN( span_COMPILER_MSVC_VERSION, 120, 130 ) + + template< extent_t U = Extent + span_REQUIRES_T(( + U != dynamic_extent + )) + > +#if span_COMPILER_GNUC_VERSION >= 900 // prevent GCC's "-Winit-list-lifetime" + span_constexpr14 explicit span( std::initializer_list<value_type> il ) span_noexcept + { + data_ = il.begin(); + size_ = il.size(); + } +#else + span_constexpr explicit span( std::initializer_list<value_type> il ) span_noexcept + : data_( il.begin() ) + , size_( il.size() ) + {} +#endif + +#endif // MSVC 120 (VS2013) + + template< extent_t U = Extent + span_REQUIRES_T(( + U == dynamic_extent + )) + > +#if span_COMPILER_GNUC_VERSION >= 900 // prevent GCC's "-Winit-list-lifetime" + span_constexpr14 /*explicit*/ span( std::initializer_list<value_type> il ) span_noexcept + { + data_ = il.begin(); + size_ = il.size(); + } +#else + span_constexpr /*explicit*/ span( std::initializer_list<value_type> il ) span_noexcept + : data_( il.begin() ) + , size_( il.size() ) + {} +#endif + +#endif // P2447 + +#if span_HAVE( IS_DEFAULT ) + span_constexpr span( span const & other ) span_noexcept = default; + + ~span() span_noexcept = default; + + span_constexpr14 span & operator=( span const & other ) span_noexcept = default; +#else + span_constexpr span( span const & other ) span_noexcept + : data_( other.data_ ) + , size_( other.size_ ) + {} + + ~span() span_noexcept + {} + + span_constexpr14 span & operator=( span const & other ) span_noexcept + { + data_ = other.data_; + size_ = other.size_; + + return *this; + } +#endif + + template< class OtherElementType, extent_type OtherExtent + span_REQUIRES_T(( + (Extent == dynamic_extent || OtherExtent == dynamic_extent || Extent == OtherExtent) + && std::is_convertible<OtherElementType(*)[], element_type(*)[]>::value + )) + > + span_constexpr_exp span( span<OtherElementType, OtherExtent> const & other ) span_noexcept + : data_( other.data() ) + , size_( other.size() ) + { + span_EXPECTS( OtherExtent == dynamic_extent || other.size() == to_size(OtherExtent) ); + } + + // 26.7.3.3 Subviews [span.sub] + + template< extent_type Count > + span_constexpr_exp span< element_type, Count > + first() const + { + span_EXPECTS( detail::is_positive( Count ) && Count <= size() ); + + return span< element_type, Count >( data(), Count ); + } + + template< extent_type Count > + span_constexpr_exp span< element_type, Count > + last() const + { + span_EXPECTS( detail::is_positive( Count ) && Count <= size() ); + + return span< element_type, Count >( data() + (size() - Count), Count ); + } + +#if span_HAVE( DEFAULT_FUNCTION_TEMPLATE_ARG ) + template< size_type Offset, extent_type Count = dynamic_extent > +#else + template< size_type Offset, extent_type Count /*= dynamic_extent*/ > +#endif + span_constexpr_exp span< element_type, Count > + subspan() const + { + span_EXPECTS( + ( detail::is_positive( Offset ) && Offset <= size() ) && + ( Count == dynamic_extent || (detail::is_positive( Count ) && Count + Offset <= size()) ) + ); + + return span< element_type, Count >( + data() + Offset, Count != dynamic_extent ? Count : (Extent != dynamic_extent ? Extent - Offset : size() - Offset) ); + } + + span_constexpr_exp span< element_type, dynamic_extent > + first( size_type count ) const + { + span_EXPECTS( detail::is_positive( count ) && count <= size() ); + + return span< element_type, dynamic_extent >( data(), count ); + } + + span_constexpr_exp span< element_type, dynamic_extent > + last( size_type count ) const + { + span_EXPECTS( detail::is_positive( count ) && count <= size() ); + + return span< element_type, dynamic_extent >( data() + ( size() - count ), count ); + } + + span_constexpr_exp span< element_type, dynamic_extent > + subspan( size_type offset, size_type count = static_cast<size_type>(dynamic_extent) ) const + { + span_EXPECTS( + ( ( detail::is_positive( offset ) && offset <= size() ) ) && + ( count == static_cast<size_type>(dynamic_extent) || ( detail::is_positive( count ) && offset + count <= size() ) ) + ); + + return span< element_type, dynamic_extent >( + data() + offset, count == static_cast<size_type>(dynamic_extent) ? size() - offset : count ); + } + + // 26.7.3.4 Observers [span.obs] + + span_constexpr size_type size() const span_noexcept + { + return size_; + } + + span_constexpr std::ptrdiff_t ssize() const span_noexcept + { + return static_cast<std::ptrdiff_t>( size_ ); + } + + span_constexpr size_type size_bytes() const span_noexcept + { + return size() * to_size( sizeof( element_type ) ); + } + + span_nodiscard span_constexpr bool empty() const span_noexcept + { + return size() == 0; + } + + // 26.7.3.5 Element access [span.elem] + + span_constexpr_exp reference operator[]( size_type idx ) const + { + span_EXPECTS( detail::is_positive( idx ) && idx < size() ); + + return *( data() + idx ); + } + +#if span_FEATURE( MEMBER_CALL_OPERATOR ) + span_deprecated("replace operator() with operator[]") + + span_constexpr_exp reference operator()( size_type idx ) const + { + span_EXPECTS( detail::is_positive( idx ) && idx < size() ); + + return *( data() + idx ); + } +#endif + +#if span_FEATURE( MEMBER_AT ) + span_constexpr14 reference at( size_type idx ) const + { +#if span_CONFIG( NO_EXCEPTIONS ) + return this->operator[]( idx ); +#else + if ( !detail::is_positive( idx ) || size() <= idx ) + { + detail::throw_out_of_range( idx, size() ); + } + return *( data() + idx ); +#endif + } +#endif + + span_constexpr pointer data() const span_noexcept + { + return data_; + } + +#if span_FEATURE( MEMBER_BACK_FRONT ) + + span_constexpr_exp reference front() const span_noexcept + { + span_EXPECTS( ! empty() ); + + return *data(); + } + + span_constexpr_exp reference back() const span_noexcept + { + span_EXPECTS( ! empty() ); + + return *( data() + size() - 1 ); + } + +#endif + + // xx.x.x.x Modifiers [span.modifiers] + +#if span_FEATURE( MEMBER_SWAP ) + + span_constexpr14 void swap( span & other ) span_noexcept + { + using std::swap; + swap( data_, other.data_ ); + swap( size_, other.size_ ); + } +#endif + + // 26.7.3.6 Iterator support [span.iterators] + + span_constexpr iterator begin() const span_noexcept + { +#if span_CPP11_OR_GREATER + return { data() }; +#else + return iterator( data() ); +#endif + } + + span_constexpr iterator end() const span_noexcept + { +#if span_CPP11_OR_GREATER + return { data() + size() }; +#else + return iterator( data() + size() ); +#endif + } + + span_constexpr const_iterator cbegin() const span_noexcept + { +#if span_CPP11_OR_GREATER + return { data() }; +#else + return const_iterator( data() ); +#endif + } + + span_constexpr const_iterator cend() const span_noexcept + { +#if span_CPP11_OR_GREATER + return { data() + size() }; +#else + return const_iterator( data() + size() ); +#endif + } + + span_constexpr reverse_iterator rbegin() const span_noexcept + { + return reverse_iterator( end() ); + } + + span_constexpr reverse_iterator rend() const span_noexcept + { + return reverse_iterator( begin() ); + } + + span_constexpr const_reverse_iterator crbegin() const span_noexcept + { + return const_reverse_iterator ( cend() ); + } + + span_constexpr const_reverse_iterator crend() const span_noexcept + { + return const_reverse_iterator( cbegin() ); + } + +private: + + // Note: C++20 has std::pointer_traits<Ptr>::to_address( it ); + +#if span_HAVE( ITERATOR_CTOR ) + static inline span_constexpr pointer to_address( std::nullptr_t ) span_noexcept + { + return nullptr; + } + + template< typename U > + static inline span_constexpr U * to_address( U * p ) span_noexcept + { + return p; + } + + template< typename Ptr + span_REQUIRES_T(( ! std::is_pointer<Ptr>::value )) + > + static inline span_constexpr pointer to_address( Ptr const & it ) span_noexcept + { + return to_address( it.operator->() ); + } +#endif // span_HAVE( ITERATOR_CTOR ) + +private: + pointer data_; + size_type size_; +}; + +// class template argument deduction guides: + +#if span_HAVE( DEDUCTION_GUIDES ) + +template< class T, size_t N > +span( T (&)[N] ) -> span<T, static_cast<extent_t>(N)>; + +template< class T, size_t N > +span( std::array<T, N> & ) -> span<T, static_cast<extent_t>(N)>; + +template< class T, size_t N > +span( std::array<T, N> const & ) -> span<const T, static_cast<extent_t>(N)>; + +#if span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) + +template< class Container > +span( Container& ) -> span<typename Container::value_type>; + +template< class Container > +span( Container const & ) -> span<const typename Container::value_type>; + +#endif + +// iterator: constraints: It satisfies contiguous_Âiterator. + +template< class It, class EndOrSize > +span( It, EndOrSize ) -> span< typename std11::remove_reference< typename std20::iter_reference_t<It> >::type >; + +#endif // span_HAVE( DEDUCTION_GUIDES ) + +// 26.7.3.7 Comparison operators [span.comparison] + +#if span_FEATURE( COMPARISON ) +#if span_FEATURE( SAME ) + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool same( span<T1,E1> const & l, span<T2,E2> const & r ) span_noexcept +{ + return std11::is_same<T1, T2>::value + && l.size() == r.size() + && static_cast<void const*>( l.data() ) == r.data(); +} + +#endif + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator==( span<T1,E1> const & l, span<T2,E2> const & r ) +{ + return +#if span_FEATURE( SAME ) + same( l, r ) || +#endif + ( l.size() == r.size() && std::equal( l.begin(), l.end(), r.begin() ) ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator<( span<T1,E1> const & l, span<T2,E2> const & r ) +{ + return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end() ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator!=( span<T1,E1> const & l, span<T2,E2> const & r ) +{ + return !( l == r ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator<=( span<T1,E1> const & l, span<T2,E2> const & r ) +{ + return !( r < l ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator>( span<T1,E1> const & l, span<T2,E2> const & r ) +{ + return ( r < l ); +} + +template< class T1, extent_t E1, class T2, extent_t E2 > +inline span_constexpr bool operator>=( span<T1,E1> const & l, span<T2,E2> const & r ) +{ + return !( l < r ); +} + +#endif // span_FEATURE( COMPARISON ) + +// 26.7.2.6 views of object representation [span.objectrep] + +#if span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE ) + +// Avoid MSVC 14.1 (1910), VS 2017: warning C4307: '*': integral constant overflow: + +template< typename T, extent_t Extent > +struct BytesExtent +{ +#if span_CPP11_OR_GREATER + enum ET : extent_t { value = span_sizeof(T) * Extent }; +#else + enum ET { value = span_sizeof(T) * Extent }; +#endif +}; + +template< typename T > +struct BytesExtent< T, dynamic_extent > +{ +#if span_CPP11_OR_GREATER + enum ET : extent_t { value = dynamic_extent }; +#else + enum ET { value = dynamic_extent }; +#endif +}; + +template< class T, extent_t Extent > +inline span_constexpr span< const std17::byte, BytesExtent<T, Extent>::value > +as_bytes( span<T,Extent> spn ) span_noexcept +{ +#if 0 + return { reinterpret_cast< std17::byte const * >( spn.data() ), spn.size_bytes() }; +#else + return span< const std17::byte, BytesExtent<T, Extent>::value >( + reinterpret_cast< std17::byte const * >( spn.data() ), spn.size_bytes() ); // NOLINT +#endif +} + +template< class T, extent_t Extent > +inline span_constexpr span< std17::byte, BytesExtent<T, Extent>::value > +as_writable_bytes( span<T,Extent> spn ) span_noexcept +{ +#if 0 + return { reinterpret_cast< std17::byte * >( spn.data() ), spn.size_bytes() }; +#else + return span< std17::byte, BytesExtent<T, Extent>::value >( + reinterpret_cast< std17::byte * >( spn.data() ), spn.size_bytes() ); // NOLINT +#endif +} + +#endif // span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE ) + +// 27.8 Container and view access [iterator.container] + +template< class T, extent_t Extent /*= dynamic_extent*/ > +span_constexpr std::size_t size( span<T,Extent> const & spn ) +{ + return static_cast<std::size_t>( spn.size() ); +} + +template< class T, extent_t Extent /*= dynamic_extent*/ > +span_constexpr std::ptrdiff_t ssize( span<T,Extent> const & spn ) +{ + return static_cast<std::ptrdiff_t>( spn.size() ); +} + +} // namespace span_lite +} // namespace nonstd + +// make available in nonstd: + +namespace nonstd { + +using span_lite::dynamic_extent; + +using span_lite::span; + +using span_lite::with_container; + +#if span_FEATURE( COMPARISON ) +#if span_FEATURE( SAME ) +using span_lite::same; +#endif + +using span_lite::operator==; +using span_lite::operator!=; +using span_lite::operator<; +using span_lite::operator<=; +using span_lite::operator>; +using span_lite::operator>=; +#endif + +#if span_HAVE( BYTE ) +using span_lite::as_bytes; +using span_lite::as_writable_bytes; +#endif + +using span_lite::size; +using span_lite::ssize; + +} // namespace nonstd + +#endif // span_USES_STD_SPAN + +// make_span() [span-lite extension]: + +#if span_FEATURE( MAKE_SPAN ) || span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) || span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) + +#if span_USES_STD_SPAN +# define span_constexpr constexpr +# define span_noexcept noexcept +# define span_nullptr nullptr +# ifndef span_CONFIG_EXTENT_TYPE +# define span_CONFIG_EXTENT_TYPE std::size_t +# endif +using extent_t = span_CONFIG_EXTENT_TYPE; +#endif // span_USES_STD_SPAN + +namespace nonstd { +namespace span_lite { + +template< class T > +inline span_constexpr span<T> +make_span( T * ptr, size_t count ) span_noexcept +{ + return span<T>( ptr, count ); +} + +template< class T > +inline span_constexpr span<T> +make_span( T * first, T * last ) span_noexcept +{ + return span<T>( first, last ); +} + +template< class T, std::size_t N > +inline span_constexpr span<T, static_cast<extent_t>(N)> +make_span( T ( &arr )[ N ] ) span_noexcept +{ + return span<T, static_cast<extent_t>(N)>( &arr[ 0 ], N ); +} + +#if span_USES_STD_SPAN || span_HAVE( ARRAY ) + +template< class T, std::size_t N > +inline span_constexpr span<T, static_cast<extent_t>(N)> +make_span( std::array< T, N > & arr ) span_noexcept +{ + return span<T, static_cast<extent_t>(N)>( arr ); +} + +template< class T, std::size_t N > +inline span_constexpr span< const T, static_cast<extent_t>(N) > +make_span( std::array< T, N > const & arr ) span_noexcept +{ + return span<const T, static_cast<extent_t>(N)>( arr ); +} + +#endif // span_HAVE( ARRAY ) + +#if span_USES_STD_SPAN || span_HAVE( INITIALIZER_LIST ) + +template< class T > +inline span_constexpr span< const T > +make_span( std::initializer_list<T> il ) span_noexcept +{ + return span<const T>( il.begin(), il.size() ); +} + +#endif // span_HAVE( INITIALIZER_LIST ) + +#if span_USES_STD_SPAN + +template< class Container, class EP = decltype( std::data(std::declval<Container&>())) > +inline span_constexpr auto +make_span( Container & cont ) span_noexcept -> span< typename std::remove_pointer<EP>::type > +{ + return span< typename std::remove_pointer<EP>::type >( cont ); +} + +template< class Container, class EP = decltype( std::data(std::declval<Container&>())) > +inline span_constexpr auto +make_span( Container const & cont ) span_noexcept -> span< const typename std::remove_pointer<EP>::type > +{ + return span< const typename std::remove_pointer<EP>::type >( cont ); +} + +#elif span_HAVE( CONSTRAINED_SPAN_CONTAINER_CTOR ) && span_HAVE( AUTO ) + +template< class Container, class EP = decltype( std17::data(std::declval<Container&>())) > +inline span_constexpr auto +make_span( Container & cont ) span_noexcept -> span< typename std::remove_pointer<EP>::type > +{ + return span< typename std::remove_pointer<EP>::type >( cont ); +} + +template< class Container, class EP = decltype( std17::data(std::declval<Container&>())) > +inline span_constexpr auto +make_span( Container const & cont ) span_noexcept -> span< const typename std::remove_pointer<EP>::type > +{ + return span< const typename std::remove_pointer<EP>::type >( cont ); +} + +#else + +template< class T > +inline span_constexpr span<T> +make_span( span<T> spn ) span_noexcept +{ + return spn; +} + +template< class T, class Allocator > +inline span_constexpr span<T> +make_span( std::vector<T, Allocator> & cont ) span_noexcept +{ + return span<T>( with_container, cont ); +} + +template< class T, class Allocator > +inline span_constexpr span<const T> +make_span( std::vector<T, Allocator> const & cont ) span_noexcept +{ + return span<const T>( with_container, cont ); +} + +#endif // span_USES_STD_SPAN || ( ... ) + +#if ! span_USES_STD_SPAN && span_FEATURE( WITH_CONTAINER ) + +template< class Container > +inline span_constexpr span<typename Container::value_type> +make_span( with_container_t, Container & cont ) span_noexcept +{ + return span< typename Container::value_type >( with_container, cont ); +} + +template< class Container > +inline span_constexpr span<const typename Container::value_type> +make_span( with_container_t, Container const & cont ) span_noexcept +{ + return span< const typename Container::value_type >( with_container, cont ); +} + +#endif // ! span_USES_STD_SPAN && span_FEATURE( WITH_CONTAINER ) + +// extensions: non-member views: +// this feature implies the presence of make_span() + +#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) + +template< extent_t Count, class T, extent_t Extent > +span_constexpr span<T, Count> +first( span<T, Extent> spn ) +{ + return spn.template first<Count>(); +} + +template< class T, extent_t Extent > +span_constexpr span<T> +first( span<T, Extent> spn, size_t count ) +{ + return spn.first( count ); +} + +template< extent_t Count, class T, extent_t Extent > +span_constexpr span<T, Count> +last( span<T, Extent> spn ) +{ + return spn.template last<Count>(); +} + +template< class T, extent_t Extent > +span_constexpr span<T> +last( span<T, Extent> spn, size_t count ) +{ + return spn.last( count ); +} + +template< size_t Offset, extent_t Count, class T, extent_t Extent > +span_constexpr span<T, Count> +subspan( span<T, Extent> spn ) +{ + return spn.template subspan<Offset, Count>(); +} + +template< class T, extent_t Extent > +span_constexpr span<T> +subspan( span<T, Extent> spn, size_t offset, extent_t count = dynamic_extent ) +{ + return spn.subspan( offset, count ); +} + +#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) + +#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) && span_CPP11_120 + +template< extent_t Count, class T > +span_constexpr auto +first( T & t ) -> decltype( make_span(t).template first<Count>() ) +{ + return make_span( t ).template first<Count>(); +} + +template< class T > +span_constexpr auto +first( T & t, size_t count ) -> decltype( make_span(t).first(count) ) +{ + return make_span( t ).first( count ); +} + +template< extent_t Count, class T > +span_constexpr auto +last( T & t ) -> decltype( make_span(t).template last<Count>() ) +{ + return make_span(t).template last<Count>(); +} + +template< class T > +span_constexpr auto +last( T & t, extent_t count ) -> decltype( make_span(t).last(count) ) +{ + return make_span( t ).last( count ); +} + +template< size_t Offset, extent_t Count = dynamic_extent, class T > +span_constexpr auto +subspan( T & t ) -> decltype( make_span(t).template subspan<Offset, Count>() ) +{ + return make_span( t ).template subspan<Offset, Count>(); +} + +template< class T > +span_constexpr auto +subspan( T & t, size_t offset, extent_t count = dynamic_extent ) -> decltype( make_span(t).subspan(offset, count) ) +{ + return make_span( t ).subspan( offset, count ); +} + +#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) + +} // namespace span_lite +} // namespace nonstd + +// make available in nonstd: + +namespace nonstd { +using span_lite::make_span; + +#if span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_SPAN ) || ( span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_CONTAINER ) && span_CPP11_120 ) + +using span_lite::first; +using span_lite::last; +using span_lite::subspan; + +#endif // span_FEATURE( NON_MEMBER_FIRST_LAST_SUB_[SPAN|CONTAINER] ) + +} // namespace nonstd + +#endif // #if span_FEATURE_TO_STD( MAKE_SPAN ) + +#if span_CPP11_OR_GREATER && span_FEATURE( BYTE_SPAN ) && ( span_HAVE( BYTE ) || span_HAVE( NONSTD_BYTE ) ) + +namespace nonstd { +namespace span_lite { + +template< class T > +inline span_constexpr auto +byte_span( T & t ) span_noexcept -> span< std17::byte, span_sizeof(T) > +{ + return span< std17::byte, span_sizeof(t) >( reinterpret_cast< std17::byte * >( &t ), span_sizeof(T) ); +} + +template< class T > +inline span_constexpr auto +byte_span( T const & t ) span_noexcept -> span< const std17::byte, span_sizeof(T) > +{ + return span< const std17::byte, span_sizeof(t) >( reinterpret_cast< std17::byte const * >( &t ), span_sizeof(T) ); +} + +} // namespace span_lite +} // namespace nonstd + +// make available in nonstd: + +namespace nonstd { +using span_lite::byte_span; +} // namespace nonstd + +#endif // span_FEATURE( BYTE_SPAN ) + +#if !span_USES_STD_SPAN && span_HAVE( STRUCT_BINDING ) + +#if span_CPP14_OR_GREATER +# include <tuple> +#elif span_CPP11_OR_GREATER +# include <tuple> +namespace std { + template< std::size_t I, typename T > + using tuple_element_t = typename tuple_element<I, T>::type; +} +#else +namespace std { + template< typename T > + class tuple_size; /*undefined*/ + + template< std::size_t I, typename T > + class tuple_element; /* undefined */ +} +#endif // span_CPP14_OR_GREATER + +namespace std { + +// 26.7.X Tuple interface + +// std::tuple_size<>: + +template< typename ElementType, nonstd::span_lite::extent_t Extent > +class tuple_size< nonstd::span<ElementType, Extent> > : public integral_constant<size_t, static_cast<size_t>(Extent)> {}; + +// std::tuple_size<>: Leave undefined for dynamic extent: + +template< typename ElementType > +class tuple_size< nonstd::span<ElementType, nonstd::dynamic_extent> >; + +// std::tuple_element<>: + +template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent > +class tuple_element< I, nonstd::span<ElementType, Extent> > +{ +public: +#if span_HAVE( STATIC_ASSERT ) + static_assert( Extent != nonstd::dynamic_extent && I < Extent, "tuple_element<I,span>: dynamic extent or index out of range" ); +#endif + using type = ElementType; +}; + +// std::get<>(), 2 variants: + +template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent > +span_constexpr ElementType & get( nonstd::span<ElementType, Extent> & spn ) span_noexcept +{ +#if span_HAVE( STATIC_ASSERT ) + static_assert( Extent != nonstd::dynamic_extent && I < Extent, "get<>(span): dynamic extent or index out of range" ); +#endif + return spn[I]; +} + +template< size_t I, typename ElementType, nonstd::span_lite::extent_t Extent > +span_constexpr ElementType const & get( nonstd::span<ElementType, Extent> const & spn ) span_noexcept +{ +#if span_HAVE( STATIC_ASSERT ) + static_assert( Extent != nonstd::dynamic_extent && I < Extent, "get<>(span): dynamic extent or index out of range" ); +#endif + return spn[I]; +} + +} // end namespace std + +#endif // !span_USES_STD_SPAN && span_HAVE( STRUCT_BINDING ) + +#if ! span_USES_STD_SPAN +span_RESTORE_WARNINGS() +#endif // span_USES_STD_SPAN + +#endif // NONSTD_SPAN_HPP_INCLUDED diff --git a/src/shared/variant/variant.qbs b/src/shared/span/span.qbs index 3a95553b7..321583ef5 100644 --- a/src/shared/variant/variant.qbs +++ b/src/shared/span/span.qbs @@ -1,10 +1,9 @@ Product { - name: "qbsvariant" + name: "span" files: [ - "LICENSE.md", + "LICENSE_1_0.txt", "README.md", - "variant.h", - "variant.hpp" + "span.hpp", ] Export { Depends { name: "cpp" } diff --git a/src/shared/variant/CMakeLists.txt b/src/shared/variant/CMakeLists.txt deleted file mode 100644 index ac73231d4..000000000 --- a/src/shared/variant/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -add_library(qbsvariant INTERFACE) -target_include_directories( - qbsvariant - INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/>) diff --git a/src/shared/variant/README.md b/src/shared/variant/README.md deleted file mode 100644 index 6644286d2..000000000 --- a/src/shared/variant/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# MPark.Variant - -> __C++17__ `std::variant` for __C++11__/__14__/__17__ - -[![release][badge.release]][release] -[![header][badge.header]][header] -[![travis][badge.travis]][travis] -[![appveyor][badge.appveyor]][appveyor] -[![license][badge.license]][license] -[![godbolt][badge.godbolt]][godbolt] -[![wandbox][badge.wandbox]][wandbox] - -[badge.release]: https://img.shields.io/github/release/mpark/variant.svg -[badge.header]: https://img.shields.io/badge/single%20header-master-blue.svg -[badge.travis]: https://travis-ci.org/mpark/variant.svg?branch=master -[badge.appveyor]: https://ci.appveyor.com/api/projects/status/github/mpark/variant?branch=master&svg=true -[badge.license]: https://img.shields.io/badge/license-boost-blue.svg -[badge.godbolt]: https://img.shields.io/badge/try%20it-on%20godbolt-222266.svg -[badge.wandbox]: https://img.shields.io/badge/try%20it-on%20wandbox-5cb85c.svg - -[release]: https://github.com/mpark/variant/releases/latest -[header]: https://github.com/mpark/variant/blob/single-header/master/variant.hpp -[travis]: https://travis-ci.org/mpark/variant -[appveyor]: https://ci.appveyor.com/project/mpark/variant -[license]: https://github.com/mpark/variant/blob/master/LICENSE.md -[godbolt]: https://godbolt.org/g/1qYDAK -[wandbox]: https://wandbox.org/permlink/QV3gZ2KQQNwgoFIB - -## Single Header - -This branch provides a standalone `variant.hpp` file for each -[release](https://github.com/mpark/variant/releases). -Copy it and `#include` away! - -## License - -Distributed under the [Boost Software License, Version 1.0](LICENSE.md). diff --git a/src/shared/variant/variant.h b/src/shared/variant/variant.h deleted file mode 100644 index ecaf81ca6..000000000 --- a/src/shared/variant/variant.h +++ /dev/null @@ -1,63 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qbs. -** -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -****************************************************************************/ - -#pragma once - -/* - See std(::experimental)::variant. -*/ - -// std::variant from Apple's Clang supports methods that throw std::bad_optional_access only -// with deployment target >= macOS 10.14 -// TODO: Use std::variant everywhere when we can require macOS 10.14 -#if !defined(__apple_build_version__) -#include <variant> - -namespace qbs { -namespace Variant { -using std::get; -using std::get_if; -using std::holds_alternative; -using std::variant; -using std::variant_alternative_t; -using std::visit; -} // namespace Variant -} // namespace qbs - -#else -#include "variant.hpp" - -namespace qbs { -namespace Variant { -using mpark::get; -using mpark::get_if; -using mpark::holds_alternative; -using mpark::variant; -using mpark::variant_alternative_t; -using mpark::visit; -} // namespace Variant -} // namespace qbs - -#endif diff --git a/src/shared/variant/variant.hpp b/src/shared/variant/variant.hpp deleted file mode 100644 index dca26986c..000000000 --- a/src/shared/variant/variant.hpp +++ /dev/null @@ -1,2465 +0,0 @@ -// MPark.Variant -// -// Copyright Michael Park, 2015-2017 -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) - -#ifndef MPARK_VARIANT_HPP -#define MPARK_VARIANT_HPP - -#if defined(__GNUC__) && __GNUC__ >= 9 -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-copy" -#endif - -/* - variant synopsis - -namespace std { - - // 20.7.2, class template variant - template <class... Types> - class variant { - public: - - // 20.7.2.1, constructors - constexpr variant() noexcept(see below); - variant(const variant&); - variant(variant&&) noexcept(see below); - - template <class T> constexpr variant(T&&) noexcept(see below); - - template <class T, class... Args> - constexpr explicit variant(in_place_type_t<T>, Args&&...); - - template <class T, class U, class... Args> - constexpr explicit variant( - in_place_type_t<T>, initializer_list<U>, Args&&...); - - template <size_t I, class... Args> - constexpr explicit variant(in_place_index_t<I>, Args&&...); - - template <size_t I, class U, class... Args> - constexpr explicit variant( - in_place_index_t<I>, initializer_list<U>, Args&&...); - - // 20.7.2.2, destructor - ~variant(); - - // 20.7.2.3, assignment - variant& operator=(const variant&); - variant& operator=(variant&&) noexcept(see below); - - template <class T> variant& operator=(T&&) noexcept(see below); - - // 20.7.2.4, modifiers - template <class T, class... Args> - T& emplace(Args&&...); - - template <class T, class U, class... Args> - T& emplace(initializer_list<U>, Args&&...); - - template <size_t I, class... Args> - variant_alternative<I, variant>& emplace(Args&&...); - - template <size_t I, class U, class... Args> - variant_alternative<I, variant>& emplace(initializer_list<U>, Args&&...); - - // 20.7.2.5, value status - constexpr bool valueless_by_exception() const noexcept; - constexpr size_t index() const noexcept; - - // 20.7.2.6, swap - void swap(variant&) noexcept(see below); - }; - - // 20.7.3, variant helper classes - template <class T> struct variant_size; // undefined - - template <class T> - constexpr size_t variant_size_v = variant_size<T>::value; - - template <class T> struct variant_size<const T>; - template <class T> struct variant_size<volatile T>; - template <class T> struct variant_size<const volatile T>; - - template <class... Types> - struct variant_size<variant<Types...>>; - - template <size_t I, class T> struct variant_alternative; // undefined - - template <size_t I, class T> - using variant_alternative_t = typename variant_alternative<I, T>::type; - - template <size_t I, class T> struct variant_alternative<I, const T>; - template <size_t I, class T> struct variant_alternative<I, volatile T>; - template <size_t I, class T> struct variant_alternative<I, const volatile T>; - - template <size_t I, class... Types> - struct variant_alternative<I, variant<Types...>>; - - constexpr size_t variant_npos = -1; - - // 20.7.4, value access - template <class T, class... Types> - constexpr bool holds_alternative(const variant<Types...>&) noexcept; - - template <size_t I, class... Types> - constexpr variant_alternative_t<I, variant<Types...>>& - get(variant<Types...>&); - - template <size_t I, class... Types> - constexpr variant_alternative_t<I, variant<Types...>>&& - get(variant<Types...>&&); - - template <size_t I, class... Types> - constexpr variant_alternative_t<I, variant<Types...>> const& - get(const variant<Types...>&); - - template <size_t I, class... Types> - constexpr variant_alternative_t<I, variant<Types...>> const&& - get(const variant<Types...>&&); - - template <class T, class... Types> - constexpr T& get(variant<Types...>&); - - template <class T, class... Types> - constexpr T&& get(variant<Types...>&&); - - template <class T, class... Types> - constexpr const T& get(const variant<Types...>&); - - template <class T, class... Types> - constexpr const T&& get(const variant<Types...>&&); - - template <size_t I, class... Types> - constexpr add_pointer_t<variant_alternative_t<I, variant<Types...>>> - get_if(variant<Types...>*) noexcept; - - template <size_t I, class... Types> - constexpr add_pointer_t<const variant_alternative_t<I, variant<Types...>>> - get_if(const variant<Types...>*) noexcept; - - template <class T, class... Types> - constexpr add_pointer_t<T> - get_if(variant<Types...>*) noexcept; - - template <class T, class... Types> - constexpr add_pointer_t<const T> - get_if(const variant<Types...>*) noexcept; - - // 20.7.5, relational operators - template <class... Types> - constexpr bool operator==(const variant<Types...>&, const variant<Types...>&); - - template <class... Types> - constexpr bool operator!=(const variant<Types...>&, const variant<Types...>&); - - template <class... Types> - constexpr bool operator<(const variant<Types...>&, const variant<Types...>&); - - template <class... Types> - constexpr bool operator>(const variant<Types...>&, const variant<Types...>&); - - template <class... Types> - constexpr bool operator<=(const variant<Types...>&, const variant<Types...>&); - - template <class... Types> - constexpr bool operator>=(const variant<Types...>&, const variant<Types...>&); - - // 20.7.6, visitation - template <class Visitor, class... Variants> - constexpr see below visit(Visitor&&, Variants&&...); - - // 20.7.7, class monostate - struct monostate; - - // 20.7.8, monostate relational operators - constexpr bool operator<(monostate, monostate) noexcept; - constexpr bool operator>(monostate, monostate) noexcept; - constexpr bool operator<=(monostate, monostate) noexcept; - constexpr bool operator>=(monostate, monostate) noexcept; - constexpr bool operator==(monostate, monostate) noexcept; - constexpr bool operator!=(monostate, monostate) noexcept; - - // 20.7.9, specialized algorithms - template <class... Types> - void swap(variant<Types...>&, variant<Types...>&) noexcept(see below); - - // 20.7.10, class bad_variant_access - class bad_variant_access; - - // 20.7.11, hash support - template <class T> struct hash; - template <class... Types> struct hash<variant<Types...>>; - template <> struct hash<monostate>; - -} // namespace std - -*/ - -#include <cstddef> -#include <exception> -#include <functional> -#include <initializer_list> -#include <new> -#include <type_traits> -#include <utility> - -// MPark.Variant -// -// Copyright Michael Park, 2015-2017 -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) - -#ifndef MPARK_CONFIG_HPP -#define MPARK_CONFIG_HPP - -// MSVC 2015 Update 3. -#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_FULL_VER < 190024210) -#error "MPark.Variant requires C++11 support." -#endif - -#ifndef __has_builtin -#define __has_builtin(x) 0 -#endif - -#ifndef __has_include -#define __has_include(x) 0 -#endif - -#ifndef __has_feature -#define __has_feature(x) 0 -#endif - -#if __has_builtin(__builtin_addressof) || \ - (defined(__GNUC__) && __GNUC__ >= 7) || defined(_MSC_VER) -#define MPARK_BUILTIN_ADDRESSOF -#endif - -#if __has_builtin(__builtin_unreachable) -#define MPARK_BUILTIN_UNREACHABLE -#endif - -#if __has_builtin(__type_pack_element) -#define MPARK_TYPE_PACK_ELEMENT -#endif - -#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304 -#if !defined(_MSC_VER) || _MSC_VER < 1915 // compile issue in msvc 2017 update 8 -#define MPARK_CPP14_CONSTEXPR -#endif -#endif - -#if __has_feature(cxx_exceptions) || defined(__cpp_exceptions) || \ - (defined(_MSC_VER) && defined(_CPPUNWIND)) -#define MPARK_EXCEPTIONS -#endif - -#if defined(__cpp_generic_lambdas) || defined(_MSC_VER) -#define MPARK_GENERIC_LAMBDAS -#endif - -#if defined(__cpp_lib_integer_sequence) -#define MPARK_INTEGER_SEQUENCE -#endif - -#if defined(__cpp_return_type_deduction) || defined(_MSC_VER) -#define MPARK_RETURN_TYPE_DEDUCTION -#endif - -#if defined(__cpp_lib_transparent_operators) || defined(_MSC_VER) -#define MPARK_TRANSPARENT_OPERATORS -#endif - -#if defined(__cpp_variable_templates) || defined(_MSC_VER) -#define MPARK_VARIABLE_TEMPLATES -#endif - -#if !defined(__GLIBCXX__) || __has_include(<codecvt>) // >= libstdc++-5 -#define MPARK_TRIVIALITY_TYPE_TRAITS -#endif - -#endif // MPARK_CONFIG_HPP - -// MPark.Variant -// -// Copyright Michael Park, 2015-2017 -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) - -#ifndef MPARK_IN_PLACE_HPP -#define MPARK_IN_PLACE_HPP - -#include <cstddef> - - -namespace mpark { - - struct in_place_t { explicit in_place_t() = default; }; - - template <std::size_t I> - struct in_place_index_t { explicit in_place_index_t() = default; }; - - template <typename T> - struct in_place_type_t { explicit in_place_type_t() = default; }; - -#ifdef MPARK_VARIABLE_TEMPLATES - constexpr in_place_t in_place{}; - - template <std::size_t I> constexpr in_place_index_t<I> in_place_index{}; - - template <typename T> constexpr in_place_type_t<T> in_place_type{}; -#endif - -} // namespace mpark - -#endif // MPARK_IN_PLACE_HPP - -// MPark.Variant -// -// Copyright Michael Park, 2015-2017 -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) - -#ifndef MPARK_LIB_HPP -#define MPARK_LIB_HPP - -#include <memory> -#include <functional> -#include <type_traits> -#include <utility> - - -#define RETURN(...) \ - noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) { \ - return __VA_ARGS__; \ - } - -namespace mpark { - namespace lib { - template <typename T> - struct identity { using type = T; }; - - inline namespace cpp14 { - template <typename T, std::size_t N> - struct array { - constexpr const T &operator[](std::size_t index) const { - return data[index]; - } - - T data[N == 0 ? 1 : N]; - }; - - template <typename T> - using add_pointer_t = typename std::add_pointer<T>::type; - - template <typename... Ts> - using common_type_t = typename std::common_type<Ts...>::type; - - template <typename T> - using decay_t = typename std::decay<T>::type; - - template <bool B, typename T = void> - using enable_if_t = typename std::enable_if<B, T>::type; - - template <typename T> - using remove_const_t = typename std::remove_const<T>::type; - - template <typename T> - using remove_reference_t = typename std::remove_reference<T>::type; - - template <typename T> - inline constexpr T &&forward(remove_reference_t<T> &t) noexcept { - return static_cast<T &&>(t); - } - - template <typename T> - inline constexpr T &&forward(remove_reference_t<T> &&t) noexcept { - static_assert(!std::is_lvalue_reference<T>::value, - "can not forward an rvalue as an lvalue"); - return static_cast<T &&>(t); - } - - template <typename T> - inline constexpr remove_reference_t<T> &&move(T &&t) noexcept { - return static_cast<remove_reference_t<T> &&>(t); - } - -#ifdef MPARK_INTEGER_SEQUENCE - using std::integer_sequence; - using std::index_sequence; - using std::make_index_sequence; - using std::index_sequence_for; -#else - template <typename T, T... Is> - struct integer_sequence { - using value_type = T; - static constexpr std::size_t size() noexcept { return sizeof...(Is); } - }; - - template <std::size_t... Is> - using index_sequence = integer_sequence<std::size_t, Is...>; - - template <typename Lhs, typename Rhs> - struct make_index_sequence_concat; - - template <std::size_t... Lhs, std::size_t... Rhs> - struct make_index_sequence_concat<index_sequence<Lhs...>, - index_sequence<Rhs...>> - : identity<index_sequence<Lhs..., (sizeof...(Lhs) + Rhs)...>> {}; - - template <std::size_t N> - struct make_index_sequence_impl; - - template <std::size_t N> - using make_index_sequence = typename make_index_sequence_impl<N>::type; - - template <std::size_t N> - struct make_index_sequence_impl - : make_index_sequence_concat<make_index_sequence<N / 2>, - make_index_sequence<N - (N / 2)>> {}; - - template <> - struct make_index_sequence_impl<0> : identity<index_sequence<>> {}; - - template <> - struct make_index_sequence_impl<1> : identity<index_sequence<0>> {}; - - template <typename... Ts> - using index_sequence_for = make_index_sequence<sizeof...(Ts)>; -#endif - - // <functional> -#ifdef MPARK_TRANSPARENT_OPERATORS - using equal_to = std::equal_to<>; -#else - struct equal_to { - template <typename Lhs, typename Rhs> - inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const - RETURN(lib::forward<Lhs>(lhs) == lib::forward<Rhs>(rhs)) - }; -#endif - -#ifdef MPARK_TRANSPARENT_OPERATORS - using not_equal_to = std::not_equal_to<>; -#else - struct not_equal_to { - template <typename Lhs, typename Rhs> - inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const - RETURN(lib::forward<Lhs>(lhs) != lib::forward<Rhs>(rhs)) - }; -#endif - -#ifdef MPARK_TRANSPARENT_OPERATORS - using less = std::less<>; -#else - struct less { - template <typename Lhs, typename Rhs> - inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const - RETURN(lib::forward<Lhs>(lhs) < lib::forward<Rhs>(rhs)) - }; -#endif - -#ifdef MPARK_TRANSPARENT_OPERATORS - using greater = std::greater<>; -#else - struct greater { - template <typename Lhs, typename Rhs> - inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const - RETURN(lib::forward<Lhs>(lhs) > lib::forward<Rhs>(rhs)) - }; -#endif - -#ifdef MPARK_TRANSPARENT_OPERATORS - using less_equal = std::less_equal<>; -#else - struct less_equal { - template <typename Lhs, typename Rhs> - inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const - RETURN(lib::forward<Lhs>(lhs) <= lib::forward<Rhs>(rhs)) - }; -#endif - -#ifdef MPARK_TRANSPARENT_OPERATORS - using greater_equal = std::greater_equal<>; -#else - struct greater_equal { - template <typename Lhs, typename Rhs> - inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const - RETURN(lib::forward<Lhs>(lhs) >= lib::forward<Rhs>(rhs)) - }; -#endif - } // namespace cpp14 - - inline namespace cpp17 { - - // <type_traits> - template <bool B> - using bool_constant = std::integral_constant<bool, B>; - - template <typename...> - struct voider : identity<void> {}; - - template <typename... Ts> - using void_t = typename voider<Ts...>::type; - - namespace detail { - namespace swappable { - - using std::swap; - - template <typename T> - struct is_swappable { - private: - template <typename U, - typename = decltype(swap(std::declval<U &>(), - std::declval<U &>()))> - inline static std::true_type test(int); - - template <typename U> - inline static std::false_type test(...); - - public: - static constexpr bool value = decltype(test<T>(0))::value; - }; - - template <typename T, bool = is_swappable<T>::value> - struct is_nothrow_swappable { - static constexpr bool value = - noexcept(swap(std::declval<T &>(), std::declval<T &>())); - }; - - template <typename T> - struct is_nothrow_swappable<T, false> : std::false_type {}; - - } // namespace swappable - } // namespace detail - - using detail::swappable::is_swappable; - using detail::swappable::is_nothrow_swappable; - - // <functional> -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4100) -#endif - template <typename F, typename... As> - inline constexpr auto invoke(F &&f, As &&... as) - RETURN(lib::forward<F>(f)(lib::forward<As>(as)...)) -#ifdef _MSC_VER -#pragma warning(pop) -#endif - - template <typename B, typename T, typename D> - inline constexpr auto invoke(T B::*pmv, D &&d) - RETURN(lib::forward<D>(d).*pmv) - - template <typename Pmv, typename Ptr> - inline constexpr auto invoke(Pmv pmv, Ptr &&ptr) - RETURN((*lib::forward<Ptr>(ptr)).*pmv) - - template <typename B, typename T, typename D, typename... As> - inline constexpr auto invoke(T B::*pmf, D &&d, As &&... as) - RETURN((lib::forward<D>(d).*pmf)(lib::forward<As>(as)...)) - - template <typename Pmf, typename Ptr, typename... As> - inline constexpr auto invoke(Pmf pmf, Ptr &&ptr, As &&... as) - RETURN(((*lib::forward<Ptr>(ptr)).*pmf)(lib::forward<As>(as)...)) - - namespace detail { - - template <typename Void, typename, typename...> - struct invoke_result {}; - - template <typename F, typename... Args> - struct invoke_result<void_t<decltype(lib::invoke( - std::declval<F>(), std::declval<Args>()...))>, - F, - Args...> - : identity<decltype( - lib::invoke(std::declval<F>(), std::declval<Args>()...))> {}; - - } // namespace detail - - template <typename F, typename... Args> - using invoke_result = detail::invoke_result<void, F, Args...>; - - template <typename F, typename... Args> - using invoke_result_t = typename invoke_result<F, Args...>::type; - - namespace detail { - - template <typename Void, typename, typename...> - struct is_invocable : std::false_type {}; - - template <typename F, typename... Args> - struct is_invocable<void_t<invoke_result_t<F, Args...>>, F, Args...> - : std::true_type {}; - - template <typename Void, typename, typename, typename...> - struct is_invocable_r : std::false_type {}; - - template <typename R, typename F, typename... Args> - struct is_invocable_r<void_t<invoke_result_t<F, Args...>>, - R, - F, - Args...> - : std::is_convertible<invoke_result_t<F, Args...>, R> {}; - - } // namespace detail - - template <typename F, typename... Args> - using is_invocable = detail::is_invocable<void, F, Args...>; - - template <typename R, typename F, typename... Args> - using is_invocable_r = detail::is_invocable_r<void, R, F, Args...>; - - // <memory> -#ifdef MPARK_BUILTIN_ADDRESSOF - template <typename T> - inline constexpr T *addressof(T &arg) { - return __builtin_addressof(arg); - } -#else - namespace detail { - - namespace has_addressof_impl { - - struct fail; - - template <typename T> - inline fail operator&(T &&); - - template <typename T> - inline static constexpr bool impl() { - return (std::is_class<T>::value || std::is_union<T>::value) && - !std::is_same<decltype(&std::declval<T &>()), fail>::value; - } - - } // namespace has_addressof_impl - - template <typename T> - using has_addressof = bool_constant<has_addressof_impl::impl<T>()>; - - template <typename T> - inline constexpr T *addressof(T &arg, std::true_type) { - return std::addressof(arg); - } - - template <typename T> - inline constexpr T *addressof(T &arg, std::false_type) { - return &arg; - } - - } // namespace detail - - template <typename T> - inline constexpr T *addressof(T &arg) { - return detail::addressof(arg, detail::has_addressof<T>{}); - } -#endif - - template <typename T> - inline constexpr T *addressof(const T &&) = delete; - - } // namespace cpp17 - - template <typename T> - struct remove_all_extents : identity<T> {}; - - template <typename T, std::size_t N> - struct remove_all_extents<array<T, N>> : remove_all_extents<T> {}; - - template <typename T> - using remove_all_extents_t = typename remove_all_extents<T>::type; - - template <std::size_t N> - using size_constant = std::integral_constant<std::size_t, N>; - - template <std::size_t I, typename T> - struct indexed_type : size_constant<I>, identity<T> {}; - - template <bool... Bs> - using all = std::is_same<integer_sequence<bool, true, Bs...>, - integer_sequence<bool, Bs..., true>>; - -#ifdef MPARK_TYPE_PACK_ELEMENT - template <std::size_t I, typename... Ts> - using type_pack_element_t = __type_pack_element<I, Ts...>; -#else - template <std::size_t I, typename... Ts> - struct type_pack_element_impl { - private: - template <typename> - struct set; - - template <std::size_t... Is> - struct set<index_sequence<Is...>> : indexed_type<Is, Ts>... {}; - - template <typename T> - inline static std::enable_if<true, T> impl(indexed_type<I, T>); - - inline static std::enable_if<false> impl(...); - - public: - using type = decltype(impl(set<index_sequence_for<Ts...>>{})); - }; - - template <std::size_t I, typename... Ts> - using type_pack_element = typename type_pack_element_impl<I, Ts...>::type; - - template <std::size_t I, typename... Ts> - using type_pack_element_t = typename type_pack_element<I, Ts...>::type; -#endif - -#ifdef MPARK_TRIVIALITY_TYPE_TRAITS - using std::is_trivially_copy_constructible; - using std::is_trivially_move_constructible; - using std::is_trivially_copy_assignable; - using std::is_trivially_move_assignable; -#else - template <typename T> - struct is_trivially_copy_constructible - : bool_constant< - std::is_copy_constructible<T>::value && __has_trivial_copy(T)> {}; - - template <typename T> - struct is_trivially_move_constructible : bool_constant<__is_trivial(T)> {}; - - template <typename T> - struct is_trivially_copy_assignable - : bool_constant< - std::is_copy_assignable<T>::value && __has_trivial_assign(T)> {}; - - template <typename T> - struct is_trivially_move_assignable : bool_constant<__is_trivial(T)> {}; -#endif - - template <typename T, bool> - struct dependent_type : T {}; - - template <typename Is, std::size_t J> - struct push_back; - - template <typename Is, std::size_t J> - using push_back_t = typename push_back<Is, J>::type; - - template <std::size_t... Is, std::size_t J> - struct push_back<index_sequence<Is...>, J> { - using type = index_sequence<Is..., J>; - }; - - } // namespace lib -} // namespace mpark - -#undef RETURN - -#endif // MPARK_LIB_HPP - - -namespace mpark { - -#ifdef MPARK_RETURN_TYPE_DEDUCTION - -#define AUTO auto -#define AUTO_RETURN(...) { return __VA_ARGS__; } - -#define AUTO_REFREF auto && -#define AUTO_REFREF_RETURN(...) { return __VA_ARGS__; } - -#define DECLTYPE_AUTO decltype(auto) -#define DECLTYPE_AUTO_RETURN(...) { return __VA_ARGS__; } - -#else - -#define AUTO auto -#define AUTO_RETURN(...) \ - -> lib::decay_t<decltype(__VA_ARGS__)> { return __VA_ARGS__; } - -#define AUTO_REFREF auto -#define AUTO_REFREF_RETURN(...) \ - -> decltype((__VA_ARGS__)) { \ - static_assert(std::is_reference<decltype((__VA_ARGS__))>::value, ""); \ - return __VA_ARGS__; \ - } - -#define DECLTYPE_AUTO auto -#define DECLTYPE_AUTO_RETURN(...) \ - -> decltype(__VA_ARGS__) { return __VA_ARGS__; } - -#endif - - class bad_variant_access : public std::exception { - public: - virtual const char *what() const noexcept { return "bad_variant_access"; } - }; - - [[noreturn]] inline void throw_bad_variant_access() { -#ifdef MPARK_EXCEPTIONS - throw bad_variant_access{}; -#else - std::terminate(); -#ifdef MPARK_BUILTIN_UNREACHABLE - __builtin_unreachable(); -#endif -#endif - } - - template <typename... Ts> - class variant; - - template <typename T> - struct variant_size; - -#ifdef MPARK_VARIABLE_TEMPLATES - template <typename T> - constexpr std::size_t variant_size_v = variant_size<T>::value; -#endif - - template <typename T> - struct variant_size<const T> : variant_size<T> {}; - - template <typename T> - struct variant_size<volatile T> : variant_size<T> {}; - - template <typename T> - struct variant_size<const volatile T> : variant_size<T> {}; - - template <typename... Ts> - struct variant_size<variant<Ts...>> : lib::size_constant<sizeof...(Ts)> {}; - - template <std::size_t I, typename T> - struct variant_alternative; - - template <std::size_t I, typename T> - using variant_alternative_t = typename variant_alternative<I, T>::type; - - template <std::size_t I, typename T> - struct variant_alternative<I, const T> - : std::add_const<variant_alternative_t<I, T>> {}; - - template <std::size_t I, typename T> - struct variant_alternative<I, volatile T> - : std::add_volatile<variant_alternative_t<I, T>> {}; - - template <std::size_t I, typename T> - struct variant_alternative<I, const volatile T> - : std::add_cv<variant_alternative_t<I, T>> {}; - - template <std::size_t I, typename... Ts> - struct variant_alternative<I, variant<Ts...>> { - static_assert(I < sizeof...(Ts), - "Index out of bounds in std::variant_alternative<>"); - using type = lib::type_pack_element_t<I, Ts...>; - }; - - constexpr std::size_t variant_npos = static_cast<std::size_t>(-1); - - namespace detail { - - constexpr std::size_t not_found = static_cast<std::size_t>(-1); - constexpr std::size_t ambiguous = static_cast<std::size_t>(-2); - -#ifdef MPARK_CPP14_CONSTEXPR - template <typename T, typename... Ts> - inline constexpr std::size_t find_index() { - constexpr lib::array<bool, sizeof...(Ts)> matches = { - {std::is_same<T, Ts>::value...} - }; - std::size_t result = not_found; - for (std::size_t i = 0; i < sizeof...(Ts); ++i) { - if (matches[i]) { - if (result != not_found) { - return ambiguous; - } - result = i; - } - } - return result; - } -#else - inline constexpr std::size_t find_index_impl(std::size_t result, - std::size_t) { - return result; - } - - template <typename... Bs> - inline constexpr std::size_t find_index_impl(std::size_t result, - std::size_t idx, - bool b, - Bs... bs) { - return b ? (result != not_found ? ambiguous - : find_index_impl(idx, idx + 1, bs...)) - : find_index_impl(result, idx + 1, bs...); - } - - template <typename T, typename... Ts> - inline constexpr std::size_t find_index() { - return find_index_impl(not_found, 0, std::is_same<T, Ts>::value...); - } -#endif - - template <std::size_t I> - using find_index_sfinae_impl = - lib::enable_if_t<I != not_found && I != ambiguous, - lib::size_constant<I>>; - - template <typename T, typename... Ts> - using find_index_sfinae = find_index_sfinae_impl<find_index<T, Ts...>()>; - - template <std::size_t I> - struct find_index_checked_impl : lib::size_constant<I> { - static_assert(I != not_found, "the specified type is not found."); - static_assert(I != ambiguous, "the specified type is ambiguous."); - }; - - template <typename T, typename... Ts> - using find_index_checked = find_index_checked_impl<find_index<T, Ts...>()>; - - struct valueless_t {}; - - enum class Trait { TriviallyAvailable, Available, Unavailable }; - - template <typename T, - template <typename> class IsTriviallyAvailable, - template <typename> class IsAvailable> - inline constexpr Trait trait() { - return IsTriviallyAvailable<T>::value - ? Trait::TriviallyAvailable - : IsAvailable<T>::value ? Trait::Available - : Trait::Unavailable; - } - -#ifdef MPARK_CPP14_CONSTEXPR - template <typename... Traits> - inline constexpr Trait common_trait(Traits... traits) { - Trait result = Trait::TriviallyAvailable; - for (Trait t : {traits...}) { - if (static_cast<int>(t) > static_cast<int>(result)) { - result = t; - } - } - return result; - } -#else - inline constexpr Trait common_trait_impl(Trait result) { return result; } - - template <typename... Traits> - inline constexpr Trait common_trait_impl(Trait result, - Trait t, - Traits... ts) { - return static_cast<int>(t) > static_cast<int>(result) - ? common_trait_impl(t, ts...) - : common_trait_impl(result, ts...); - } - - template <typename... Traits> - inline constexpr Trait common_trait(Traits... ts) { - return common_trait_impl(Trait::TriviallyAvailable, ts...); - } -#endif - - template <typename... Ts> - struct traits { - static constexpr Trait copy_constructible_trait = - common_trait(trait<Ts, - lib::is_trivially_copy_constructible, - std::is_copy_constructible>()...); - - static constexpr Trait move_constructible_trait = - common_trait(trait<Ts, - lib::is_trivially_move_constructible, - std::is_move_constructible>()...); - - static constexpr Trait copy_assignable_trait = - common_trait(copy_constructible_trait, - trait<Ts, - lib::is_trivially_copy_assignable, - std::is_copy_assignable>()...); - - static constexpr Trait move_assignable_trait = - common_trait(move_constructible_trait, - trait<Ts, - lib::is_trivially_move_assignable, - std::is_move_assignable>()...); - - static constexpr Trait destructible_trait = - common_trait(trait<Ts, - std::is_trivially_destructible, - std::is_destructible>()...); - }; - - namespace access { - - struct recursive_union { -#ifdef MPARK_RETURN_TYPE_DEDUCTION - template <typename V> - inline static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) { - return lib::forward<V>(v).head_; - } - - template <typename V, std::size_t I> - inline static constexpr auto &&get_alt(V &&v, in_place_index_t<I>) { - return get_alt(lib::forward<V>(v).tail_, in_place_index_t<I - 1>{}); - } -#else - template <std::size_t I, bool Dummy = true> - struct get_alt_impl { - template <typename V> - inline constexpr AUTO_REFREF operator()(V &&v) const - AUTO_REFREF_RETURN(get_alt_impl<I - 1>{}(lib::forward<V>(v).tail_)) - }; - - template <bool Dummy> - struct get_alt_impl<0, Dummy> { - template <typename V> - inline constexpr AUTO_REFREF operator()(V &&v) const - AUTO_REFREF_RETURN(lib::forward<V>(v).head_) - }; - - template <typename V, std::size_t I> - inline static constexpr AUTO_REFREF get_alt(V &&v, in_place_index_t<I>) - AUTO_REFREF_RETURN(get_alt_impl<I>{}(lib::forward<V>(v))) -#endif - }; - - struct base { - template <std::size_t I, typename V> - inline static constexpr AUTO_REFREF get_alt(V &&v) - AUTO_REFREF_RETURN(recursive_union::get_alt( - data(lib::forward<V>(v)), in_place_index_t<I>{})) - }; - - struct variant { - template <std::size_t I, typename V> - inline static constexpr AUTO_REFREF get_alt(V &&v) - AUTO_REFREF_RETURN(base::get_alt<I>(lib::forward<V>(v).impl_)) - }; - - } // namespace access - - namespace visitation { - - struct base { - template <typename T> - inline static constexpr const T &at(const T &elem) { - return elem; - } - - template <typename T, std::size_t N, typename... Is> - inline static constexpr const lib::remove_all_extents_t<T> &at( - const lib::array<T, N> &elems, std::size_t i, Is... is) { - return at(elems[i], is...); - } - - template <typename F, typename... Fs> - inline static constexpr int visit_visitor_return_type_check() { - static_assert(lib::all<std::is_same<F, Fs>::value...>::value, - "`mpark::visit` requires the visitor to have a single " - "return type."); - return 0; - } - - template <typename... Fs> - inline static constexpr lib::array< - lib::common_type_t<lib::decay_t<Fs>...>, - sizeof...(Fs)> - make_farray(Fs &&... fs) { - using result = lib::array<lib::common_type_t<lib::decay_t<Fs>...>, - sizeof...(Fs)>; - return visit_visitor_return_type_check<lib::decay_t<Fs>...>(), - result{{lib::forward<Fs>(fs)...}}; - } - - template <std::size_t... Is> - struct dispatcher { - template <typename F, typename... Vs> - struct impl { - inline static constexpr DECLTYPE_AUTO dispatch(F f, Vs... vs) - DECLTYPE_AUTO_RETURN(lib::invoke( - static_cast<F>(f), - access::base::get_alt<Is>(static_cast<Vs>(vs))...)) - }; - }; - - template <typename F, typename... Vs, std::size_t... Is> - inline static constexpr AUTO make_dispatch(lib::index_sequence<Is...>) - AUTO_RETURN(&dispatcher<Is...>::template impl<F, Vs...>::dispatch) - - template <std::size_t I, typename F, typename... Vs> - inline static constexpr AUTO make_fdiagonal_impl() - AUTO_RETURN(make_dispatch<F, Vs...>( - lib::index_sequence<lib::indexed_type<I, Vs>::value...>{})) - - template <typename F, typename... Vs, std::size_t... Is> - inline static constexpr AUTO make_fdiagonal_impl( - lib::index_sequence<Is...>) - AUTO_RETURN(make_farray(make_fdiagonal_impl<Is, F, Vs...>()...)) - - template <typename F, typename V, typename... Vs> - inline static constexpr /* auto * */ auto make_fdiagonal() - -> decltype(make_fdiagonal_impl<F, V, Vs...>( - lib::make_index_sequence<lib::decay_t<V>::size()>{})) { - static_assert(lib::all<(lib::decay_t<V>::size() == - lib::decay_t<Vs>::size())...>::value, - "all of the variants must be the same size."); - return make_fdiagonal_impl<F, V, Vs...>( - lib::make_index_sequence<lib::decay_t<V>::size()>{}); - } - -#ifdef MPARK_RETURN_TYPE_DEDUCTION - template <typename F, typename... Vs, typename Is> - inline static constexpr auto make_fmatrix_impl(Is is) { - return make_dispatch<F, Vs...>(is); - } - - template <typename F, - typename... Vs, - typename Is, - std::size_t... Js, - typename... Ls> - inline static constexpr auto make_fmatrix_impl( - Is, lib::index_sequence<Js...>, Ls... ls) { - return make_farray(make_fmatrix_impl<F, Vs...>( - lib::push_back_t<Is, Js>{}, ls...)...); - } - - template <typename F, typename... Vs> - inline static constexpr auto make_fmatrix() { - return make_fmatrix_impl<F, Vs...>( - lib::index_sequence<>{}, - lib::make_index_sequence<lib::decay_t<Vs>::size()>{}...); - } -#else - template <typename F, typename... Vs> - struct make_fmatrix_impl { - template <typename...> - struct impl; - - template <typename Is> - struct impl<Is> { - inline constexpr AUTO operator()() const - AUTO_RETURN(make_dispatch<F, Vs...>(Is{})) - }; - - template <typename Is, std::size_t... Js, typename... Ls> - struct impl<Is, lib::index_sequence<Js...>, Ls...> { - inline constexpr AUTO operator()() const - AUTO_RETURN( - make_farray(impl<lib::push_back_t<Is, Js>, Ls...>{}()...)) - }; - }; - - template <typename F, typename... Vs> - inline static constexpr AUTO make_fmatrix() - AUTO_RETURN( - typename make_fmatrix_impl<F, Vs...>::template impl< - lib::index_sequence<>, - lib::make_index_sequence<lib::decay_t<Vs>::size()>...>{}()) -#endif - }; // namespace base - - template <typename F, typename... Vs> - using FDiagonal = decltype(base::make_fdiagonal<F, Vs...>()); - - template <typename F, typename... Vs> - struct fdiagonal { -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4268) -#endif - static constexpr FDiagonal<F, Vs...> value = - base::make_fdiagonal<F, Vs...>(); -#ifdef _MSC_VER -#pragma warning(pop) -#endif - }; - - template <typename F, typename... Vs> - constexpr FDiagonal<F, Vs...> fdiagonal<F, Vs...>::value; - - template <typename F, typename... Vs> - using FMatrix = decltype(base::make_fmatrix<F, Vs...>()); - - template <typename F, typename... Vs> - struct fmatrix { -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4268) -#endif - static constexpr FMatrix<F, Vs...> value = - base::make_fmatrix<F, Vs...>(); -#ifdef _MSC_VER -#pragma warning(pop) -#endif - }; - - template <typename F, typename... Vs> - constexpr FMatrix<F, Vs...> fmatrix<F, Vs...>::value; - - struct alt { - template <typename Visitor, typename... Vs> - inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index, - Visitor &&visitor, - Vs &&... vs) - DECLTYPE_AUTO_RETURN(base::at( - fdiagonal<Visitor &&, - decltype(as_base(lib::forward<Vs>(vs)))...>::value, - index)(lib::forward<Visitor>(visitor), - as_base(lib::forward<Vs>(vs))...)) - - template <typename Visitor, typename... Vs> - inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor, - Vs &&... vs) - DECLTYPE_AUTO_RETURN(base::at( - fmatrix<Visitor &&, - decltype(as_base(lib::forward<Vs>(vs)))...>::value, - vs.index()...)(lib::forward<Visitor>(visitor), - as_base(lib::forward<Vs>(vs))...)) - }; - - struct variant { - private: - template <typename Visitor, typename... Values> - struct visit_exhaustive_visitor_check { - static_assert( - lib::is_invocable<Visitor, Values...>::value, - "`mpark::visit` requires the visitor to be exhaustive."); - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4100) -#endif - inline constexpr DECLTYPE_AUTO operator()(Visitor &&visitor, - Values &&... values) const - DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward<Visitor>(visitor), - lib::forward<Values>(values)...)) -#ifdef _MSC_VER -#pragma warning(pop) -#endif - }; - - template <typename Visitor> - struct value_visitor { - Visitor &&visitor_; - - template <typename... Alts> - inline constexpr DECLTYPE_AUTO operator()(Alts &&... alts) const - DECLTYPE_AUTO_RETURN( - visit_exhaustive_visitor_check< - Visitor, - decltype((lib::forward<Alts>(alts).value))...>{}( - lib::forward<Visitor>(visitor_), - lib::forward<Alts>(alts).value...)) - }; - - template <typename Visitor> - inline static constexpr AUTO make_value_visitor(Visitor &&visitor) - AUTO_RETURN(value_visitor<Visitor>{lib::forward<Visitor>(visitor)}) - - public: - template <typename Visitor, typename... Vs> - inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index, - Visitor &&visitor, - Vs &&... vs) - DECLTYPE_AUTO_RETURN( - alt::visit_alt_at(index, - lib::forward<Visitor>(visitor), - lib::forward<Vs>(vs).impl_...)) - - template <typename Visitor, typename... Vs> - inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor, - Vs &&... vs) - DECLTYPE_AUTO_RETURN(alt::visit_alt(lib::forward<Visitor>(visitor), - lib::forward<Vs>(vs).impl_...)) - - template <typename Visitor, typename... Vs> - inline static constexpr DECLTYPE_AUTO visit_value_at(std::size_t index, - Visitor &&visitor, - Vs &&... vs) - DECLTYPE_AUTO_RETURN( - visit_alt_at(index, - make_value_visitor(lib::forward<Visitor>(visitor)), - lib::forward<Vs>(vs)...)) - - template <typename Visitor, typename... Vs> - inline static constexpr DECLTYPE_AUTO visit_value(Visitor &&visitor, - Vs &&... vs) - DECLTYPE_AUTO_RETURN( - visit_alt(make_value_visitor(lib::forward<Visitor>(visitor)), - lib::forward<Vs>(vs)...)) - }; - - } // namespace visitation - - template <std::size_t Index, typename T> - struct alt { - using value_type = T; - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4244) -#endif - template <typename... Args> - inline explicit constexpr alt(in_place_t, Args &&... args) - : value(lib::forward<Args>(args)...) {} -#ifdef _MSC_VER -#pragma warning(pop) -#endif - - T value; - }; - - template <Trait DestructibleTrait, std::size_t Index, typename... Ts> - union recursive_union; - - template <Trait DestructibleTrait, std::size_t Index> - union recursive_union<DestructibleTrait, Index> {}; - -#define MPARK_VARIANT_RECURSIVE_UNION(destructible_trait, destructor) \ - template <std::size_t Index, typename T, typename... Ts> \ - union recursive_union<destructible_trait, Index, T, Ts...> { \ - public: \ - inline explicit constexpr recursive_union(valueless_t) noexcept \ - : dummy_{} {} \ - \ - template <typename... Args> \ - inline explicit constexpr recursive_union(in_place_index_t<0>, \ - Args &&... args) \ - : head_(in_place_t{}, lib::forward<Args>(args)...) {} \ - \ - template <std::size_t I, typename... Args> \ - inline explicit constexpr recursive_union(in_place_index_t<I>, \ - Args &&... args) \ - : tail_(in_place_index_t<I - 1>{}, lib::forward<Args>(args)...) {} \ - \ - recursive_union(const recursive_union &) = default; \ - recursive_union(recursive_union &&) = default; \ - \ - destructor \ - \ - recursive_union &operator=(const recursive_union &) = default; \ - recursive_union &operator=(recursive_union &&) = default; \ - \ - private: \ - char dummy_; \ - alt<Index, T> head_; \ - recursive_union<destructible_trait, Index + 1, Ts...> tail_; \ - \ - friend struct access::recursive_union; \ - } - - MPARK_VARIANT_RECURSIVE_UNION(Trait::TriviallyAvailable, - ~recursive_union() = default;); - MPARK_VARIANT_RECURSIVE_UNION(Trait::Available, - ~recursive_union() {}); - MPARK_VARIANT_RECURSIVE_UNION(Trait::Unavailable, - ~recursive_union() = delete;); - -#undef MPARK_VARIANT_RECURSIVE_UNION - - using index_t = unsigned int; - - template <Trait DestructibleTrait, typename... Ts> - class base { - public: - inline explicit constexpr base(valueless_t tag) noexcept - : data_(tag), index_(static_cast<index_t>(-1)) {} - - template <std::size_t I, typename... Args> - inline explicit constexpr base(in_place_index_t<I>, Args &&... args) - : data_(in_place_index_t<I>{}, lib::forward<Args>(args)...), - index_(I) {} - - inline constexpr bool valueless_by_exception() const noexcept { - return index_ == static_cast<index_t>(-1); - } - - inline constexpr std::size_t index() const noexcept { - return valueless_by_exception() ? variant_npos : index_; - } - - protected: - using data_t = recursive_union<DestructibleTrait, 0, Ts...>; - - friend inline constexpr base &as_base(base &b) { return b; } - friend inline constexpr const base &as_base(const base &b) { return b; } - friend inline constexpr base &&as_base(base &&b) { return lib::move(b); } - friend inline constexpr const base &&as_base(const base &&b) { return lib::move(b); } - - friend inline constexpr data_t &data(base &b) { return b.data_; } - friend inline constexpr const data_t &data(const base &b) { return b.data_; } - friend inline constexpr data_t &&data(base &&b) { return lib::move(b).data_; } - friend inline constexpr const data_t &&data(const base &&b) { return lib::move(b).data_; } - - inline static constexpr std::size_t size() { return sizeof...(Ts); } - - data_t data_; - index_t index_; - - friend struct access::base; - friend struct visitation::base; - }; - - struct dtor { -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4100) -#endif - template <typename Alt> - inline void operator()(Alt &alt) const noexcept { alt.~Alt(); } -#ifdef _MSC_VER -#pragma warning(pop) -#endif - }; - -#if defined(_MSC_VER) && _MSC_VER < 1910 -#define INHERITING_CTOR(type, base) \ - template <typename... Args> \ - inline explicit constexpr type(Args &&... args) \ - : base(lib::forward<Args>(args)...) {} -#else -#define INHERITING_CTOR(type, base) using base::base; -#endif - - template <typename Traits, Trait = Traits::destructible_trait> - class destructor; - -#define MPARK_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \ - template <typename... Ts> \ - class destructor<traits<Ts...>, destructible_trait> \ - : public base<destructible_trait, Ts...> { \ - using super = base<destructible_trait, Ts...>; \ - \ - public: \ - INHERITING_CTOR(destructor, super) \ - using super::operator=; \ - \ - destructor(const destructor &) = default; \ - destructor(destructor &&) = default; \ - definition \ - destructor &operator=(const destructor &) = default; \ - destructor &operator=(destructor &&) = default; \ - \ - protected: \ - destroy \ - } - - MPARK_VARIANT_DESTRUCTOR( - Trait::TriviallyAvailable, - ~destructor() = default;, - inline void destroy() noexcept { - this->index_ = static_cast<index_t>(-1); - }); - - MPARK_VARIANT_DESTRUCTOR( - Trait::Available, - ~destructor() { destroy(); }, - inline void destroy() noexcept { - if (!this->valueless_by_exception()) { - visitation::alt::visit_alt(dtor{}, *this); - } - this->index_ = static_cast<index_t>(-1); - }); - - MPARK_VARIANT_DESTRUCTOR( - Trait::Unavailable, - ~destructor() = delete;, - inline void destroy() noexcept = delete;); - -#undef MPARK_VARIANT_DESTRUCTOR - - template <typename Traits> - class constructor : public destructor<Traits> { - using super = destructor<Traits>; - - public: - INHERITING_CTOR(constructor, super) - using super::operator=; - - protected: -#ifndef MPARK_GENERIC_LAMBDAS - struct ctor { - template <typename LhsAlt, typename RhsAlt> - inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const { - constructor::construct_alt(lhs_alt, - lib::forward<RhsAlt>(rhs_alt).value); - } - }; -#endif - - template <std::size_t I, typename T, typename... Args> - inline static T &construct_alt(alt<I, T> &a, Args &&... args) { - ::new (static_cast<void *>(lib::addressof(a))) - alt<I, T>(in_place_t{}, lib::forward<Args>(args)...); - return a.value; - } - - template <typename Rhs> - inline static void generic_construct(constructor &lhs, Rhs &&rhs) { - lhs.destroy(); - if (!rhs.valueless_by_exception()) { - visitation::alt::visit_alt_at( - rhs.index(), -#ifdef MPARK_GENERIC_LAMBDAS - [](auto &lhs_alt, auto &&rhs_alt) { - constructor::construct_alt( - lhs_alt, lib::forward<decltype(rhs_alt)>(rhs_alt).value); - } -#else - ctor{} -#endif - , - lhs, - lib::forward<Rhs>(rhs)); - lhs.index_ = rhs.index_; - } - } - }; - - template <typename Traits, Trait = Traits::move_constructible_trait> - class move_constructor; - -#define MPARK_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \ - template <typename... Ts> \ - class move_constructor<traits<Ts...>, move_constructible_trait> \ - : public constructor<traits<Ts...>> { \ - using super = constructor<traits<Ts...>>; \ - \ - public: \ - INHERITING_CTOR(move_constructor, super) \ - using super::operator=; \ - \ - move_constructor(const move_constructor &) = default; \ - definition \ - ~move_constructor() = default; \ - move_constructor &operator=(const move_constructor &) = default; \ - move_constructor &operator=(move_constructor &&) = default; \ - } - - MPARK_VARIANT_MOVE_CONSTRUCTOR( - Trait::TriviallyAvailable, - move_constructor(move_constructor &&that) = default;); - - MPARK_VARIANT_MOVE_CONSTRUCTOR( - Trait::Available, - move_constructor(move_constructor &&that) noexcept( - lib::all<std::is_nothrow_move_constructible<Ts>::value...>::value) - : move_constructor(valueless_t{}) { - this->generic_construct(*this, lib::move(that)); - }); - - MPARK_VARIANT_MOVE_CONSTRUCTOR( - Trait::Unavailable, - move_constructor(move_constructor &&) = delete;); - -#undef MPARK_VARIANT_MOVE_CONSTRUCTOR - - template <typename Traits, Trait = Traits::copy_constructible_trait> - class copy_constructor; - -#define MPARK_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \ - template <typename... Ts> \ - class copy_constructor<traits<Ts...>, copy_constructible_trait> \ - : public move_constructor<traits<Ts...>> { \ - using super = move_constructor<traits<Ts...>>; \ - \ - public: \ - INHERITING_CTOR(copy_constructor, super) \ - using super::operator=; \ - \ - definition \ - copy_constructor(copy_constructor &&) = default; \ - ~copy_constructor() = default; \ - copy_constructor &operator=(const copy_constructor &) = default; \ - copy_constructor &operator=(copy_constructor &&) = default; \ - } - - MPARK_VARIANT_COPY_CONSTRUCTOR( - Trait::TriviallyAvailable, - copy_constructor(const copy_constructor &that) = default;); - - MPARK_VARIANT_COPY_CONSTRUCTOR( - Trait::Available, - copy_constructor(const copy_constructor &that) - : copy_constructor(valueless_t{}) { - this->generic_construct(*this, that); - }); - - MPARK_VARIANT_COPY_CONSTRUCTOR( - Trait::Unavailable, - copy_constructor(const copy_constructor &) = delete;); - -#undef MPARK_VARIANT_COPY_CONSTRUCTOR - - template <typename Traits> - class assignment : public copy_constructor<Traits> { - using super = copy_constructor<Traits>; - - public: - INHERITING_CTOR(assignment, super) - using super::operator=; - - template <std::size_t I, typename... Args> - inline /* auto & */ auto emplace(Args &&... args) - -> decltype(this->construct_alt(access::base::get_alt<I>(*this), - lib::forward<Args>(args)...)) { - this->destroy(); - auto &result = this->construct_alt(access::base::get_alt<I>(*this), - lib::forward<Args>(args)...); - this->index_ = I; - return result; - } - - protected: -#ifndef MPARK_GENERIC_LAMBDAS - template <typename That> - struct assigner { - template <typename ThisAlt, typename ThatAlt> - inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const { - self->assign_alt(this_alt, lib::forward<ThatAlt>(that_alt).value); - } - assignment *self; - }; -#endif - - template <std::size_t I, typename T, typename Arg> - inline void assign_alt(alt<I, T> &a, Arg &&arg) { - if (this->index() == I) { -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4244) -#endif - a.value = lib::forward<Arg>(arg); -#ifdef _MSC_VER -#pragma warning(pop) -#endif - } else { - struct { - void operator()(std::true_type) const { - this_->emplace<I>(lib::forward<Arg>(arg_)); - } - void operator()(std::false_type) const { - this_->emplace<I>(T(lib::forward<Arg>(arg_))); - } - assignment *this_; - Arg &&arg_; - } impl{this, lib::forward<Arg>(arg)}; - impl(lib::bool_constant< - std::is_nothrow_constructible<T, Arg>::value || - !std::is_nothrow_move_constructible<T>::value>{}); - } - } - - template <typename That> - inline void generic_assign(That &&that) { - if (this->valueless_by_exception() && that.valueless_by_exception()) { - // do nothing. - } else if (that.valueless_by_exception()) { - this->destroy(); - } else { - visitation::alt::visit_alt_at( - that.index(), -#ifdef MPARK_GENERIC_LAMBDAS - [this](auto &this_alt, auto &&that_alt) { - this->assign_alt( - this_alt, lib::forward<decltype(that_alt)>(that_alt).value); - } -#else - assigner<That>{this} -#endif - , - *this, - lib::forward<That>(that)); - } - } - }; - - template <typename Traits, Trait = Traits::move_assignable_trait> - class move_assignment; - -#define MPARK_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \ - template <typename... Ts> \ - class move_assignment<traits<Ts...>, move_assignable_trait> \ - : public assignment<traits<Ts...>> { \ - using super = assignment<traits<Ts...>>; \ - \ - public: \ - INHERITING_CTOR(move_assignment, super) \ - using super::operator=; \ - \ - move_assignment(const move_assignment &) = default; \ - move_assignment(move_assignment &&) = default; \ - ~move_assignment() = default; \ - move_assignment &operator=(const move_assignment &) = default; \ - definition \ - } - - MPARK_VARIANT_MOVE_ASSIGNMENT( - Trait::TriviallyAvailable, - move_assignment &operator=(move_assignment &&that) = default;); - - MPARK_VARIANT_MOVE_ASSIGNMENT( - Trait::Available, - move_assignment & - operator=(move_assignment &&that) noexcept( - lib::all<(std::is_nothrow_move_constructible<Ts>::value && - std::is_nothrow_move_assignable<Ts>::value)...>::value) { - this->generic_assign(lib::move(that)); - return *this; - }); - - MPARK_VARIANT_MOVE_ASSIGNMENT( - Trait::Unavailable, - move_assignment &operator=(move_assignment &&) = delete;); - -#undef MPARK_VARIANT_MOVE_ASSIGNMENT - - template <typename Traits, Trait = Traits::copy_assignable_trait> - class copy_assignment; - -#define MPARK_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \ - template <typename... Ts> \ - class copy_assignment<traits<Ts...>, copy_assignable_trait> \ - : public move_assignment<traits<Ts...>> { \ - using super = move_assignment<traits<Ts...>>; \ - \ - public: \ - INHERITING_CTOR(copy_assignment, super) \ - using super::operator=; \ - \ - copy_assignment(const copy_assignment &) = default; \ - copy_assignment(copy_assignment &&) = default; \ - ~copy_assignment() = default; \ - definition \ - copy_assignment &operator=(copy_assignment &&) = default; \ - } - - MPARK_VARIANT_COPY_ASSIGNMENT( - Trait::TriviallyAvailable, - copy_assignment &operator=(const copy_assignment &that) = default;); - - MPARK_VARIANT_COPY_ASSIGNMENT( - Trait::Available, - copy_assignment &operator=(const copy_assignment &that) { - this->generic_assign(that); - return *this; - }); - - MPARK_VARIANT_COPY_ASSIGNMENT( - Trait::Unavailable, - copy_assignment &operator=(const copy_assignment &) = delete;); - -#undef MPARK_VARIANT_COPY_ASSIGNMENT - - template <typename... Ts> - class impl : public copy_assignment<traits<Ts...>> { - using super = copy_assignment<traits<Ts...>>; - - public: - INHERITING_CTOR(impl, super) - using super::operator=; - - template <std::size_t I, typename Arg> - inline void assign(Arg &&arg) { - this->assign_alt(access::base::get_alt<I>(*this), - lib::forward<Arg>(arg)); - } - - inline void swap(impl &that) { - if (this->valueless_by_exception() && that.valueless_by_exception()) { - // do nothing. - } else if (this->index() == that.index()) { - visitation::alt::visit_alt_at(this->index(), -#ifdef MPARK_GENERIC_LAMBDAS - [](auto &this_alt, auto &that_alt) { - using std::swap; - swap(this_alt.value, - that_alt.value); - } -#else - swapper{} -#endif - , - *this, - that); - } else { - impl *lhs = this; - impl *rhs = lib::addressof(that); - if (lhs->move_nothrow() && !rhs->move_nothrow()) { - std::swap(lhs, rhs); - } - impl tmp(lib::move(*rhs)); -#ifdef MPARK_EXCEPTIONS - // EXTENSION: When the move construction of `lhs` into `rhs` throws - // and `tmp` is nothrow move constructible then we move `tmp` back - // into `rhs` and provide the strong exception safety guarantee. - try { - this->generic_construct(*rhs, lib::move(*lhs)); - } catch (...) { - if (tmp.move_nothrow()) { - this->generic_construct(*rhs, lib::move(tmp)); - } - throw; - } -#else - this->generic_construct(*rhs, lib::move(*lhs)); -#endif - this->generic_construct(*lhs, lib::move(tmp)); - } - } - - private: -#ifndef MPARK_GENERIC_LAMBDAS - struct swapper { - template <typename ThisAlt, typename ThatAlt> - inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const { - using std::swap; - swap(this_alt.value, that_alt.value); - } - }; -#endif - - inline constexpr bool move_nothrow() const { - return this->valueless_by_exception() || - lib::array<bool, sizeof...(Ts)>{ - {std::is_nothrow_move_constructible<Ts>::value...} - }[this->index()]; - } - }; - - template <std::size_t I, typename T> - struct overload_leaf { - using F = lib::size_constant<I> (*)(T); - operator F() const { return nullptr; } - }; - - template <typename... Ts> - struct overload_impl { - private: - template <typename> - struct impl; - - template <std::size_t... Is> - struct impl<lib::index_sequence<Is...>> : overload_leaf<Is, Ts>... {}; - - public: - using type = impl<lib::index_sequence_for<Ts...>>; - }; - - template <typename... Ts> - using overload = typename overload_impl<Ts...>::type; - - template <typename T, typename... Ts> - using best_match = lib::invoke_result_t<overload<Ts...>, T &&>; - - template <typename T> - struct is_in_place_index : std::false_type {}; - - template <std::size_t I> - struct is_in_place_index<in_place_index_t<I>> : std::true_type {}; - - template <typename T> - struct is_in_place_type : std::false_type {}; - - template <typename T> - struct is_in_place_type<in_place_type_t<T>> : std::true_type {}; - - } // detail - - template <typename... Ts> - class variant { - static_assert(0 < sizeof...(Ts), - "variant must consist of at least one alternative."); - - static_assert(lib::all<!std::is_array<Ts>::value...>::value, - "variant can not have an array type as an alternative."); - - static_assert(lib::all<!std::is_reference<Ts>::value...>::value, - "variant can not have a reference type as an alternative."); - - static_assert(lib::all<!std::is_void<Ts>::value...>::value, - "variant can not have a void type as an alternative."); - - public: - template < - typename Front = lib::type_pack_element_t<0, Ts...>, - lib::enable_if_t<std::is_default_constructible<Front>::value, int> = 0> - inline constexpr variant() - : impl_(in_place_index_t<0>{}) {} - - variant(const variant &) = default; - variant(variant &&) = default; - - template < - typename Arg, - typename Decayed = lib::decay_t<Arg>, - lib::enable_if_t<!std::is_same<Decayed, variant>::value, int> = 0, - lib::enable_if_t<!detail::is_in_place_index<Decayed>::value, int> = 0, - lib::enable_if_t<!detail::is_in_place_type<Decayed>::value, int> = 0, - std::size_t I = detail::best_match<Arg, Ts...>::value, - typename T = lib::type_pack_element_t<I, Ts...>, - lib::enable_if_t<std::is_constructible<T, Arg>::value, int> = 0> - inline constexpr variant(Arg &&arg) noexcept( - std::is_nothrow_constructible<T, Arg>::value) - : impl_(in_place_index_t<I>{}, lib::forward<Arg>(arg)) {} - - template < - std::size_t I, - typename... Args, - typename T = lib::type_pack_element_t<I, Ts...>, - lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0> - inline explicit constexpr variant( - in_place_index_t<I>, - Args &&... args) noexcept(std::is_nothrow_constructible<T, - Args...>::value) - : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {} - - template < - std::size_t I, - typename Up, - typename... Args, - typename T = lib::type_pack_element_t<I, Ts...>, - lib::enable_if_t<std::is_constructible<T, - std::initializer_list<Up> &, - Args...>::value, - int> = 0> - inline explicit constexpr variant( - in_place_index_t<I>, - std::initializer_list<Up> il, - Args &&... args) noexcept(std:: - is_nothrow_constructible< - T, - std::initializer_list<Up> &, - Args...>::value) - : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {} - - template < - typename T, - typename... Args, - std::size_t I = detail::find_index_sfinae<T, Ts...>::value, - lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0> - inline explicit constexpr variant( - in_place_type_t<T>, - Args &&... args) noexcept(std::is_nothrow_constructible<T, - Args...>::value) - : impl_(in_place_index_t<I>{}, lib::forward<Args>(args)...) {} - - template < - typename T, - typename Up, - typename... Args, - std::size_t I = detail::find_index_sfinae<T, Ts...>::value, - lib::enable_if_t<std::is_constructible<T, - std::initializer_list<Up> &, - Args...>::value, - int> = 0> - inline explicit constexpr variant( - in_place_type_t<T>, - std::initializer_list<Up> il, - Args &&... args) noexcept(std:: - is_nothrow_constructible< - T, - std::initializer_list<Up> &, - Args...>::value) - : impl_(in_place_index_t<I>{}, il, lib::forward<Args>(args)...) {} - - ~variant() = default; - - variant &operator=(const variant &) = default; - variant &operator=(variant &&) = default; - - template <typename Arg, - lib::enable_if_t<!std::is_same<lib::decay_t<Arg>, variant>::value, - int> = 0, - std::size_t I = detail::best_match<Arg, Ts...>::value, - typename T = lib::type_pack_element_t<I, Ts...>, - lib::enable_if_t<(std::is_assignable<T &, Arg>::value && - std::is_constructible<T, Arg>::value), - int> = 0> - inline variant &operator=(Arg &&arg) { - impl_.template assign<I>(lib::forward<Arg>(arg)); - return *this; - } - - template < - std::size_t I, - typename... Args, - typename T = lib::type_pack_element_t<I, Ts...>, - lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0> - inline T &emplace(Args &&... args) { - return impl_.template emplace<I>(lib::forward<Args>(args)...); - } - - template < - std::size_t I, - typename Up, - typename... Args, - typename T = lib::type_pack_element_t<I, Ts...>, - lib::enable_if_t<std::is_constructible<T, - std::initializer_list<Up> &, - Args...>::value, - int> = 0> - inline T &emplace(std::initializer_list<Up> il, Args &&... args) { - return impl_.template emplace<I>(il, lib::forward<Args>(args)...); - } - - template < - typename T, - typename... Args, - std::size_t I = detail::find_index_sfinae<T, Ts...>::value, - lib::enable_if_t<std::is_constructible<T, Args...>::value, int> = 0> - inline T &emplace(Args &&... args) { - return impl_.template emplace<I>(lib::forward<Args>(args)...); - } - - template < - typename T, - typename Up, - typename... Args, - std::size_t I = detail::find_index_sfinae<T, Ts...>::value, - lib::enable_if_t<std::is_constructible<T, - std::initializer_list<Up> &, - Args...>::value, - int> = 0> - inline T &emplace(std::initializer_list<Up> il, Args &&... args) { - return impl_.template emplace<I>(il, lib::forward<Args>(args)...); - } - - inline constexpr bool valueless_by_exception() const noexcept { - return impl_.valueless_by_exception(); - } - - inline constexpr std::size_t index() const noexcept { - return impl_.index(); - } - - template <bool Dummy = true, - lib::enable_if_t< - lib::all<Dummy, - (lib::dependent_type<std::is_move_constructible<Ts>, - Dummy>::value && - lib::dependent_type<lib::is_swappable<Ts>, - Dummy>::value)...>::value, - int> = 0> - inline void swap(variant &that) noexcept( - lib::all<(std::is_nothrow_move_constructible<Ts>::value && - lib::is_nothrow_swappable<Ts>::value)...>::value) { - impl_.swap(that.impl_); - } - - private: - detail::impl<Ts...> impl_; - - friend struct detail::access::variant; - friend struct detail::visitation::variant; - }; - - template <std::size_t I, typename... Ts> - inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept { - return v.index() == I; - } - - template <typename T, typename... Ts> - inline constexpr bool holds_alternative(const variant<Ts...> &v) noexcept { - return holds_alternative<detail::find_index_checked<T, Ts...>::value>(v); - } - - namespace detail { - template <std::size_t I, typename V> - struct generic_get_impl { - constexpr generic_get_impl(int) {} - - constexpr AUTO_REFREF operator()(V &&v) const - AUTO_REFREF_RETURN( - access::variant::get_alt<I>(lib::forward<V>(v)).value) - }; - - template <std::size_t I, typename V> - inline constexpr AUTO_REFREF generic_get(V &&v) - AUTO_REFREF_RETURN(generic_get_impl<I, V>( - holds_alternative<I>(v) ? 0 : (throw_bad_variant_access(), 0))( - lib::forward<V>(v))) - } // namespace detail - - template <std::size_t I, typename... Ts> - inline constexpr variant_alternative_t<I, variant<Ts...>> &get( - variant<Ts...> &v) { - return detail::generic_get<I>(v); - } - - template <std::size_t I, typename... Ts> - inline constexpr variant_alternative_t<I, variant<Ts...>> &&get( - variant<Ts...> &&v) { - return detail::generic_get<I>(lib::move(v)); - } - - template <std::size_t I, typename... Ts> - inline constexpr const variant_alternative_t<I, variant<Ts...>> &get( - const variant<Ts...> &v) { - return detail::generic_get<I>(v); - } - - template <std::size_t I, typename... Ts> - inline constexpr const variant_alternative_t<I, variant<Ts...>> &&get( - const variant<Ts...> &&v) { - return detail::generic_get<I>(lib::move(v)); - } - - template <typename T, typename... Ts> - inline constexpr T &get(variant<Ts...> &v) { - return get<detail::find_index_checked<T, Ts...>::value>(v); - } - - template <typename T, typename... Ts> - inline constexpr T &&get(variant<Ts...> &&v) { - return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v)); - } - - template <typename T, typename... Ts> - inline constexpr const T &get(const variant<Ts...> &v) { - return get<detail::find_index_checked<T, Ts...>::value>(v); - } - - template <typename T, typename... Ts> - inline constexpr const T &&get(const variant<Ts...> &&v) { - return get<detail::find_index_checked<T, Ts...>::value>(lib::move(v)); - } - - namespace detail { - - template <std::size_t I, typename V> - inline constexpr /* auto * */ AUTO generic_get_if(V *v) noexcept - AUTO_RETURN(v && holds_alternative<I>(*v) - ? lib::addressof(access::variant::get_alt<I>(*v).value) - : nullptr) - - } // namespace detail - - template <std::size_t I, typename... Ts> - inline constexpr lib::add_pointer_t<variant_alternative_t<I, variant<Ts...>>> - get_if(variant<Ts...> *v) noexcept { - return detail::generic_get_if<I>(v); - } - - template <std::size_t I, typename... Ts> - inline constexpr lib::add_pointer_t< - const variant_alternative_t<I, variant<Ts...>>> - get_if(const variant<Ts...> *v) noexcept { - return detail::generic_get_if<I>(v); - } - - template <typename T, typename... Ts> - inline constexpr lib::add_pointer_t<T> - get_if(variant<Ts...> *v) noexcept { - return get_if<detail::find_index_checked<T, Ts...>::value>(v); - } - - template <typename T, typename... Ts> - inline constexpr lib::add_pointer_t<const T> - get_if(const variant<Ts...> *v) noexcept { - return get_if<detail::find_index_checked<T, Ts...>::value>(v); - } - - template <typename... Ts> - inline constexpr bool operator==(const variant<Ts...> &lhs, - const variant<Ts...> &rhs) { - using detail::visitation::variant; - using lib::equal_to; -#ifdef MPARK_CPP14_CONSTEXPR - if (lhs.index() != rhs.index()) return false; - if (lhs.valueless_by_exception()) return true; - return variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs); -#else - return lhs.index() == rhs.index() && - (lhs.valueless_by_exception() || - variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs)); -#endif - } - - template <typename... Ts> - inline constexpr bool operator!=(const variant<Ts...> &lhs, - const variant<Ts...> &rhs) { - using detail::visitation::variant; - using lib::not_equal_to; -#ifdef MPARK_CPP14_CONSTEXPR - if (lhs.index() != rhs.index()) return true; - if (lhs.valueless_by_exception()) return false; - return variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs); -#else - return lhs.index() != rhs.index() || - (!lhs.valueless_by_exception() && - variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs)); -#endif - } - - template <typename... Ts> - inline constexpr bool operator<(const variant<Ts...> &lhs, - const variant<Ts...> &rhs) { - using detail::visitation::variant; - using lib::less; -#ifdef MPARK_CPP14_CONSTEXPR - if (rhs.valueless_by_exception()) return false; - if (lhs.valueless_by_exception()) return true; - if (lhs.index() < rhs.index()) return true; - if (lhs.index() > rhs.index()) return false; - return variant::visit_value_at(lhs.index(), less{}, lhs, rhs); -#else - return !rhs.valueless_by_exception() && - (lhs.valueless_by_exception() || lhs.index() < rhs.index() || - (lhs.index() == rhs.index() && - variant::visit_value_at(lhs.index(), less{}, lhs, rhs))); -#endif - } - - template <typename... Ts> - inline constexpr bool operator>(const variant<Ts...> &lhs, - const variant<Ts...> &rhs) { - using detail::visitation::variant; - using lib::greater; -#ifdef MPARK_CPP14_CONSTEXPR - if (lhs.valueless_by_exception()) return false; - if (rhs.valueless_by_exception()) return true; - if (lhs.index() > rhs.index()) return true; - if (lhs.index() < rhs.index()) return false; - return variant::visit_value_at(lhs.index(), greater{}, lhs, rhs); -#else - return !lhs.valueless_by_exception() && - (rhs.valueless_by_exception() || lhs.index() > rhs.index() || - (lhs.index() == rhs.index() && - variant::visit_value_at(lhs.index(), greater{}, lhs, rhs))); -#endif - } - - template <typename... Ts> - inline constexpr bool operator<=(const variant<Ts...> &lhs, - const variant<Ts...> &rhs) { - using detail::visitation::variant; - using lib::less_equal; -#ifdef MPARK_CPP14_CONSTEXPR - if (lhs.valueless_by_exception()) return true; - if (rhs.valueless_by_exception()) return false; - if (lhs.index() < rhs.index()) return true; - if (lhs.index() > rhs.index()) return false; - return variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs); -#else - return lhs.valueless_by_exception() || - (!rhs.valueless_by_exception() && - (lhs.index() < rhs.index() || - (lhs.index() == rhs.index() && - variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs)))); -#endif - } - - template <typename... Ts> - inline constexpr bool operator>=(const variant<Ts...> &lhs, - const variant<Ts...> &rhs) { - using detail::visitation::variant; - using lib::greater_equal; -#ifdef MPARK_CPP14_CONSTEXPR - if (rhs.valueless_by_exception()) return true; - if (lhs.valueless_by_exception()) return false; - if (lhs.index() > rhs.index()) return true; - if (lhs.index() < rhs.index()) return false; - return variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs); -#else - return rhs.valueless_by_exception() || - (!lhs.valueless_by_exception() && - (lhs.index() > rhs.index() || - (lhs.index() == rhs.index() && - variant::visit_value_at( - lhs.index(), greater_equal{}, lhs, rhs)))); -#endif - } - - struct monostate {}; - - inline constexpr bool operator<(monostate, monostate) noexcept { - return false; - } - - inline constexpr bool operator>(monostate, monostate) noexcept { - return false; - } - - inline constexpr bool operator<=(monostate, monostate) noexcept { - return true; - } - - inline constexpr bool operator>=(monostate, monostate) noexcept { - return true; - } - - inline constexpr bool operator==(monostate, monostate) noexcept { - return true; - } - - inline constexpr bool operator!=(monostate, monostate) noexcept { - return false; - } - -#ifdef MPARK_CPP14_CONSTEXPR - namespace detail { - - inline constexpr bool all(std::initializer_list<bool> bs) { - for (bool b : bs) { - if (!b) { - return false; - } - } - return true; - } - - } // namespace detail - - template <typename Visitor, typename... Vs> - inline constexpr decltype(auto) visit(Visitor &&visitor, Vs &&... vs) { - return (detail::all({!vs.valueless_by_exception()...}) - ? (void)0 - : throw_bad_variant_access()), - detail::visitation::variant::visit_value( - lib::forward<Visitor>(visitor), lib::forward<Vs>(vs)...); - } -#else - namespace detail { - - template <std::size_t N> - inline constexpr bool all_impl(const lib::array<bool, N> &bs, - std::size_t idx) { - return idx >= N || (bs[idx] && all_impl(bs, idx + 1)); - } - - template <std::size_t N> - inline constexpr bool all(const lib::array<bool, N> &bs) { - return all_impl(bs, 0); - } - - } // namespace detail - - template <typename Visitor, typename... Vs> - inline constexpr DECLTYPE_AUTO visit(Visitor &&visitor, Vs &&... vs) - DECLTYPE_AUTO_RETURN( - (detail::all( - lib::array<bool, sizeof...(Vs)>{{!vs.valueless_by_exception()...}}) - ? (void)0 - : throw_bad_variant_access()), - detail::visitation::variant::visit_value(lib::forward<Visitor>(visitor), - lib::forward<Vs>(vs)...)) -#endif - - template <typename... Ts> - inline auto swap(variant<Ts...> &lhs, - variant<Ts...> &rhs) noexcept(noexcept(lhs.swap(rhs))) - -> decltype(lhs.swap(rhs)) { - lhs.swap(rhs); - } - - namespace detail { - - template <typename T, typename...> - using enabled_type = T; - - namespace hash { - - template <typename H, typename K> - constexpr bool meets_requirements() { - return std::is_copy_constructible<H>::value && - std::is_move_constructible<H>::value && - lib::is_invocable_r<std::size_t, H, const K &>::value; - } - - template <typename K> - constexpr bool is_enabled() { - using H = std::hash<K>; - return meets_requirements<H, K>() && - std::is_default_constructible<H>::value && - std::is_copy_assignable<H>::value && - std::is_move_assignable<H>::value; - } - - } // namespace hash - - } // namespace detail - -#undef AUTO -#undef AUTO_RETURN - -#undef AUTO_REFREF -#undef AUTO_REFREF_RETURN - -#undef DECLTYPE_AUTO -#undef DECLTYPE_AUTO_RETURN - -} // namespace mpark - -namespace std { - - template <typename... Ts> - struct hash<mpark::detail::enabled_type< - mpark::variant<Ts...>, - mpark::lib::enable_if_t<mpark::lib::all<mpark::detail::hash::is_enabled< - mpark::lib::remove_const_t<Ts>>()...>::value>>> { - using argument_type = mpark::variant<Ts...>; - using result_type = std::size_t; - - inline result_type operator()(const argument_type &v) const { - using mpark::detail::visitation::variant; - std::size_t result = - v.valueless_by_exception() - ? 299792458 // Random value chosen by the universe upon creation - : variant::visit_alt( -#ifdef MPARK_GENERIC_LAMBDAS - [](const auto &alt) { - using alt_type = mpark::lib::decay_t<decltype(alt)>; - using value_type = mpark::lib::remove_const_t< - typename alt_type::value_type>; - return hash<value_type>{}(alt.value); - } -#else - hasher{} -#endif - , - v); - return hash_combine(result, hash<std::size_t>{}(v.index())); - } - - private: -#ifndef MPARK_GENERIC_LAMBDAS - struct hasher { - template <typename Alt> - inline std::size_t operator()(const Alt &alt) const { - using alt_type = mpark::lib::decay_t<Alt>; - using value_type = - mpark::lib::remove_const_t<typename alt_type::value_type>; - return hash<value_type>{}(alt.value); - } - }; -#endif - - static std::size_t hash_combine(std::size_t lhs, std::size_t rhs) { - return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2); - } - }; - - template <> - struct hash<mpark::monostate> { - using argument_type = mpark::monostate; - using result_type = std::size_t; - - inline result_type operator()(const argument_type &) const noexcept { - return 66740831; // return a fundamentally attractive random value. - } - }; - -} // namespace std - -#if defined(__GNUC__) && __GNUC__ >= 9 -#pragma GCC diagnostic pop -#endif - -#endif // MPARK_VARIANT_HPP diff --git a/src/src.qbs b/src/src.qbs index f5a5ae5a1..7c07e5b61 100644 --- a/src/src.qbs +++ b/src/src.qbs @@ -9,6 +9,6 @@ Project { "shared/json/json.qbs", "shared/lsp/lsp.qbs", "shared/quickjs/quickjs.qbs", - "shared/variant/variant.qbs", + "shared/span/span.qbs", ] } diff --git a/tests/auto/api/tst_api.cpp b/tests/auto/api/tst_api.cpp index 3816eca2b..45463312b 100644 --- a/tests/auto/api/tst_api.cpp +++ b/tests/auto/api/tst_api.cpp @@ -2902,6 +2902,7 @@ void TestApi::targetArtifactStatus() void TestApi::timeout() { QFETCH(QString, projectDirName); + QFETCH(QString, cancelOutput); const auto setupParams = defaultSetupParameters(projectDirName + "/timeout.qbs"); std::unique_ptr<qbs::SetupProjectJob> setupJob{ qbs::Project().setupProject(setupParams, m_logSink, nullptr)}; @@ -2934,13 +2935,15 @@ void TestApi::timeout() const auto errorString = buildJob->error().toString(); QVERIFY2(errorString.contains("cancel"), qPrintable(errorString)); QVERIFY(errorString.contains("timeout")); + QVERIFY(errorString.contains(cancelOutput)); } void TestApi::timeout_data() { QTest::addColumn<QString>("projectDirName"); - QTest::newRow("JS Command") << QString("timeout-js"); - QTest::newRow("Process Command") << QString("timeout-process"); + QTest::addColumn<QString>("cancelOutput"); + QTest::newRow("JS Command") << QString("timeout-js") << QString("infinite loop"); + QTest::newRow("Process Command") << QString("timeout-process") << QString("infinite-loop"); } void TestApi::toolInModule() diff --git a/tests/auto/blackbox/testdata-providers/conan-provider/conan-module-provider.qbs b/tests/auto/blackbox/testdata-providers/conan-provider/conan-module-provider.qbs new file mode 100644 index 000000000..e8880fc96 --- /dev/null +++ b/tests/auto/blackbox/testdata-providers/conan-provider/conan-module-provider.qbs @@ -0,0 +1,11 @@ +CppApplication { + consoleApplication: true + name: "p" + files: "main.cpp" + qbsModuleProviders: "conan" + qbs.buildVariant: "release" + qbs.installPrefix: "" + install: true + Depends { name: "conanmoduleprovider.testlib" } + Depends { name: "conanmoduleprovider.testlibheader" } +} diff --git a/tests/auto/blackbox/testdata-providers/conan-provider/conanfile.txt b/tests/auto/blackbox/testdata-providers/conan-provider/conanfile.txt new file mode 100644 index 000000000..7c40ff7d6 --- /dev/null +++ b/tests/auto/blackbox/testdata-providers/conan-provider/conanfile.txt @@ -0,0 +1,3 @@ +[requires] +conanmoduleprovider.testlib/1.2.3 +conanmoduleprovider.testlibheader/0.1.0 diff --git a/tests/auto/blackbox/testdata-providers/conan-provider/main.cpp b/tests/auto/blackbox/testdata-providers/conan-provider/main.cpp new file mode 100644 index 000000000..6250927b3 --- /dev/null +++ b/tests/auto/blackbox/testdata-providers/conan-provider/main.cpp @@ -0,0 +1,8 @@ +#include <testlib.h> + +#include <header.h> + +int main() +{ + HelloWorld h(42 + hello()); +} diff --git a/tests/auto/blackbox/testdata-providers/conan-provider/testlib/CMakeLists.txt b/tests/auto/blackbox/testdata-providers/conan-provider/testlib/CMakeLists.txt new file mode 100644 index 000000000..d186d1906 --- /dev/null +++ b/tests/auto/blackbox/testdata-providers/conan-provider/testlib/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.15) +project(conanmoduleprovider.testlib) + +find_package(conanmoduleprovider.testlibdep REQUIRED) + +add_library(${PROJECT_NAME} STATIC testlib.cpp) +set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER "testlib.h") +target_link_libraries(${PROJECT_NAME} conanmoduleprovider.testlibdep::conanmoduleprovider.testlibdep) +install(TARGETS ${PROJECT_NAME}) diff --git a/tests/auto/blackbox/testdata-providers/conan-provider/testlib/conanfile.py b/tests/auto/blackbox/testdata-providers/conan-provider/testlib/conanfile.py new file mode 100644 index 000000000..7cb91f9ef --- /dev/null +++ b/tests/auto/blackbox/testdata-providers/conan-provider/testlib/conanfile.py @@ -0,0 +1,36 @@ +from conan import ConanFile +from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps +from conan.tools.files import collect_libs +import os + +class ConanModuleProviderTestlib(ConanFile): + name = "conanmoduleprovider.testlib" + license = "none" + version = "1.2.3" + + exports_sources = "*.cpp", "*.h", "CMakeLists.txt" + settings = "os", "compiler", "build_type", "arch" + + def requirements(self): + self.requires("conanmoduleprovider.testlibdep/1.2.3") + + def layout(self): + cmake_layout(self) + + def generate(self): + deps = CMakeDeps(self) + deps.generate() + tc = CMakeToolchain(self, generator="Ninja") + tc.generate() + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + cmake = CMake(self) + cmake.install() + + def package_info(self): + self.cpp_info.libs = collect_libs(self) diff --git a/tests/auto/blackbox/testdata-providers/conan-provider/testlib/testlib.cpp b/tests/auto/blackbox/testdata-providers/conan-provider/testlib/testlib.cpp new file mode 100644 index 000000000..7118dfda9 --- /dev/null +++ b/tests/auto/blackbox/testdata-providers/conan-provider/testlib/testlib.cpp @@ -0,0 +1,7 @@ +#include "testlib.h" + +#include <testlibdep.h> + +HelloWorld::HelloWorld(int x) + : m_x(foo(x)) +{} diff --git a/tests/auto/blackbox/testdata-providers/conan-provider/testlib/testlib.h b/tests/auto/blackbox/testdata-providers/conan-provider/testlib/testlib.h new file mode 100644 index 000000000..11a354b70 --- /dev/null +++ b/tests/auto/blackbox/testdata-providers/conan-provider/testlib/testlib.h @@ -0,0 +1,10 @@ +#pragma once + +class HelloWorld +{ +public: + explicit HelloWorld(int x); + +private: + int m_x; +}; diff --git a/tests/auto/blackbox/testdata-providers/conan-provider/testlibdep/CMakeLists.txt b/tests/auto/blackbox/testdata-providers/conan-provider/testlibdep/CMakeLists.txt new file mode 100644 index 000000000..5510a9250 --- /dev/null +++ b/tests/auto/blackbox/testdata-providers/conan-provider/testlibdep/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.15) +project(conanmoduleprovider.testlibdep) + +add_library(${PROJECT_NAME} STATIC testlibdep.cpp) +set_target_properties(${PROJECT_NAME} PROPERTIES PUBLIC_HEADER "testlibdep.h") +install(TARGETS ${PROJECT_NAME}) +install(FILES lorem_ipsum.txt DESTINATION share)
\ No newline at end of file diff --git a/tests/auto/blackbox/testdata-providers/conan-provider/testlibdep/conanfile.py b/tests/auto/blackbox/testdata-providers/conan-provider/testlibdep/conanfile.py new file mode 100644 index 000000000..f2c577c5e --- /dev/null +++ b/tests/auto/blackbox/testdata-providers/conan-provider/testlibdep/conanfile.py @@ -0,0 +1,35 @@ +from conan import ConanFile +from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps +from conan.tools.files import collect_libs, copy +import os + +class ConanModuleProviderTestlib(ConanFile): + name = "conanmoduleprovider.testlibdep" + license = "none" + version = "1.2.3" + + exports_sources = "*.cpp", "*.h", "*.txt" + settings = "os", "compiler", "build_type", "arch" + + def layout(self): + cmake_layout(self) + + def generate(self): + deps = CMakeDeps(self) + deps.generate() + tc = CMakeToolchain(self, generator="Ninja") + tc.generate() + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def package(self): + cmake = CMake(self) + cmake.install() + + def package_info(self): + self.cpp_info.libs = collect_libs(self) + self.cpp_info.resdirs = ['share'] + diff --git a/tests/auto/blackbox/testdata-providers/conan-provider/testlibdep/lorem_ipsum.txt b/tests/auto/blackbox/testdata-providers/conan-provider/testlibdep/lorem_ipsum.txt new file mode 100644 index 000000000..d8634396c --- /dev/null +++ b/tests/auto/blackbox/testdata-providers/conan-provider/testlibdep/lorem_ipsum.txt @@ -0,0 +1 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit.
\ No newline at end of file diff --git a/tests/auto/blackbox/testdata-providers/conan-provider/testlibdep/testlibdep.cpp b/tests/auto/blackbox/testdata-providers/conan-provider/testlibdep/testlibdep.cpp new file mode 100644 index 000000000..893478c82 --- /dev/null +++ b/tests/auto/blackbox/testdata-providers/conan-provider/testlibdep/testlibdep.cpp @@ -0,0 +1,6 @@ +#include "testlibdep.h" + +int foo(int i) +{ + return i * i; +}
\ No newline at end of file diff --git a/tests/auto/blackbox/testdata-providers/conan-provider/testlibdep/testlibdep.h b/tests/auto/blackbox/testdata-providers/conan-provider/testlibdep/testlibdep.h new file mode 100644 index 000000000..ca7bd3cbc --- /dev/null +++ b/tests/auto/blackbox/testdata-providers/conan-provider/testlibdep/testlibdep.h @@ -0,0 +1,3 @@ +#pragma once + +int foo(int i);
\ No newline at end of file diff --git a/tests/auto/blackbox/testdata-providers/conan-provider/testlibheader/conanfile.py b/tests/auto/blackbox/testdata-providers/conan-provider/testlibheader/conanfile.py new file mode 100644 index 000000000..6078b9750 --- /dev/null +++ b/tests/auto/blackbox/testdata-providers/conan-provider/testlibheader/conanfile.py @@ -0,0 +1,15 @@ +from conan import ConanFile +from conan.tools.files import copy + +import os + +class Recipe(ConanFile): + exports_sources = ("header.h") + version = '0.1.0' + name = 'conanmoduleprovider.testlibheader' + + def package(self): + copy(self, + "header.h", + src=self.source_folder, + dst=os.path.join(self.package_folder, "include"))
\ No newline at end of file diff --git a/tests/auto/blackbox/testdata-providers/conan-provider/testlibheader/header.h b/tests/auto/blackbox/testdata-providers/conan-provider/testlibheader/header.h new file mode 100644 index 000000000..66bd50d57 --- /dev/null +++ b/tests/auto/blackbox/testdata-providers/conan-provider/testlibheader/header.h @@ -0,0 +1,6 @@ +#pragma once + +inline int hello() +{ + return 0; +}
\ No newline at end of file diff --git a/tests/auto/blackbox/testdata-providers/qbs-module-providers/qbs-module-providers.qbs b/tests/auto/blackbox/testdata-providers/qbs-module-providers/qbs-module-providers.qbs index 00776a62e..7767c2cc5 100644 --- a/tests/auto/blackbox/testdata-providers/qbs-module-providers/qbs-module-providers.qbs +++ b/tests/auto/blackbox/testdata-providers/qbs-module-providers/qbs-module-providers.qbs @@ -1,10 +1,8 @@ Project { qbsModuleProviders: "provider_a" - property stringList wantedProviders: qbsModuleProviders name: "project" Project { name: "innerProject" - qbsModuleProviders: project.wantedProviders Product { name: "p1" Depends { name: "qbsmetatestmodule" } diff --git a/tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.qbs b/tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.qbs index 0d7c8a1bb..4674d6a6a 100644 --- a/tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.qbs +++ b/tests/auto/blackbox/testdata/capnproto/capnproto_absolute_import.qbs @@ -17,4 +17,5 @@ CppApplication { "capnproto_absolute_import.cpp", "imports/foo.capnp", ] + qbs.buildVariant: "release" } diff --git a/tests/auto/blackbox/testdata/capnproto/capnproto_cpp.qbs b/tests/auto/blackbox/testdata/capnproto/capnproto_cpp.qbs index c31824bb0..9f287e906 100644 --- a/tests/auto/blackbox/testdata/capnproto/capnproto_cpp.qbs +++ b/tests/auto/blackbox/testdata/capnproto/capnproto_cpp.qbs @@ -15,4 +15,5 @@ CppApplication { "capnproto_cpp.cpp", "foo.capnp" ] + qbs.buildVariant: "release" } diff --git a/tests/auto/blackbox/testdata/capnproto/capnproto_cpp_pkgconfig.qbs b/tests/auto/blackbox/testdata/capnproto/capnproto_cpp_pkgconfig.qbs deleted file mode 100644 index 90201e440..000000000 --- a/tests/auto/blackbox/testdata/capnproto/capnproto_cpp_pkgconfig.qbs +++ /dev/null @@ -1,19 +0,0 @@ -import qbs.Host - -CppApplication { - Depends { name: "capnproto.cpp"; required: false } - condition: { - var result = qbs.targetPlatform === Host.platform(); - if (!result) - console.info("targetPlatform differs from hostPlatform"); - if (!capnproto.cpp.present) - console.info("capnproto is not present"); - return result && capnproto.cpp.present; - } - cpp.minimumMacosVersion: "10.8" - files: [ - "capnproto_cpp.cpp", - "foo.capnp" - ] - qbsModuleProviders: "qbspkgconfig" -} diff --git a/tests/auto/blackbox/testdata/capnproto/conanfile.txt b/tests/auto/blackbox/testdata/capnproto/conanfile.txt new file mode 100644 index 000000000..7313bb82e --- /dev/null +++ b/tests/auto/blackbox/testdata/capnproto/conanfile.txt @@ -0,0 +1,6 @@ +[requires] +capnproto/1.0.2 +[tool_requires] +capnproto/1.0.2 +[generators] +QbsDeps diff --git a/tests/auto/blackbox/testdata/capnproto/greeter_cpp.qbs b/tests/auto/blackbox/testdata/capnproto/greeter_cpp.qbs index 7266e9e15..5fc5464b1 100644 --- a/tests/auto/blackbox/testdata/capnproto/greeter_cpp.qbs +++ b/tests/auto/blackbox/testdata/capnproto/greeter_cpp.qbs @@ -14,11 +14,14 @@ Project { name: "server" consoleApplication: true cpp.minimumMacosVersion: "10.8" + // workaround for broken capnproto + cpp.staticLibraries: qbs.targetOS.contains("windows") ? "Advapi32" : [] capnproto.cpp.useRpc: true files: [ "greeter.capnp", "greeter-server.cpp" ] + qbs.buildVariant: "release" } CppApplication { Depends { name: "capnproto.cpp"; required: false } @@ -26,9 +29,11 @@ Project { consoleApplication: true capnproto.cpp.useRpc: true cpp.minimumMacosVersion: "10.8" + cpp.staticLibraries: qbs.targetOS.contains("windows") ? "Advapi32" : [] files: [ "greeter.capnp", "greeter-client.cpp" ] + qbs.buildVariant: "release" } } diff --git a/tests/auto/blackbox/testdata/flatbuf/bar.fbs b/tests/auto/blackbox/testdata/flatbuf/bar.fbs new file mode 100644 index 000000000..47148f800 --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/bar.fbs @@ -0,0 +1,9 @@ +include "foo.fbs"; + +namespace QbsTest; + +table Bar { + foo:Foo; +} + +root_type Bar; diff --git a/tests/auto/blackbox/testdata/flatbuf/baz.fbs b/tests/auto/blackbox/testdata/flatbuf/baz.fbs new file mode 100644 index 000000000..312183710 --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/baz.fbs @@ -0,0 +1,9 @@ +include "imported_foo/imported_foo.fbs"; + +namespace QbsTest; + +table Baz { + foo:Foo; +} + +root_type Baz; diff --git a/tests/auto/blackbox/testdata/flatbuf/conanfile.txt b/tests/auto/blackbox/testdata/flatbuf/conanfile.txt new file mode 100644 index 000000000..188da5897 --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/conanfile.txt @@ -0,0 +1,6 @@ +[requires] +flatbuffers/24.3.25 +[tool_requires] +flatbuffers/24.3.25 +[generators] +QbsDeps diff --git a/tests/auto/blackbox/testdata/flatbuf/flat.c b/tests/auto/blackbox/testdata/flatbuf/flat.c new file mode 100644 index 000000000..55e25e556 --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/flat.c @@ -0,0 +1,36 @@ +#include "foo_builder.h" + +#include <stdio.h> + +#undef ns +#define ns(x) FLATBUFFERS_WRAP_NAMESPACE(QbsTest, x) // Specified in the schema. + +#define test_assert(x) do { if (!(x)) { assert(0); return -1; }} while (0) + +int main() +{ + void *buffer = NULL; + size_t size = 0; + + flatcc_builder_t builder; + flatcc_builder_init(&builder); + + flatbuffers_string_ref_t name = flatbuffers_string_create_str(&builder, "John Doe"); + + ns(Foo_create_as_root(&builder, name, 42)); + + buffer = flatcc_builder_finalize_aligned_buffer(&builder, &size); + + ns(Foo_table_t) foo = ns(Foo_as_root(buffer)); + + test_assert(strcmp(ns(Foo_name(foo)), "John Doe") == 0); + test_assert(ns(Foo_count(foo)) == 42); + + free(buffer); + + flatcc_builder_clear(&builder); + + printf("The FlatBuffer was successfully created and accessed!\n"); + + return 0; +} diff --git a/tests/auto/blackbox/testdata/flatbuf/flat.cpp b/tests/auto/blackbox/testdata/flatbuf/flat.cpp new file mode 100644 index 000000000..56332bacd --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/flat.cpp @@ -0,0 +1,22 @@ +#include "foo_generated.h" + +#include <iostream> + +using namespace QbsTest; + +int main() +{ + flatbuffers::FlatBufferBuilder builder; + auto name = builder.CreateString("John Doe"); + auto newFoo = QbsTest::CreateFoo(builder, name, 42); + builder.Finish(newFoo); + + auto foo = GetFoo(builder.GetBufferPointer()); + + assert(foo->name()->str() == "John Doe"); + assert(foo->count() == 42); + + std::cout << "The FlatBuffer was successfully created and accessed!" << std::endl; + + return 0; +} diff --git a/tests/auto/blackbox/testdata/flatbuf/flat_absolute_import.cpp b/tests/auto/blackbox/testdata/flatbuf/flat_absolute_import.cpp new file mode 100644 index 000000000..b7c43d8a5 --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/flat_absolute_import.cpp @@ -0,0 +1,25 @@ +#include "baz_generated.h" + +#include <iostream> + +using namespace QbsTest; + +int main() +{ + flatbuffers::FlatBufferBuilder builder; + + auto name = builder.CreateString("John Doe"); + auto newFoo = QbsTest::CreateFoo(builder, name, 42); + + auto newBaz = QbsTest::CreateBaz(builder, newFoo); + builder.Finish(newBaz); + + auto baz = GetBaz(builder.GetBufferPointer()); + + assert(baz->foo()->name()->str() == "John Doe"); + assert(baz->foo()->count() == 42); + + std::cout << "The FlatBuffer was successfully created and accessed!" << std::endl; + + return 0; +} diff --git a/tests/auto/blackbox/testdata/flatbuf/flat_absolute_import.qbs b/tests/auto/blackbox/testdata/flatbuf/flat_absolute_import.qbs new file mode 100644 index 000000000..888bfd4e3 --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/flat_absolute_import.qbs @@ -0,0 +1,24 @@ +CppApplication { + Depends { name: "flatbuf.cpp"; required: false } + + consoleApplication: true + condition: { + var result = qbs.targetPlatform === qbs.hostPlatform; + if (!result) + console.info("targetPlatform differs from hostPlatform"); + return result && hasFlatbuffers; + } + property bool hasFlatbuffers: { + console.info("has flatbuffers: " + flatbuf.cpp.present); + return flatbuf.cpp.present; + } + + flatbuf.cpp.importPaths: "imports/" + + files: [ + "flat_absolute_import.cpp", + "baz.fbs", + "imports/imported_foo/imported_foo.fbs", + ] + qbsModuleProviders: "conan" +} diff --git a/tests/auto/blackbox/testdata/flatbuf/flat_c.qbs b/tests/auto/blackbox/testdata/flatbuf/flat_c.qbs new file mode 100644 index 000000000..6d365252e --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/flat_c.qbs @@ -0,0 +1,21 @@ +CppApplication { + Depends { name: "flatbuffers.c"; required: false } + + consoleApplication: true + condition: { + var result = qbs.targetPlatform === qbs.hostPlatform; + if (!result) + console.info("targetPlatform differs from hostPlatform"); + return result && hasFlatbuffers; + } + property bool hasFlatbuffers: { + console.info("has flatbuffers: " + flatbuffers.c.present); + return flatbuffers.c.present; + } + + files: [ + "flat.c", + "foo.fbs", + ] + qbsModuleProviders: "conan" +} diff --git a/tests/auto/blackbox/testdata/flatbuf/flat_cpp.qbs b/tests/auto/blackbox/testdata/flatbuf/flat_cpp.qbs new file mode 100644 index 000000000..99e89a5fa --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/flat_cpp.qbs @@ -0,0 +1,21 @@ +CppApplication { + Depends { name: "flatbuf.cpp"; required: false } + + consoleApplication: true + condition: { + var result = qbs.targetPlatform === qbs.hostPlatform; + if (!result) + console.info("targetPlatform differs from hostPlatform"); + return result && hasFlatbuffers; + } + property bool hasFlatbuffers: { + console.info("has flatbuffers: " + flatbuf.cpp.present); + return flatbuf.cpp.present; + } + + files: [ + "flat.cpp", + "foo.fbs", + ] + qbsModuleProviders: "conan" +} diff --git a/tests/auto/blackbox/testdata/flatbuf/flat_filename_extension.cpp b/tests/auto/blackbox/testdata/flatbuf/flat_filename_extension.cpp new file mode 100644 index 000000000..77ed64acd --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/flat_filename_extension.cpp @@ -0,0 +1,22 @@ +#include "foo_generated.hpp" + +#include <iostream> + +using namespace QbsTest; + +int main() +{ + flatbuffers::FlatBufferBuilder builder; + auto name = builder.CreateString("John Doe"); + auto newFoo = QbsTest::CreateFoo(builder, name, 42); + builder.Finish(newFoo); + + auto foo = GetFoo(builder.GetBufferPointer()); + + assert(foo->name()->str() == "John Doe"); + assert(foo->count() == 42); + + std::cout << "The FlatBuffer was successfully created and accessed!" << std::endl; + + return 0; +} diff --git a/tests/auto/blackbox/testdata/flatbuf/flat_filename_extension.qbs b/tests/auto/blackbox/testdata/flatbuf/flat_filename_extension.qbs new file mode 100644 index 000000000..31eec7629 --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/flat_filename_extension.qbs @@ -0,0 +1,23 @@ +CppApplication { + Depends { name: "flatbuf.cpp"; required: false } + + consoleApplication: true + condition: { + var result = qbs.targetPlatform === qbs.hostPlatform; + if (!result) + console.info("targetPlatform differs from hostPlatform"); + return result && hasFlatbuffers; + } + property bool hasFlatbuffers: { + console.info("has flatbuffers: " + flatbuf.cpp.present); + return flatbuf.cpp.present; + } + + flatbuf.cpp.filenameExtension: "hpp" + + files: [ + "flat_filename_extension.cpp", + "foo.fbs", + ] + qbsModuleProviders: "conan" +} diff --git a/tests/auto/blackbox/testdata/flatbuf/flat_filename_suffix.cpp b/tests/auto/blackbox/testdata/flatbuf/flat_filename_suffix.cpp new file mode 100644 index 000000000..630e4aaef --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/flat_filename_suffix.cpp @@ -0,0 +1,22 @@ +#include "foo.fbs.h" + +#include <iostream> + +using namespace QbsTest; + +int main() +{ + flatbuffers::FlatBufferBuilder builder; + auto name = builder.CreateString("John Doe"); + auto newFoo = QbsTest::CreateFoo(builder, name, 42); + builder.Finish(newFoo); + + auto foo = GetFoo(builder.GetBufferPointer()); + + assert(foo->name()->str() == "John Doe"); + assert(foo->count() == 42); + + std::cout << "The FlatBuffer was successfully created and accessed!" << std::endl; + + return 0; +} diff --git a/tests/auto/blackbox/testdata/flatbuf/flat_filename_suffix.qbs b/tests/auto/blackbox/testdata/flatbuf/flat_filename_suffix.qbs new file mode 100644 index 000000000..5103b041f --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/flat_filename_suffix.qbs @@ -0,0 +1,23 @@ +CppApplication { + Depends { name: "flatbuf.cpp"; required: false } + + consoleApplication: true + condition: { + var result = qbs.targetPlatform === qbs.hostPlatform; + if (!result) + console.info("targetPlatform differs from hostPlatform"); + return result && hasFlatbuffers; + } + property bool hasFlatbuffers: { + console.info("has flatbuffers: " + flatbuf.cpp.present); + return flatbuf.cpp.present; + } + + flatbuf.cpp.filenameSuffix: ".fbs" + + files: [ + "flat_filename_suffix.cpp", + "foo.fbs", + ] + qbsModuleProviders: "conan" +} diff --git a/tests/auto/blackbox/testdata/flatbuf/flat_keep_prefix.cpp b/tests/auto/blackbox/testdata/flatbuf/flat_keep_prefix.cpp new file mode 100644 index 000000000..5f4b55e96 --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/flat_keep_prefix.cpp @@ -0,0 +1,26 @@ +#include "baz_generated.h" +#include "imported_foo/imported_foo_generated.h" + +#include <iostream> + +using namespace QbsTest; + +int main() +{ + flatbuffers::FlatBufferBuilder builder; + + auto name = builder.CreateString("John Doe"); + auto newFoo = QbsTest::CreateFoo(builder, name, 42); + + auto newBaz = QbsTest::CreateBaz(builder, newFoo); + builder.Finish(newBaz); + + auto baz = GetBaz(builder.GetBufferPointer()); + + assert(baz->foo()->name()->str() == "John Doe"); + assert(baz->foo()->count() == 42); + + std::cout << "The FlatBuffer was successfully created and accessed!" << std::endl; + + return 0; +} diff --git a/tests/auto/blackbox/testdata/flatbuf/flat_keep_prefix.qbs b/tests/auto/blackbox/testdata/flatbuf/flat_keep_prefix.qbs new file mode 100644 index 000000000..0ea0d1dac --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/flat_keep_prefix.qbs @@ -0,0 +1,25 @@ +CppApplication { + Depends { name: "flatbuf.cpp"; required: false } + + consoleApplication: true + condition: { + var result = qbs.targetPlatform === qbs.hostPlatform; + if (!result) + console.info("targetPlatform differs from hostPlatform"); + return result && hasFlatbuffers; + } + property bool hasFlatbuffers: { + console.info("has flatbuffers: " + flatbuf.cpp.present); + return flatbuf.cpp.present; + } + + flatbuf.cpp.importPaths: "imports/" + flatbuf.cpp.keepPrefix: true + + files: [ + "flat_keep_prefix.cpp", + "baz.fbs", + "imports/imported_foo/imported_foo.fbs", + ] + qbsModuleProviders: "conan" +} diff --git a/tests/auto/blackbox/testdata/flatbuf/flat_relative_import.cpp b/tests/auto/blackbox/testdata/flatbuf/flat_relative_import.cpp new file mode 100644 index 000000000..bec6dadfd --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/flat_relative_import.cpp @@ -0,0 +1,25 @@ +#include "bar_generated.h" + +#include <iostream> + +using namespace QbsTest; + +int main() +{ + flatbuffers::FlatBufferBuilder builder; + + auto name = builder.CreateString("John Doe"); + auto newFoo = QbsTest::CreateFoo(builder, name, 42); + + auto newBar = QbsTest::CreateBar(builder, newFoo); + builder.Finish(newBar); + + auto bar = GetBar(builder.GetBufferPointer()); + + assert(bar->foo()->name()->str() == "John Doe"); + assert(bar->foo()->count() == 42); + + std::cout << "The FlatBuffer was successfully created and accessed!" << std::endl; + + return 0; +} diff --git a/tests/auto/blackbox/testdata/flatbuf/flat_relative_import.qbs b/tests/auto/blackbox/testdata/flatbuf/flat_relative_import.qbs new file mode 100644 index 000000000..f5a2c5d0b --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/flat_relative_import.qbs @@ -0,0 +1,22 @@ +CppApplication { + Depends { name: "flatbuf.cpp"; required: false } + + consoleApplication: true + condition: { + var result = qbs.targetPlatform === qbs.hostPlatform; + if (!result) + console.info("targetPlatform differs from hostPlatform"); + return result && hasFlatbuffers; + } + property bool hasFlatbuffers: { + console.info("has flatbuffers: " + flatbuf.cpp.present); + return flatbuf.cpp.present; + } + + files: [ + "flat_relative_import.cpp", + "bar.fbs", + "foo.fbs", + ] + qbsModuleProviders: "conan" +} diff --git a/tests/auto/blackbox/testdata/flatbuf/foo.fbs b/tests/auto/blackbox/testdata/flatbuf/foo.fbs new file mode 100644 index 000000000..dff3b488f --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/foo.fbs @@ -0,0 +1,8 @@ +namespace QbsTest; + +table Foo { + name:string; + count:int; +} + +root_type Foo; diff --git a/tests/auto/blackbox/testdata/flatbuf/imports/imported_foo/imported_foo.fbs b/tests/auto/blackbox/testdata/flatbuf/imports/imported_foo/imported_foo.fbs new file mode 100644 index 000000000..dff3b488f --- /dev/null +++ b/tests/auto/blackbox/testdata/flatbuf/imports/imported_foo/imported_foo.fbs @@ -0,0 +1,8 @@ +namespace QbsTest; + +table Foo { + name:string; + count:int; +} + +root_type Foo; diff --git a/tests/auto/blackbox/testdata/grpc/conanfile.txt b/tests/auto/blackbox/testdata/grpc/conanfile.txt new file mode 100644 index 000000000..f88e6e8d6 --- /dev/null +++ b/tests/auto/blackbox/testdata/grpc/conanfile.txt @@ -0,0 +1,7 @@ +[requires] +grpc/1.54.3 +[tool_requires] +protobuf/3.21.12 +grpc/1.54.3 +[generators] +QbsDeps diff --git a/tests/auto/blackbox/testdata/grpc/grpc_cpp.qbs b/tests/auto/blackbox/testdata/grpc/grpc_cpp.qbs index e36ae47ca..d1bdd5d60 100644 --- a/tests/auto/blackbox/testdata/grpc/grpc_cpp.qbs +++ b/tests/auto/blackbox/testdata/grpc/grpc_cpp.qbs @@ -14,6 +14,7 @@ CppApplication { cpp.cxxLanguageVersion: "c++17" cpp.minimumMacosVersion: "10.15" cpp.warningLevel: "none" + qbs.buildVariant: "release" Depends { name: "protobuf.cpp"; required: false } Depends { name: "grpc++"; id: grpcpp; required: false } @@ -22,7 +23,7 @@ CppApplication { property bool hasDependencies: { console.info("has grpc: " + protobuf.cpp.present); console.info("has modules: " + grpcpp.present); - return protobuf.cpp.present; + return protobuf.cpp.present && grpcpp.present; } files: "grpc.cpp" diff --git a/tests/auto/blackbox/testdata/linkerMode/darwin.s b/tests/auto/blackbox/testdata/linkerMode/darwin.s new file mode 100644 index 000000000..fb165114b --- /dev/null +++ b/tests/auto/blackbox/testdata/linkerMode/darwin.s @@ -0,0 +1,6 @@ +.globl _main +.globl main + +_main: +main: + ret diff --git a/tests/auto/blackbox/testdata/linkerMode/linkerMode.qbs b/tests/auto/blackbox/testdata/linkerMode/linkerMode.qbs index 9934efb04..176730ce0 100644 --- a/tests/auto/blackbox/testdata/linkerMode/linkerMode.qbs +++ b/tests/auto/blackbox/testdata/linkerMode/linkerMode.qbs @@ -2,7 +2,7 @@ Project { CppApplication { consoleApplication: true name: "LinkedProduct-Assembly" - files: ["main.s"] + files: qbs.targetOS.includes("darwin") ? "darwin.s" : "main.s" cpp.linkerPath: cpp.compilerPathByLanguage["c"] diff --git a/tests/auto/blackbox/testdata/linkerMode/main.s b/tests/auto/blackbox/testdata/linkerMode/main.s index fb165114b..ef88f8c52 100644 --- a/tests/auto/blackbox/testdata/linkerMode/main.s +++ b/tests/auto/blackbox/testdata/linkerMode/main.s @@ -3,4 +3,3 @@ _main: main: - ret diff --git a/tests/auto/blackbox/testdata/protobuf/conanfile.txt b/tests/auto/blackbox/testdata/protobuf/conanfile.txt new file mode 100644 index 000000000..e7d849b1a --- /dev/null +++ b/tests/auto/blackbox/testdata/protobuf/conanfile.txt @@ -0,0 +1,6 @@ +[requires] +protobuf/3.21.12 +[tool_requires] +protobuf/3.21.12 +[generators] +QbsDeps diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index 8497b008b..dbabfb311 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -144,6 +144,26 @@ QString TestBlackbox::findArchiver(const QString &fileName, int *status) return binary; } +bool TestBlackbox::prepareAndRunConan() +{ + QString executable = findExecutable({"conan"}); + if (executable.isEmpty()) { + qInfo() << "conan is not installed or not available in PATH."; + return false; + } + const auto profilePath = QDir::homePath() + "/.conan2/profiles/qbs-test"; + if (!QFileInfo(profilePath).exists()) { + qInfo() << "conan profile is not installed, run './scripts/setup-conan-profiles.sh'"; + return false; + } + QProcess conan; + QDir::setCurrent(testDataDir + "/conan-provider/testlibdep"); + rmDirR("build"); + QStringList arguments{"install", ".", "--profile:all=qbs-test", "--output-folder=build"}; + conan.start(executable, arguments); + return waitForProcessSuccess(conan, 60000); +} + bool TestBlackbox::lexYaccExist() { return !findExecutable(QStringList("lex")).isEmpty() @@ -707,6 +727,45 @@ void TestBlackbox::buildDirectories() QVERIFY2(outputLines.contains(projectDir), m_qbsStdout.constData()); } +void TestBlackbox::buildDirPlaceholders_data() +{ + QTest::addColumn<QString>("buildDir"); + QTest::addColumn<bool>("setProjectFile"); + QTest::addColumn<bool>("successExpected"); + + QTest::newRow("normal dir, with project file") << "somedir" << true << true; + QTest::newRow("normal dir, without project file") << "somedir" << false << true; + QTest::newRow("@project, with project file") << "somedir/@project" << true << true; + QTest::newRow("@project, without project file") << "somedir/@project" << false << false; + QTest::newRow("@path, with project file") << "somedir/@path" << true << true; + QTest::newRow("@path, without project file") << "somedir/@path" << false << false; +} + +void TestBlackbox::buildDirPlaceholders() +{ + QFETCH(QString, buildDir); + QFETCH(bool, setProjectFile); + QFETCH(bool, successExpected); + + const QString projectDir = testDataDir + "/build-dir-placeholders"; + rmDirR(projectDir); + QVERIFY(QDir().mkpath(projectDir)); + QDir::setCurrent(projectDir); + QFile projectFile("build-dir-placeholders.qbs"); + QVERIFY(projectFile.open(QIODevice::WriteOnly)); + projectFile.write("Product {\n}\n"); + projectFile.flush(); + rmDirR(relativeBuildDir()); + QbsRunParameters params; + params.buildDirectory = buildDir; + if (setProjectFile) { + params.arguments << "-f" + << "build-dir-placeholders.qbs"; + } + params.expectFailure = !successExpected; + QCOMPARE(runQbs(params) == 0, successExpected); +} + void TestBlackbox::buildEnvChange() { QDir::setCurrent(testDataDir + "/buildenv-change"); @@ -793,28 +852,45 @@ void TestBlackbox::buildVariantDefaults() void TestBlackbox::capnproto() { QFETCH(QString, projectFile); + QFETCH(QStringList, arguments); QDir::setCurrent(testDataDir + "/capnproto"); rmDirR(relativeBuildDir()); + if (QTest::currentDataTag() == QLatin1String("cpp-conan") + || QTest::currentDataTag() == QLatin1String("rpc-conan")) { + if (!prepareAndRunConan()) + QSKIP("conan is not prepared, check messages above"); + } + QbsRunParameters params{QStringLiteral("resolve"), {QStringLiteral("-f"), projectFile}}; + params.arguments << arguments; + QCOMPARE(runQbs(params), 0); if (m_qbsStdout.contains("targetPlatform differs from hostPlatform")) QSKIP("Cannot run binaries in cross-compiled build"); if (m_qbsStdout.contains("capnproto is not present")) QSKIP("capnproto is not present"); - params.command.clear(); + params.command = QStringLiteral("build"); QCOMPARE(runQbs(params), 0); } void TestBlackbox::capnproto_data() { QTest::addColumn<QString>("projectFile"); + QTest::addColumn<QStringList>("arguments"); - QTest::newRow("cpp") << QStringLiteral("capnproto_cpp.qbs"); - QTest::newRow("cpp-pkgconfig") << QStringLiteral("capnproto_cpp_pkgconfig.qbs"); - QTest::newRow("greeter cpp (grpc)") << QStringLiteral("greeter_cpp.qbs"); - QTest::newRow("relative import") << QStringLiteral("capnproto_relative_import.qbs"); - QTest::newRow("absolute import") << QStringLiteral("capnproto_absolute_import.qbs"); + QStringList pkgConfigArgs({"project.qbsModuleProviders:qbspkgconfig"}); + QTest::newRow("cpp-pkgconfig") << QStringLiteral("capnproto_cpp.qbs") << pkgConfigArgs; + QTest::newRow("rpc-pkgconfig") << QStringLiteral("greeter_cpp.qbs") << pkgConfigArgs; + QTest::newRow("relative import") + << QStringLiteral("capnproto_relative_import.qbs") << pkgConfigArgs; + QTest::newRow("absolute import") + << QStringLiteral("capnproto_absolute_import.qbs") << pkgConfigArgs; + + QStringList conanArgs( + {"project.qbsModuleProviders:conan", "moduleProviders.conan.installDirectory:build"}); + QTest::newRow("cpp-conan") << QStringLiteral("capnproto_cpp.qbs") << conanArgs; + QTest::newRow("rpc-conan") << QStringLiteral("greeter_cpp.qbs") << conanArgs; } void TestBlackbox::changedFiles_data() @@ -1386,19 +1462,6 @@ void TestBlackbox::variantSuffix_data() std::make_pair(QString("unix"), QStringList())}); } -static bool waitForProcessSuccess(QProcess &p, int msecs = 30000) -{ - if (!p.waitForStarted(msecs) || !p.waitForFinished(msecs)) { - qDebug() << p.errorString(); - return false; - } - if (p.exitCode() != 0) { - qDebug() << p.readAllStandardError(); - return false; - } - return true; -} - void TestBlackbox::vcsGit() { const QString gitFilePath = findExecutable(QStringList("git")); @@ -1925,6 +1988,9 @@ void TestBlackbox::conanfileProbe() { QFETCH(bool, forceFailure); + if (qEnvironmentVariableIsSet("GITHUB_ACTIONS")) + QSKIP("Skip this test when running on GitHub"); + QString executable = findExecutable({"conan"}); if (executable.isEmpty()) QSKIP("conan is not installed or not available in PATH."); @@ -4343,6 +4409,43 @@ void TestBlackbox::fileTagsFilterMerging() QVERIFY2(QFile::exists(otherOutput), qPrintable(otherOutput)); } +void TestBlackbox::flatbuf() +{ + QFETCH(QString, projectFile); + + QDir::setCurrent(testDataDir + "/flatbuf"); + + rmDirR(relativeBuildDir()); + if (!prepareAndRunConan()) + QSKIP("conan is not prepared, check messages above"); + + QbsRunParameters resolveParams( + "resolve", QStringList{"-f", projectFile, "moduleProviders.conan.installDirectory:build"}); + QCOMPARE(runQbs(resolveParams), 0); + if (m_qbsStdout.contains("targetPlatform differs from hostPlatform")) + QSKIP("Cannot run binaries in cross-compiled build"); + const bool withFlatbuffers = m_qbsStdout.contains("has flatbuffers: true"); + const bool withoutFlatbuffers = m_qbsStdout.contains("has flatbuffers: false"); + QVERIFY2(withFlatbuffers || withoutFlatbuffers, m_qbsStdout.constData()); + if (withoutFlatbuffers) + QSKIP("flatbuf module not present"); + QbsRunParameters runParams("run"); + QCOMPARE(runQbs(runParams), 0); +} + +void TestBlackbox::flatbuf_data() +{ + QTest::addColumn<QString>("projectFile"); + + // QTest::newRow("c") << QString("flat_c.qbs"); + QTest::newRow("cpp") << QString("flat_cpp.qbs"); + QTest::newRow("relative import") << QString("flat_relative_import.qbs"); + QTest::newRow("absolute import") << QString("flat_absolute_import.qbs"); + QTest::newRow("filename suffix") << QString("flat_filename_suffix.qbs"); + QTest::newRow("filename extension") << QString("flat_filename_extension.qbs"); + QTest::newRow("keep prefix") << QString("flat_keep_prefix.qbs"); +} + void TestBlackbox::freedesktop() { if (!HostOsInfo::isAnyUnixHost()) @@ -6088,6 +6191,12 @@ void TestBlackbox::protobuf_data() QTest::newRow("cpp-pkgconfig") << QString("addressbook_cpp.qbs") << QStringList({"project.qbsModuleProviders:qbspkgconfig"}) << true << true; + QTest::newRow("cpp-conan") << QString("addressbook_cpp.qbs") + << QStringList( + {"project.qbsModuleProviders:conan", + "qbs.buildVariant:release", + "moduleProviders.conan.installDirectory:build"}) + << true << true; QTest::newRow("objc") << QString("addressbook_objc.qbs") << QStringList() << false << true; QTest::newRow("nanopb") << QString("addressbook_nanopb.qbs") << QStringList() << false << true; QTest::newRow("import") << QString("import.qbs") << QStringList() << true << true; @@ -6108,6 +6217,12 @@ void TestBlackbox::protobuf() QFETCH(bool, hasModules); QFETCH(bool, successExpected); rmDirR(relativeBuildDir()); + + if (QTest::currentDataTag() == QLatin1String("cpp-conan")) { + if (!prepareAndRunConan()) + QSKIP("conan is not prepared, check messages above"); + } + QbsRunParameters resolveParams("resolve", QStringList{"-f", projectFile} << properties); QCOMPARE(runQbs(resolveParams), 0); if (m_qbsStdout.contains("targetPlatform differs from hostPlatform")) @@ -7558,6 +7673,7 @@ void TestBlackbox::autotestTimeout() { QFETCH(QStringList, resolveParams); QFETCH(bool, expectFailure); + QFETCH(QString, errorDetails); QDir::setCurrent(testDataDir + "/autotest-timeout"); QbsRunParameters resolveParameters("resolve", resolveParams); QCOMPARE(runQbs(resolveParameters), 0); @@ -7567,7 +7683,9 @@ void TestBlackbox::autotestTimeout() buildParameters.expectFailure = expectFailure; if (expectFailure) { QVERIFY(runQbs(buildParameters) != 0); - QVERIFY(m_qbsStderr.contains("cancelled") && m_qbsStderr.contains("timeout")); + QVERIFY( + m_qbsStderr.contains("cancelled") && m_qbsStderr.contains("timeout") + && m_qbsStderr.contains(errorDetails.toLocal8Bit())); } else QVERIFY(runQbs(buildParameters) == 0); @@ -7577,11 +7695,12 @@ void TestBlackbox::autotestTimeout_data() { QTest::addColumn<QStringList>("resolveParams"); QTest::addColumn<bool>("expectFailure"); - QTest::newRow("no timeout") << QStringList() << false; - QTest::newRow("timeout on test") << QStringList({"products.testApp.autotest.timeout:2"}) - << true; - QTest::newRow("timeout on runner") << QStringList({"products.autotest-runner.timeout:2"}) - << true; + QTest::addColumn<QString>("errorDetails"); + QTest::newRow("no timeout") << QStringList() << false << QString(); + QTest::newRow("timeout on test") + << QStringList({"products.testApp.autotest.timeout:2"}) << true << QString("testApp"); + QTest::newRow("timeout on runner") + << QStringList({"products.autotest-runner.timeout:2"}) << true << QString("testApp"); } void TestBlackbox::autotests_data() @@ -8663,6 +8782,9 @@ void TestBlackbox::grpc_data() << "moduleProviders.qbspkgconfig.extraPaths:/usr/local/opt/openssl@1.1/lib/pkgconfig"; } QTest::newRow("cpp-pkgconfig") << QString("grpc_cpp.qbs") << pkgConfigArgs << true; + QStringList conanArgs( + {"project.qbsModuleProviders:conan", "moduleProviders.conan.installDirectory:build"}); + QTest::newRow("cpp-conan") << QString("grpc_cpp.qbs") << conanArgs << true; } void TestBlackbox::grpc() @@ -8673,14 +8795,12 @@ void TestBlackbox::grpc() QFETCH(bool, hasModules); rmDirR(relativeBuildDir()); - QbsRunParameters resolveParams("resolve", QStringList{"-f", projectFile}); - if (QTest::currentDataTag() == QLatin1String("cpp")) { - if (const QString extraLibs = qEnvironmentVariable("QBS_EXTRA_GRPC_LIBS"); - !extraLibs.isEmpty()) { - resolveParams.arguments << (QLatin1String("modules.protobuf.cpp._extraGrpcLibs:") - + extraLibs); - } + if (QTest::currentDataTag() == QLatin1String("cpp-conan")) { + if (!prepareAndRunConan()) + QSKIP("conan is not prepared, check messages above"); } + + QbsRunParameters resolveParams("resolve", QStringList{"-f", projectFile}); resolveParams.arguments << arguments; QCOMPARE(runQbs(resolveParams), 0); const bool withGrpc = m_qbsStdout.contains("has grpc: true"); diff --git a/tests/auto/blackbox/tst_blackbox.h b/tests/auto/blackbox/tst_blackbox.h index d4c62c67c..3f817b481 100644 --- a/tests/auto/blackbox/tst_blackbox.h +++ b/tests/auto/blackbox/tst_blackbox.h @@ -60,6 +60,8 @@ private slots: void bomSources(); void buildDataOfDisabledProduct(); void buildDirectories(); + void buildDirPlaceholders_data(); + void buildDirPlaceholders(); void buildEnvChange(); void buildGraphVersions(); void buildVariantDefaults_data(); @@ -133,6 +135,8 @@ private slots: void externalLibs(); void fileDependencies(); void fileTagsFilterMerging(); + void flatbuf(); + void flatbuf_data(); void freedesktop(); void generatedArtifactAsInputToDynamicRule(); void generateLinkerMapFile(); @@ -361,6 +365,7 @@ private: QMap<QString, QString> findNodejs(int *status); QMap<QString, QString> findTypeScript(int *status); QString findArchiver(const QString &fileName, int *status = nullptr); + bool prepareAndRunConan(); static bool lexYaccExist(); static qbs::Version bisonVersion(); }; diff --git a/tests/auto/blackbox/tst_blackboxbase.cpp b/tests/auto/blackbox/tst_blackboxbase.cpp index 17652e779..317a6b663 100644 --- a/tests/auto/blackbox/tst_blackboxbase.cpp +++ b/tests/auto/blackbox/tst_blackboxbase.cpp @@ -274,3 +274,16 @@ qbs::Version TestBlackboxBase::qmakeVersion(const QString &qmakeFilePath) qDebug() << "qmake '" << qmakeFilePath << "' version is not valid."; return version; } + +bool waitForProcessSuccess(QProcess &p, int msecs) +{ + if (!p.waitForStarted(msecs) || !p.waitForFinished(msecs)) { + qDebug() << p.errorString(); + return false; + } + if (p.exitCode() != 0) { + qDebug() << p.readAllStandardError(); + return false; + } + return true; +} diff --git a/tests/auto/blackbox/tst_blackboxbase.h b/tests/auto/blackbox/tst_blackboxbase.h index d020b7cd9..5733cacd7 100644 --- a/tests/auto/blackbox/tst_blackboxbase.h +++ b/tests/auto/blackbox/tst_blackboxbase.h @@ -90,6 +90,8 @@ public: public slots: virtual void initTestCase(); + static QString findExecutable(const QStringList &fileNames); + protected: virtual void validateTestProfile(); @@ -99,7 +101,6 @@ protected: static QByteArray unifiedLineEndings(const QByteArray &ba); static void sanitizeOutput(QByteArray *ba); static void ccp(const QString &sourceDirPath, const QString &targetDirPath); - static QString findExecutable(const QStringList &fileNames); QMap<QString, QString> findJdkTools(int *status); static qbs::Version qmakeVersion(const QString &qmakeFilePath); @@ -113,4 +114,6 @@ protected: int m_needsQt = false; }; +bool waitForProcessSuccess(QProcess &p, int msecs = 30000); + #endif // TST_BLACKBOXBASE_H diff --git a/tests/auto/blackbox/tst_blackboxexamples.cpp b/tests/auto/blackbox/tst_blackboxexamples.cpp index b6a26b7f3..13ccb7796 100644 --- a/tests/auto/blackbox/tst_blackboxexamples.cpp +++ b/tests/auto/blackbox/tst_blackboxexamples.cpp @@ -82,6 +82,7 @@ void TestBlackboxExamples::examples_data() auto examples = collectExamples(testDataDir); examples.append(collectExamples(testDataDir + "/protobuf")); + examples.append(collectExamples(testDataDir + "/flatbuffers")); std::sort(examples.begin(), examples.end()); for (const auto &example: examples) { diff --git a/tests/auto/blackbox/tst_blackboxproviders.cpp b/tests/auto/blackbox/tst_blackboxproviders.cpp index 3d64d32d5..cf6594229 100644 --- a/tests/auto/blackbox/tst_blackboxproviders.cpp +++ b/tests/auto/blackbox/tst_blackboxproviders.cpp @@ -87,6 +87,72 @@ void TestBlackboxProviders::brokenProvider() QCOMPARE(m_qbsStderr.count("This provider is broken"), 2); } +void TestBlackboxProviders::conanProvider() +{ + QFETCH(bool, generateConanFiles); + QFETCH(bool, successExpected); + + const auto executable = findExecutable({"conan"}); + if (executable.isEmpty()) + QSKIP("conan is not installed or not available in PATH."); + + const auto generator = QDir::homePath() + "/.conan2/extensions/generators/qbsdeps.py"; + if (!QFileInfo(generator).exists()) { + QSKIP( + "qbsdeps.py is not installed, call 'conan config install src/conan/ from qbs source'."); + } + + const auto profilePath = QDir::homePath() + "/.conan2/profiles/qbs-test"; + if (!QFileInfo(profilePath).exists()) + QSKIP("conan profile is not installed, run './scripts/setup-conan-profiles.sh'."); + + // install testlibdep first + QProcess conan; + QDir::setCurrent(testDataDir + "/conan-provider/testlibdep"); + conan.start(executable, {"create", ".", "--profile:all=qbs-test"}); + QVERIFY(waitForProcessSuccess(conan)); + + // install testlib second + QDir::setCurrent(testDataDir + "/conan-provider/testlib"); + conan.start(executable, {"create", ".", "--profile:all=qbs-test"}); + QVERIFY(waitForProcessSuccess(conan)); + + // install header lib third + QDir::setCurrent(testDataDir + "/conan-provider/testlibheader"); + conan.start(executable, {"create", ".", "--profile:all=qbs-test"}); + QVERIFY(waitForProcessSuccess(conan)); + + // now build an app using those libs + QDir::setCurrent(testDataDir + "/conan-provider"); + + rmDirR(relativeBuildDir()); + rmDirR("build"); + + if (generateConanFiles) { + QStringList arguments{ + "install", ".", "-g=QbsDeps", "--profile:all=qbs-test", "--output-folder=build"}; + QProcess conan; + conan.start(executable, arguments); + QVERIFY(waitForProcessSuccess(conan)); + } + + QbsRunParameters buildParams( + "build", + {"--force-probe-execution", + "moduleProviders.conan.installDirectory:" + QDir::currentPath() + "/build"}); + buildParams.expectFailure = !successExpected; + QCOMPARE(runQbs(buildParams) == 0, successExpected); +} + +void TestBlackboxProviders::conanProvider_data() +{ + QTest::addColumn<bool>("generateConanFiles"); + QTest::addColumn<bool>("successExpected"); + + QTest::addRow("no conan files generated") << false << false; + QTest::addRow("conan files generated") << true << true; +} + void TestBlackboxProviders::moduleProviders() { QDir::setCurrent(testDataDir + "/module-providers"); diff --git a/tests/auto/blackbox/tst_blackboxproviders.h b/tests/auto/blackbox/tst_blackboxproviders.h index dd0039013..088cea6a3 100644 --- a/tests/auto/blackbox/tst_blackboxproviders.h +++ b/tests/auto/blackbox/tst_blackboxproviders.h @@ -44,6 +44,8 @@ private slots: void allowedValues(); void allowedValues_data(); void brokenProvider(); + void conanProvider(); + void conanProvider_data(); void moduleProviders(); void moduleProvidersCache(); void nonEagerModuleProvider(); diff --git a/tests/auto/language/testdata/erroneous/modules/recursion_helper/recursion_helper.qbs b/tests/auto/language/testdata/erroneous/modules/recursion_helper/recursion_helper.qbs new file mode 100644 index 000000000..22436a4aa --- /dev/null +++ b/tests/auto/language/testdata/erroneous/modules/recursion_helper/recursion_helper.qbs @@ -0,0 +1,3 @@ +Module { + property bool a: product.a +} diff --git a/tests/auto/language/testdata/erroneous/recursive-property-direct.qbs b/tests/auto/language/testdata/erroneous/recursive-property-direct.qbs new file mode 100644 index 000000000..ad14e7618 --- /dev/null +++ b/tests/auto/language/testdata/erroneous/recursive-property-direct.qbs @@ -0,0 +1,3 @@ +Product { + property bool a: a +} diff --git a/tests/auto/language/testdata/erroneous/recursive-property-indirect-via-module.qbs b/tests/auto/language/testdata/erroneous/recursive-property-indirect-via-module.qbs new file mode 100644 index 000000000..fc3ed4f88 --- /dev/null +++ b/tests/auto/language/testdata/erroneous/recursive-property-indirect-via-module.qbs @@ -0,0 +1,4 @@ +Product { + Depends { name: "recursion_helper" } + property bool a: recursion_helper.a +} diff --git a/tests/auto/language/testdata/erroneous/recursive-property-indirect.qbs b/tests/auto/language/testdata/erroneous/recursive-property-indirect.qbs new file mode 100644 index 000000000..6fd76e1ae --- /dev/null +++ b/tests/auto/language/testdata/erroneous/recursive-property-indirect.qbs @@ -0,0 +1,5 @@ +Product { + property bool a: b + property bool b: c + property bool c: a +} diff --git a/tests/auto/language/tst_language.cpp b/tests/auto/language/tst_language.cpp index 6a3344842..1a3794148 100644 --- a/tests/auto/language/tst_language.cpp +++ b/tests/auto/language/tst_language.cpp @@ -1018,6 +1018,9 @@ void TestLanguage::erroneousFiles_data() QTest::newRow("module-property-binding-in-project") << "Module properties cannot be set in Project items"; QTest::newRow("module-with-id") << "Module items cannot have an id property"; + QTest::newRow("recursive-property-direct") << "refers to itself"; + QTest::newRow("recursive-property-indirect") << "refers to itself.*via 'b'.*via 'c'"; + QTest::newRow("recursive-property-indirect-via-module") << "refers to itself.*via 'a'"; } void TestLanguage::erroneousFiles() diff --git a/tests/auto/tools/tst_tools.cpp b/tests/auto/tools/tst_tools.cpp index a872bdd0d..ab65f4f6b 100644 --- a/tests/auto/tools/tst_tools.cpp +++ b/tests/auto/tools/tst_tools.cpp @@ -53,6 +53,7 @@ #include <tools/set.h> #include <tools/settings.h> #include <tools/setupprojectparameters.h> +#include <tools/span.h> #include <tools/stringutils.h> #include <tools/version.h> @@ -1207,87 +1208,6 @@ void TestTools::stringutils_join_char_data() << std::string("a b c"); } -void TestTools::stringutils_startsWith() -{ - std::string a; - a = "AB"; - QVERIFY( startsWith(a, "A") ); - QVERIFY( startsWith(a, "AB") ); - QVERIFY( !startsWith(a, "C") ); - QVERIFY( !startsWith(a, "ABCDEF") ); - QVERIFY( startsWith(a, "") ); - QVERIFY( startsWith(a, 'A') ); - QVERIFY( !startsWith(a, 'C') ); - QVERIFY( !startsWith(a, char()) ); - - QVERIFY( startsWith(a, "A") ); - QVERIFY( startsWith(a, "AB") ); - QVERIFY( !startsWith(a, "C") ); - QVERIFY( !startsWith(a, "ABCDEF") ); - QVERIFY( startsWith(a, "") ); - - a = ""; - QVERIFY( startsWith(a, "") ); - QVERIFY( !startsWith(a, "ABC") ); - - QVERIFY( startsWith(a, "") ); - QVERIFY( !startsWith(a, "ABC") ); - - QVERIFY( !startsWith(a, 'x') ); - QVERIFY( !startsWith(a, char()) ); - - a = std::string(); - QVERIFY( startsWith(a, "") ); // different from QString::startsWith - QVERIFY( !startsWith(a, "ABC") ); - - QVERIFY( !startsWith(a, 'x') ); - QVERIFY( !startsWith(a, char()) ); - - a = u8"\xc3\xa9"; - QVERIFY( startsWith(a, u8"\xc3\xa9") ); - QVERIFY( !startsWith(a, u8"\xc3\xa1") ); -} - -void TestTools::stringutils_endsWith() -{ - std::string a; - a = "AB"; - QVERIFY( endsWith(a, "B") ); - QVERIFY( endsWith(a, "AB") ); - QVERIFY( !endsWith(a, "C") ); - QVERIFY( !endsWith(a, "ABCDEF") ); - QVERIFY( endsWith(a, "") ); - QVERIFY( endsWith(a, 'B') ); - QVERIFY( !endsWith(a, 'C') ); - QVERIFY( !endsWith(a, char()) ); - - QVERIFY( endsWith(a, "B") ); - QVERIFY( endsWith(a, "AB") ); - QVERIFY( !endsWith(a, "C") ); - QVERIFY( !endsWith(a, "ABCDEF") ); - QVERIFY( endsWith(a, "") ); - - a = ""; - QVERIFY( endsWith(a, "") ); - QVERIFY( !endsWith(a, "ABC") ); - QVERIFY( !endsWith(a, 'x') ); - QVERIFY( !endsWith(a, char()) ); - - QVERIFY( endsWith(a, "") ); - QVERIFY( !endsWith(a, "ABC") ); - - a = std::string(); - QVERIFY( endsWith(a, "") ); // different from QString::endsWith - QVERIFY( !endsWith(a, "ABC") ); - - QVERIFY( !endsWith(a, 'x') ); - QVERIFY( !endsWith(a, char()) ); - - a = u8"\xc3\xa9"; - QVERIFY( endsWith(a, u8"\xc3\xa9") ); - QVERIFY( !endsWith(a, u8"\xc3\xa1") ); -} - void TestTools::stringutils_trimmed() { std::string a; @@ -1343,6 +1263,12 @@ void TestTools::hash_range() QCOMPARE(map[key2], 2); } +void TestTools::span() +{ + std::vector<int> vec; + qbs::Internal::span<int> span(vec); +} + int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); diff --git a/tests/auto/tools/tst_tools.h b/tests/auto/tools/tst_tools.h index 619174976..dd27977da 100644 --- a/tests/auto/tools/tst_tools.h +++ b/tests/auto/tools/tst_tools.h @@ -96,13 +96,13 @@ private slots: void stringutils_join_empty(); void stringutils_join_char(); void stringutils_join_char_data(); - void stringutils_startsWith(); - void stringutils_endsWith(); void stringutils_trimmed(); void hash_tuple(); void hash_range(); + void span(); + private: QString setupSettingsDir1(); QString setupSettingsDir2(); diff --git a/tests/benchmarker/benchmarker-main.cpp b/tests/benchmarker/benchmarker-main.cpp index 2aa379372..4dfe053cc 100644 --- a/tests/benchmarker/benchmarker-main.cpp +++ b/tests/benchmarker/benchmarker-main.cpp @@ -41,7 +41,7 @@ static bool hasRegression = false; static int relativeChange(qint64 oldVal, qint64 newVal) { - return newVal == 0 ? 0 : newVal * 100 / oldVal - 100; + return newVal == 0 ? 0 : (newVal - oldVal) * 100 / oldVal; } static QByteArray relativeChangeString(int change) |