mirror of
https://github.com/Samsung/escargot.git
synced 2026-06-22 10:01:50 +00:00
Compare commits
No commits in common. "master" and "v4.1.1" have entirely different histories.
408 changed files with 34838 additions and 216122 deletions
39
.github/workflows/analysis-actions.yml
vendored
39
.github/workflows/analysis-actions.yml
vendored
|
|
@ -4,23 +4,19 @@ on:
|
|||
schedule:
|
||||
# trigger on every monday, wednesday and friday
|
||||
- cron: '30 22 * * 1,3,5'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
coverity-scan:
|
||||
if: github.repository == 'Samsung/escargot'
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build ICU(64)
|
||||
- name: Install Packages
|
||||
run: |
|
||||
git clone --depth 1 --single-branch -b release-78.1 https://github.com/unicode-org/icu.git $GITHUB_WORKSPACE/icu64-build/
|
||||
cd $GITHUB_WORKSPACE/icu64-build/icu4c/source
|
||||
LDFLAGS="-Wl,-rpath=$GITHUB_WORKSPACE/icu64/lib/" ./runConfigureICU Linux/gcc --prefix="$GITHUB_WORKSPACE/icu64/"
|
||||
make -j8
|
||||
make install
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ninja-build
|
||||
- name: Download Coverity Tool
|
||||
env:
|
||||
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
|
||||
|
|
@ -30,10 +26,10 @@ jobs:
|
|||
tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
|
||||
- name: Build
|
||||
env:
|
||||
BUILD_OPTIONS: -DESCARGOT_MODE=release -DESCARGOT_THREADING=ON -DESCARGOT_TCO=ON -DESCARGOT_CODE_CACHE=ON -DESCARGOT_WASM=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
BUILD_OPTIONS: -DESCARGOT_HOST=linux -DESCARGOT_ARCH=x64 -DESCARGOT_MODE=release -DESCARGOT_THREADING=ON -DESCARGOT_TCO=ON -DESCARGOT_CODE_CACHE=ON -DESCARGOT_WASM=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
run: |
|
||||
export PATH=$GITHUB_WORKSPACE/cov-analysis-linux64/bin:$PATH
|
||||
LDFLAGS="-L$GITHUB_WORKSPACE/icu64/lib/ -Wl,-rpath=$GITHUB_WORKSPACE/icu64/lib/" PKG_CONFIG_PATH="$GITHUB_WORKSPACE/icu64/lib/pkgconfig/" cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H. -Bout/coverity_scan $BUILD_OPTIONS
|
||||
cmake -H. -Bout/coverity_scan $BUILD_OPTIONS
|
||||
cov-build --dir cov-int ninja -Cout/coverity_scan
|
||||
- name: Submit
|
||||
env:
|
||||
|
|
@ -45,26 +41,28 @@ jobs:
|
|||
--form token=$TOKEN \
|
||||
--form email=$NOTI_MAIL \
|
||||
--form file=@escargot.tgz \
|
||||
--form version="4.3.0" \
|
||||
--form version="4.0.0" \
|
||||
--form description="escargot coverity scan" \
|
||||
https://scan.coverity.com/builds?project=Samsung%2Fescargot
|
||||
|
||||
coverage-scan:
|
||||
if: github.repository == 'Samsung/escargot'
|
||||
runs-on: [self-hosted, linux, x64, test]
|
||||
timeout-minutes: 600
|
||||
# ubuntu version fixed
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Packages
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y pypy ninja-build libicu-dev gcovr
|
||||
- name: Build x64
|
||||
env:
|
||||
BUILD_OPTIONS: -DESCARGOT_MODE=release -DESCARGOT_THREADING=ON -DESCARGOT_TCO=ON -DESCARGOT_COVERAGE=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
BUILD_OPTIONS: -DESCARGOT_HOST=linux -DESCARGOT_ARCH=x64 -DESCARGOT_MODE=release -DESCARGOT_THREADING=ON -DESCARGOT_TCO=ON -DESCARGOT_COVERAGE=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
run: |
|
||||
LDFLAGS=" -L/usr/icu78-64/lib/ -Wl,-rpath=/usr/icu78-64/lib/" PKG_CONFIG_PATH="/usr/icu78-64/lib/pkgconfig/" cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H. -Bout/coverage64 -DESCARGOT_ARCH=x64 $BUILD_OPTIONS
|
||||
ninja -Cout/coverage64
|
||||
LDFLAGS=" -L/usr/icu78-32/lib/ -Wl,-rpath=/usr/icu78-32/lib/" PKG_CONFIG_PATH="/usr/icu78-32/lib/pkgconfig/" cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H. -Bout/coverage32 -DESCARGOT_ARCH=x86 $BUILD_OPTIONS
|
||||
ninja -Cout/coverage32
|
||||
cmake -H. -Bout/coverage $BUILD_OPTIONS
|
||||
ninja -Cout/coverage
|
||||
- name: Run test262 and collect coverage data
|
||||
# test262 is unstable in actions env, but coverage data will be accumulated
|
||||
continue-on-error: true
|
||||
|
|
@ -73,11 +71,10 @@ jobs:
|
|||
sudo locale-gen en_US.UTF-8
|
||||
export LANG=en_US.UTF-8
|
||||
locale
|
||||
LD_LIBRARY_PATH=/usr/icu78-64/lib/ GC_FREE_SPACE_DIVISOR=1 tools/run-tests.py --arch=x86_64 --engine="$GITHUB_WORKSPACE/out/coverage64/escargot" new-es regression-tests test262 octane chakracore sunspider-js modifiedVendorTest jsc-stress v8 spidermonkey intl jetstream-only-cdjs
|
||||
LD_LIBRARY_PATH=/usr/icu78-32/lib/ GC_FREE_SPACE_DIVISOR=1 tools/run-tests.py --arch=x86 --engine="$GITHUB_WORKSPACE/out/coverage32/escargot" new-es regression-tests test262 octane chakracore sunspider-js modifiedVendorTest jsc-stress v8 spidermonkey intl jetstream-only-cdjs
|
||||
tools/run-tests.py --arch=x86_64 --engine="$GITHUB_WORKSPACE/out/coverage/escargot" new-es regression-tests test262
|
||||
- name: Generate coverage report
|
||||
run: |
|
||||
gcovr --gcov-ignore-parse-errors --exclude-unreachable-branches --exclude-throw-branches --exclude '.*third_party/' --exclude '.*shell/' --exclude '.*api/' -r . --xml coverage.xml
|
||||
gcovr --gcov-ignore-parse-errors --exclude-unreachable-branches --exclude-throw-branches --exclude '.*third_party/' --exclude '.*api/' -r . --xml coverage.xml
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
|
|
|
|||
97
.github/workflows/android-release.yml
vendored
97
.github/workflows/android-release.yml
vendored
|
|
@ -1,97 +0,0 @@
|
|||
name: Android-Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
|
||||
jobs:
|
||||
build-android-on-ubuntu:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4.1.0
|
||||
with:
|
||||
distribution: "zulu"
|
||||
java-version: 17
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
- name: Build with Gradle
|
||||
working-directory: ./build/android
|
||||
run: |
|
||||
./gradlew bundleHostJar
|
||||
./gradlew javadocJar
|
||||
./gradlew sourcesJar
|
||||
./gradlew :escargot:testDebugUnitTest
|
||||
mv ./escargot/build/libs/escargot.jar ./escargot/build/libs/escargot-ubuntu.jar
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-artifact-ubuntu
|
||||
path: |
|
||||
./build/android/escargot/build/**/escargot-*.aar
|
||||
./build/android/escargot/build/**/escargot-*.jar
|
||||
!./build/android/escargot/build/**/escargot-*Shell.aar
|
||||
if-no-files-found: error
|
||||
|
||||
build-android-on-macos:
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Packages
|
||||
run: |
|
||||
brew update
|
||||
brew install ninja icu4c
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4.1.0
|
||||
with:
|
||||
distribution: "zulu"
|
||||
java-version: 17
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v3
|
||||
- name: Build with Gradle
|
||||
working-directory: ./build/android
|
||||
run: |
|
||||
./gradlew bundleHostJar
|
||||
./gradlew :escargot:testDebugUnitTest
|
||||
mv ./escargot/build/libs/escargot.jar ./escargot/build/libs/escargot-mac.jar
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-artifact-mac
|
||||
path: |
|
||||
./build/android/escargot/build/libs/escargot-mac.jar
|
||||
if-no-files-found: error
|
||||
|
||||
merge-update-release:
|
||||
needs: [build-android-on-ubuntu, build-android-on-macos]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
pattern: build-artifact-*
|
||||
merge-multiple: true
|
||||
- name: Set release date
|
||||
run: |
|
||||
echo "RELEASE_DATE=$(date --rfc-3339=date)" >> $GITHUB_ENV
|
||||
- name: Merge build artifacts
|
||||
working-directory: ./artifacts
|
||||
run: |
|
||||
ls -R ./
|
||||
echo ${RELEASE_DATE}
|
||||
find . -type f -name "escargot-*.aar" -exec mv {} . \;
|
||||
find . -type f -name "escargot-*.jar" -exec mv {} . \;
|
||||
ls -R ./
|
||||
find ./ -type f -name "escargot-*.aar" -o -name "escargot-*.jar" | zip Android-Release-${{ env.RELEASE_DATE }}.zip -@
|
||||
- name: Upload to release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
artifacts/Android-Release-${{ env.RELEASE_DATE }}.zip
|
||||
110
.github/workflows/code-review.yml
vendored
110
.github/workflows/code-review.yml
vendored
|
|
@ -1,110 +0,0 @@
|
|||
name: Review Bot (PR)
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
branches: [ "master" ]
|
||||
|
||||
concurrency:
|
||||
group: review-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
review:
|
||||
if: github.repository == 'Samsung/escargot'
|
||||
runs-on: [self-hosted, escargot-review]
|
||||
|
||||
steps:
|
||||
- name: Compute diff range (incremental on synchronize)
|
||||
id: shas
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const action = context.payload.action;
|
||||
const pr = context.payload.pull_request.number;
|
||||
const baseSha = context.payload.pull_request.base.sha;
|
||||
const headSha = context.payload.pull_request.head.sha;
|
||||
|
||||
let base = baseSha;
|
||||
let skip = false;
|
||||
if (action === 'synchronize') {
|
||||
const before = context.payload.before;
|
||||
if (before) {
|
||||
base = before;
|
||||
} else {
|
||||
skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
core.setOutput('base', base);
|
||||
core.setOutput('head', headSha);
|
||||
core.setOutput('pr', pr);
|
||||
core.setOutput('skip', String(skip));
|
||||
- name: Skip review (no before on synchronize)
|
||||
if: ${{ steps.shas.outputs.skip == 'true' }}
|
||||
run: |
|
||||
echo "[Review Bot] Skipping review: 'before' SHA missing on synchronize event."
|
||||
- name: Call review server
|
||||
id: call
|
||||
if: ${{ steps.shas.outputs.skip != 'true' }}
|
||||
env:
|
||||
REVIEW_SERVER: ${{ secrets.REVIEW_SERVER }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
REVIEW_SERVER="${REVIEW_SERVER}"
|
||||
BASE_SHA="${{ steps.shas.outputs.base }}"
|
||||
HEAD_SHA="${{ steps.shas.outputs.head }}"
|
||||
PR_NUMBER="${{ steps.shas.outputs.pr }}"
|
||||
|
||||
curl -sS --fail-with-body --show-error \
|
||||
--connect-timeout 10 --max-time 7200 \
|
||||
-X POST "${REVIEW_SERVER}/review" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"base_sha\":\"${BASE_SHA}\",\"head_sha\":\"${HEAD_SHA}\",\"pull_request_number\":${PR_NUMBER}}" \
|
||||
-o review.json
|
||||
|
||||
echo "==== review.json ===="
|
||||
cat review.json
|
||||
|
||||
- name: Post review comments
|
||||
if: ${{ steps.shas.outputs.skip != 'true' }}
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const pr = context.payload.pull_request.number;
|
||||
let raw;
|
||||
try {
|
||||
raw = fs.readFileSync('review.json', 'utf8');
|
||||
} catch (e) {
|
||||
core.warning(`review.json not found: ${e}`);
|
||||
return;
|
||||
}
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(raw);
|
||||
} catch (e) {
|
||||
core.warning(`Failed to parse review.json as JSON: ${e}`);
|
||||
return;
|
||||
}
|
||||
const comments = Array.isArray(data.comments) ? data.comments : [];
|
||||
|
||||
for (const c of comments) {
|
||||
await github.request('POST /repos/{owner}/{repo}/pulls/{pull_number}/comments', {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: pr,
|
||||
body: c.body,
|
||||
commit_id: c.commit_id,
|
||||
path: c.path,
|
||||
line: c.line,
|
||||
side: c.side,
|
||||
headers: { 'accept': 'application/vnd.github+json' }
|
||||
});
|
||||
|
||||
await new Promise(r => setTimeout(r, 200));
|
||||
}
|
||||
434
.github/workflows/es-actions.yml
vendored
434
.github/workflows/es-actions.yml
vendored
|
|
@ -5,29 +5,27 @@ on:
|
|||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
RUNNER: tools/run-tests.py
|
||||
|
||||
jobs:
|
||||
check-tidy:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Packages
|
||||
run: |
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
|
||||
sudo add-apt-repository "deb [trusted=yes] http://apt.llvm.org/noble/ llvm-toolchain-noble-20 main"
|
||||
sudo add-apt-repository "deb http://mirrors.kernel.org/ubuntu/ focal main universe"
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y clang-format-20
|
||||
sudo apt-get install -y clang-format-6.0
|
||||
- name: Test
|
||||
run: tools/check_tidy.py
|
||||
|
||||
build-on-macos:
|
||||
runs-on: macos-15
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
|
@ -35,53 +33,21 @@ jobs:
|
|||
- name: Install Packages
|
||||
run: |
|
||||
brew update
|
||||
brew install ninja icu4c
|
||||
brew install cmake ninja pkg-config
|
||||
- name: Build x64
|
||||
env:
|
||||
BUILD_OPTIONS: -DESCARGOT_WASM=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
BUILD_OPTIONS: -DESCARGOT_WASM=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
run: |
|
||||
# check cpu
|
||||
sysctl -a | grep machdep.cpu
|
||||
# add icu path to pkg_config_path
|
||||
export PKG_CONFIG_PATH="$(brew --prefix icu4c)/lib/pkgconfig"
|
||||
echo $PKG_CONFIG_PATH
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.10 -H. -Bout/debug/ -DESCARGOT_MODE=debug $BUILD_OPTIONS
|
||||
cmake -H. -Bout/debug/ -DESCARGOT_MODE=debug $BUILD_OPTIONS
|
||||
ninja -Cout/debug/
|
||||
$RUNNER --engine="./out/debug/escargot" new-es
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.10 -H. -Bout/release/ -DESCARGOT_MODE=release $BUILD_OPTIONS
|
||||
ninja -Cout/release/
|
||||
cp test/octane/*.js .
|
||||
./out/release/escargot run.js
|
||||
|
||||
build-on-macos-arm64:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install Packages
|
||||
run: |
|
||||
brew update
|
||||
brew install ninja icu4c
|
||||
- name: Build arm64
|
||||
env:
|
||||
BUILD_OPTIONS: -DESCARGOT_WASM=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
run: |
|
||||
# check cpu
|
||||
sysctl -a | grep machdep.cpu
|
||||
# add icu path to pkg_config_path
|
||||
export PKG_CONFIG_PATH="$(brew --prefix icu4c)/lib/pkgconfig"
|
||||
echo $PKG_CONFIG_PATH
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.10 -H. -Bout/debug/ -DESCARGOT_MODE=debug $BUILD_OPTIONS
|
||||
ninja -Cout/debug/
|
||||
$RUNNER --engine="./out/debug/escargot" new-es
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.10 -H. -Bout/release/ -DESCARGOT_MODE=release $BUILD_OPTIONS
|
||||
./tools/run-tests.py --engine="./out/debug/escargot" new-es
|
||||
cmake -H. -Bout/release/ -DESCARGOT_MODE=release $BUILD_OPTIONS
|
||||
ninja -Cout/release/
|
||||
cp test/octane/*.js .
|
||||
./out/release/escargot run.js
|
||||
|
||||
build-test-on-android:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: macos-12
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86, x86_64]
|
||||
|
|
@ -90,11 +56,10 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Enable KVM
|
||||
- name: Install Packages
|
||||
run: |
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
brew update
|
||||
brew install cmake pkg-config
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4.1.0
|
||||
with:
|
||||
|
|
@ -110,21 +75,31 @@ jobs:
|
|||
force-avd-creation: false
|
||||
emulator-options: -no-window -gpu swiftshader_indirect -camera-back none -no-snapshot-save -gpu swiftshader_indirect -noaudio -no-boot-anim
|
||||
disable-animations: true
|
||||
script: cd build/android/;./gradlew connectedDebugAndroidTest -DESCARGOT_BUILD_TLS_ACCESS_BY_PTHREAD_KEY=ON
|
||||
script: cd build/android/;./gradlew connectedDebugAndroidTest
|
||||
|
||||
build-test-tizen:
|
||||
runs-on: ubuntu-24.04
|
||||
build-by-clang:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: prepare deb sources for GBS
|
||||
run: echo "deb [trusted=yes] http://download.tizen.org/tools/latest-release/Ubuntu_24.04/ /" | sudo tee /etc/apt/sources.list.d/tizen.list
|
||||
- name: install GBS
|
||||
run: sudo apt-get update && sudo apt-get install -y gbs
|
||||
- name: build
|
||||
- name: Install Packages
|
||||
run: |
|
||||
gbs -c .github/workflows/gbs.conf build -A armv7l -P profile.tizen --incremental --define "enable_shell 1"
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ninja-build gcc-multilib g++-multilib libicu-dev
|
||||
- name: Build x86
|
||||
env:
|
||||
BUILD_OPTIONS: -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=x86 -DESCARGOT_MODE=debug -DESCARGOT_WASM=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
run: |
|
||||
CC=clang CXX=clang++ cmake -H. -Bout/clang/x86 $BUILD_OPTIONS
|
||||
ninja -Cout/clang/x86
|
||||
- name: Build x64
|
||||
env:
|
||||
BUILD_OPTIONS: -DESCARGOT_MODE=debug -DESCARGOT_WASM=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
run: |
|
||||
CC=clang CXX=clang++ cmake -H. -Bout/clang/x64 $BUILD_OPTIONS
|
||||
ninja -Cout/clang/x64
|
||||
./tools/run-tests.py --engine="./out/clang/x64/escargot" new-es
|
||||
|
||||
test-on-windows-clang-cl:
|
||||
runs-on: windows-2022
|
||||
|
|
@ -132,11 +107,9 @@ jobs:
|
|||
matrix:
|
||||
# clang-cl with cannot generate c++ exception code well
|
||||
# if clang-cl bug fixed, we can add x64
|
||||
# clang version and STL version are sometimes not matched in github actions,
|
||||
# so I add -D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH
|
||||
arch: [
|
||||
{cpu: "x86", flag: "-m32 -D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH"}
|
||||
#, {cpu: "x64", flag: "-D_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH"}
|
||||
{cpu: "x86", flag: "-m32"}
|
||||
# , {cpu: "x64", flag: ""}
|
||||
]
|
||||
steps:
|
||||
- name: Set git cllf config
|
||||
|
|
@ -150,43 +123,23 @@ jobs:
|
|||
with:
|
||||
timezoneWindows: "Pacific Standard Time"
|
||||
- uses: lukka/get-cmake@latest
|
||||
with:
|
||||
cmakeVersion: "~3.25.0"
|
||||
- uses: GuillaumeFalourd/setup-windows10-sdk-action@v2
|
||||
with:
|
||||
sdk-version: 26100
|
||||
sdk-version: 20348
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Download and Install Visual C++ Redistributable
|
||||
shell: powershell
|
||||
- name: Install msvc redist package
|
||||
run: |
|
||||
$vcRedistUrl64 = "https://aka.ms/vs/17/release/vc_redist.x64.exe" # Or the appropriate URL for your target architecture/version
|
||||
$vcRedistPath64 = "$env:TEMP\vc_redist.x64.exe"
|
||||
$vcRedistUrl32 = "https://aka.ms/vs/17/release/vc_redist.x86.exe" # Or the appropriate URL for your target architecture/version
|
||||
$vcRedistPath32 = "$env:TEMP\vc_redist.x86.exe"
|
||||
|
||||
Write-Host "Downloading Visual C++ Redistributable from $vcRedistUrl64"
|
||||
Invoke-WebRequest -Uri $vcRedistUrl64 -OutFile $vcRedistPath64
|
||||
|
||||
Write-Host "Downloading Visual C++ Redistributable from $vcRedistUrl32"
|
||||
Invoke-WebRequest -Uri $vcRedistUrl32 -OutFile $vcRedistPath32
|
||||
|
||||
Write-Host "Installing Visual C++ Redistributable silently"
|
||||
Start-Process -FilePath $vcRedistPath64 -ArgumentList "/install /quiet /norestart" -Wait
|
||||
Start-Process -FilePath $vcRedistPath32 -ArgumentList "/install /quiet /norestart" -Wait
|
||||
Write-Host "Visual C++ Redistributable installation complete."
|
||||
(new-object System.Net.WebClient).DownloadFile('https://github.com/abbodi1406/vcredist/releases/download/v0.73.0/VisualCppRedist_AIO_x86_x64.exe','VisualCppRedist_AIO_x86_x64.exe')
|
||||
.\VisualCppRedist_AIO_x86_x64.exe /y
|
||||
- uses: ilammy/msvc-dev-cmd@v1.13.0
|
||||
with:
|
||||
arch: ${{ matrix.arch.cpu }}
|
||||
sdk: "10.0.26100.0"
|
||||
- uses: egor-tensin/setup-clang@v1
|
||||
with:
|
||||
version: 19.1.7
|
||||
platform: ${{ matrix.arch.cpu }}
|
||||
sdk: "10.0.20348.0"
|
||||
- name: Build ${{ matrix.arch.cpu }} Release
|
||||
run: |
|
||||
CMake -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_SYSTEM_VERSION:STRING="10.0" -DCMAKE_SYSTEM_PROCESSOR=${{ matrix.arch.cpu }} -Bout/ -DESCARGOT_OUTPUT=shell -DESCARGOT_LIBICU_SUPPORT=ON -DESCARGOT_THREADING=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -G Ninja -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_BUILD_TYPE=release -DCMAKE_C_FLAGS="${{ matrix.arch.flag }}" -DCMAKE_CXX_FLAGS="${{ matrix.arch.flag }}"
|
||||
CMake -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_SYSTEM_VERSION:STRING="10.0" -DCMAKE_SYSTEM_PROCESSOR=${{ matrix.arch.cpu }} -Bout/ -DESCARGOT_OUTPUT=shell -DESCARGOT_LIBICU_SUPPORT=ON -DESCARGOT_LIBICU_SUPPORT_WITH_DLOPEN=OFF -DESCARGOT_THREADING=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -G Ninja -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_BUILD_TYPE=release -DCMAKE_C_FLAGS="${{ matrix.arch.flag }}" -DCMAKE_CXX_FLAGS="${{ matrix.arch.flag }}"
|
||||
CMake --build out/ --config Release
|
||||
- name: Run octane
|
||||
run: |
|
||||
|
|
@ -196,9 +149,9 @@ jobs:
|
|||
# clang-cl with cannot generate c++ exception code well. if clang-cl bug fixed, we can enable test262
|
||||
# - name: Run test262
|
||||
# run: |
|
||||
# set GC_FREE_SPACE_DIVISOR=1
|
||||
# pip install chardet
|
||||
# python tools\run-tests.py --engine=%cd%\out\escargot.exe test262 --test262-extra-arg="--skip Temporal --skip intl402 --skip Atomics"
|
||||
# set GC_FREE_SPACE_DIVISOR=1
|
||||
# pip install chardet
|
||||
# python tools\run-tests.py --engine=%cd%\out\escargot.exe test262 --test262-extra-arg="--skip Temporal --skip intl402 --skip Atomics"
|
||||
# shell: cmd
|
||||
- if: ${{ failure() }}
|
||||
uses: mxschmitt/action-tmate@v3
|
||||
|
|
@ -221,39 +174,23 @@ jobs:
|
|||
with:
|
||||
timezoneWindows: "Pacific Standard Time"
|
||||
- uses: lukka/get-cmake@latest
|
||||
with:
|
||||
cmakeVersion: "~3.25.0" # <--= optional, use most recent 3.25.x version
|
||||
- uses: GuillaumeFalourd/setup-windows10-sdk-action@v2
|
||||
with:
|
||||
sdk-version: 26100
|
||||
sdk-version: 20348
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Download and Install Visual C++ Redistributable
|
||||
shell: powershell
|
||||
- name: Install msvc redist package
|
||||
run: |
|
||||
$vcRedistUrl64 = "https://aka.ms/vs/17/release/vc_redist.x64.exe" # Or the appropriate URL for your target architecture/version
|
||||
$vcRedistPath64 = "$env:TEMP\vc_redist.x64.exe"
|
||||
$vcRedistUrl32 = "https://aka.ms/vs/17/release/vc_redist.x86.exe" # Or the appropriate URL for your target architecture/version
|
||||
$vcRedistPath32 = "$env:TEMP\vc_redist.x86.exe"
|
||||
|
||||
Write-Host "Downloading Visual C++ Redistributable from $vcRedistUrl64"
|
||||
Invoke-WebRequest -Uri $vcRedistUrl64 -OutFile $vcRedistPath64
|
||||
|
||||
Write-Host "Downloading Visual C++ Redistributable from $vcRedistUrl32"
|
||||
Invoke-WebRequest -Uri $vcRedistUrl32 -OutFile $vcRedistPath32
|
||||
|
||||
Write-Host "Installing Visual C++ Redistributable silently"
|
||||
Start-Process -FilePath $vcRedistPath64 -ArgumentList "/install /quiet /norestart" -Wait
|
||||
Start-Process -FilePath $vcRedistPath32 -ArgumentList "/install /quiet /norestart" -Wait
|
||||
Write-Host "Visual C++ Redistributable installation complete."
|
||||
(new-object System.Net.WebClient).DownloadFile('https://github.com/abbodi1406/vcredist/releases/download/v0.73.0/VisualCppRedist_AIO_x86_x64.exe','VisualCppRedist_AIO_x86_x64.exe')
|
||||
.\VisualCppRedist_AIO_x86_x64.exe /y
|
||||
- uses: ilammy/msvc-dev-cmd@v1.13.0
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
sdk: "10.0.26100.0"
|
||||
sdk: "10.0.20348.0"
|
||||
- name: Build ${{ matrix.arch }} Release
|
||||
run: |
|
||||
CMake -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_SYSTEM_VERSION:STRING="10.0" -DCMAKE_SYSTEM_PROCESSOR=${{ matrix.arch }} -DESCARGOT_ARCH=${{ matrix.arch }} -Bout/ -DESCARGOT_OUTPUT=shell -DESCARGOT_LIBICU_SUPPORT=ON -DESCARGOT_WASM=ON -DESCARGOT_THREADING=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -G Ninja -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -DCMAKE_BUILD_TYPE=release
|
||||
CMake -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_SYSTEM_VERSION:STRING="10.0" -DCMAKE_SYSTEM_PROCESSOR=${{ matrix.arch }} -DESCARGOT_ARCH=${{ matrix.arch }} -Bout/ -DESCARGOT_OUTPUT=shell -DESCARGOT_LIBICU_SUPPORT=ON -DESCARGOT_LIBICU_SUPPORT_WITH_DLOPEN=OFF -DESCARGOT_WASM=ON -DESCARGOT_THREADING=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -G Ninja -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -DCMAKE_BUILD_TYPE=release
|
||||
CMake --build out/ --config Release
|
||||
# windows internal ICU doesn't support Temporal and intl402 well
|
||||
# github action windows runner only have 2 CPUs. that's why I disable Atomics(timeout occured with some tests)
|
||||
|
|
@ -261,7 +198,7 @@ jobs:
|
|||
run: |
|
||||
set GC_FREE_SPACE_DIVISOR=1
|
||||
pip install chardet
|
||||
python tools\run-tests.py --engine=%cd%\out\escargot.exe test262 --test262-extra-arg="--skip Temporal --skip intl402 --skip Atomics --skip sm"
|
||||
python tools\run-tests.py --engine=%cd%\out\escargot.exe test262 --test262-extra-arg="--skip Temporal --skip intl402 --skip Atomics"
|
||||
shell: cmd
|
||||
- name: Run octane
|
||||
run: |
|
||||
|
|
@ -283,28 +220,26 @@ jobs:
|
|||
with:
|
||||
submodules: true
|
||||
- uses: lukka/get-cmake@latest
|
||||
with:
|
||||
cmakeVersion: "~3.25.0" # <--= optional, use most recent 3.25.x version
|
||||
- uses: GuillaumeFalourd/setup-windows10-sdk-action@v2
|
||||
with:
|
||||
sdk-version: 26100
|
||||
sdk-version: 20348
|
||||
- uses: ilammy/msvc-dev-cmd@v1.13.0
|
||||
with:
|
||||
arch: x64
|
||||
sdk: "10.0.26100.0"
|
||||
sdk: "10.0.20348.0"
|
||||
uwp: true
|
||||
- name: Build x64 UWP Release
|
||||
run: |
|
||||
CMake -G "Visual Studio 17 2022" -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION:STRING="10.0" -DCMAKE_SYSTEM_PROCESSOR=x64 -Bout/win64_release_uwp/ -DESCARGOT_OUTPUT=shell -DESCARGOT_LIBICU_SUPPORT=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON
|
||||
CMake --build out\win64_release_uwp --config Release
|
||||
CMake -G "Visual Studio 17 2022" -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION:STRING="10.0" -DCMAKE_SYSTEM_PROCESSOR=x64 -Bout/win64_release_uwp/ -DESCARGOT_OUTPUT=shell -DESCARGOT_LIBICU_SUPPORT=ON -DESCARGOT_LIBICU_SUPPORT_WITH_DLOPEN=OFF -DESCARGOT_TEST=ON
|
||||
cmake --build out\win64_release_uwp --config Release
|
||||
shell: cmd
|
||||
- uses: ilammy/msvc-dev-cmd@v1.13.0
|
||||
with:
|
||||
arch: x86
|
||||
sdk: "10.0.26100.0"
|
||||
sdk: "10.0.20348.0"
|
||||
- name: Build x86 DLL Release
|
||||
run: |
|
||||
CMake -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_SYSTEM_VERSION:STRING="10.0" -DCMAKE_SYSTEM_PROCESSOR=x86 -Bout/win32_release_shared/ -DESCARGOT_OUTPUT=shared_lib -DESCARGOT_LIBICU_SUPPORT=ON -DESCARGOT_THREADING=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -G Ninja -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -DCMAKE_BUILD_TYPE=release
|
||||
CMake -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_SYSTEM_VERSION:STRING="10.0" -DCMAKE_SYSTEM_PROCESSOR=x86 -Bout/win32_release_shared/ -DESCARGOT_OUTPUT=shared_lib -DESCARGOT_LIBICU_SUPPORT=ON -DESCARGOT_LIBICU_SUPPORT_WITH_DLOPEN=OFF -DESCARGOT_THREADING=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -G Ninja -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -DCMAKE_BUILD_TYPE=release
|
||||
CMake --build out/win32_release_shared --config Release
|
||||
shell: cmd
|
||||
- if: ${{ failure() }}
|
||||
|
|
@ -312,7 +247,7 @@ jobs:
|
|||
timeout-minutes: 15
|
||||
|
||||
build-test-on-x86-release:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
tc: ['new-es octane', 'v8 chakracore spidermonkey', 'jetstream-only-simple-parallel-1', 'jetstream-only-simple-parallel-2 jsc-stress', 'jetstream-only-simple-parallel-3 jetstream-only-cdjs']
|
||||
|
|
@ -323,45 +258,32 @@ jobs:
|
|||
- name: Install Packages
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ninja-build gcc-multilib g++-multilib make g++ pkg-config automake libtool git build-essential checkinstall libncurses-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev libffi-dev
|
||||
- name: Build python2
|
||||
sudo apt-get install -y ninja-build gcc-multilib g++-multilib python2
|
||||
sudo update-alternatives --install /usr/bin/python python /usr/bin/python2 1
|
||||
- name: Install ICU
|
||||
run: |
|
||||
mkdir $GITHUB_WORKSPACE/python2-build/
|
||||
cd $GITHUB_WORKSPACE/python2-build/
|
||||
wget https://www.python.org/ftp/python/2.7.18/Python-2.7.18.tgz
|
||||
tar xzf Python-2.7.18.tgz
|
||||
cd Python-2.7.18
|
||||
./configure --prefix=/usr/local/python2.7
|
||||
make -j8
|
||||
sudo make install
|
||||
sudo update-alternatives --install /usr/bin/python python /usr/local/python2.7/bin/python2.7 1
|
||||
- name: Build ICU
|
||||
run: |
|
||||
git clone --depth 1 --single-branch -b release-78.1 https://github.com/unicode-org/icu.git $GITHUB_WORKSPACE/icu-build/
|
||||
cd $GITHUB_WORKSPACE/icu-build/icu4c/source
|
||||
LDFLAGS="-m32 -Wl,-rpath=$GITHUB_WORKSPACE/icu32/lib/" CFLAGS="-m32" CXXFLAGS="-m32" ./runConfigureICU Linux/gcc --prefix="$GITHUB_WORKSPACE/icu32/"
|
||||
make -j8
|
||||
make install
|
||||
ls $GITHUB_WORKSPACE/icu32/lib/
|
||||
wget http://mirrors.kernel.org/ubuntu/pool/main/i/icu/libicu-dev_70.1-2ubuntu1_i386.deb
|
||||
dpkg -X libicu-dev_70.1-2ubuntu1_i386.deb $GITHUB_WORKSPACE/icu32
|
||||
- name: Build x86
|
||||
env:
|
||||
BUILD_OPTIONS: -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=x86 -DESCARGOT_THREADING=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TLS_ACCESS_BY_ADDRESS=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
BUILD_OPTIONS: -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=x86 -DESCARGOT_THREADING=ON -DESCARGOT_LIBICU_SUPPORT_WITH_DLOPEN=OFF -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
run: |
|
||||
export CXXFLAGS="-I$GITHUB_WORKSPACE/icu32/include"
|
||||
export LDFLAGS="-L$GITHUB_WORKSPACE/icu32/lib/ -Wl,-rpath=$GITHUB_WORKSPACE/icu32/lib/"
|
||||
export PKG_CONFIG_PATH=$GITHUB_WORKSPACE/icu32/lib/pkgconfig
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H. -Bout/release/x86 $BUILD_OPTIONS
|
||||
export CXXFLAGS="-I$GITHUB_WORKSPACE/icu32/usr/include"
|
||||
export LDFLAGS="-L$GITHUB_WORKSPACE/icu32/usr/lib/i386-linux-gnu -Wl,-rpath=$GITHUB_WORKSPACE/icu32/usr/lib/i386-linux-gnu"
|
||||
export PKG_CONFIG_PATH=$GITHUB_WORKSPACE/icu32/usr/lib/i386-linux-gnu/pkgconfig
|
||||
cmake -H. -Bout/release/x86 $BUILD_OPTIONS
|
||||
ninja -Cout/release/x86
|
||||
- name: Run release-x86 test
|
||||
env:
|
||||
GC_FREE_SPACE_DIVISOR: 1
|
||||
run: LD_LIBRARY_PATH=$GITHUB_WORKSPACE/icu32/lib $RUNNER --arch=x86 --engine="$GITHUB_WORKSPACE/out/release/x86/escargot" ${{ matrix.tc }}
|
||||
run: $RUNNER --arch=x86 --engine="$GITHUB_WORKSPACE/out/release/x86/escargot" ${{ matrix.tc }}
|
||||
- if: ${{ failure() }}
|
||||
uses: mxschmitt/action-tmate@v3
|
||||
timeout-minutes: 15
|
||||
|
||||
|
||||
build-test-on-x64-release:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
tc: ['octane v8 web-tooling-benchmark', 'chakracore spidermonkey new-es']
|
||||
|
|
@ -377,39 +299,18 @@ jobs:
|
|||
- name: Install Packages
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ninja-build g++ pkg-config automake libtool git build-essential checkinstall libncurses-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev libffi-dev
|
||||
- name: Build ICU(64)
|
||||
run: |
|
||||
git clone --depth 1 --single-branch -b release-78.1 https://github.com/unicode-org/icu.git $GITHUB_WORKSPACE/icu64-build/
|
||||
cd $GITHUB_WORKSPACE/icu64-build/icu4c/source
|
||||
LDFLAGS="-Wl,-rpath=$GITHUB_WORKSPACE/icu64/lib/" ./runConfigureICU Linux/gcc --prefix="$GITHUB_WORKSPACE/icu64/"
|
||||
make -j8
|
||||
make install
|
||||
- name: Build python2
|
||||
run: |
|
||||
mkdir $GITHUB_WORKSPACE/python2-build/
|
||||
cd $GITHUB_WORKSPACE/python2-build/
|
||||
wget https://www.python.org/ftp/python/2.7.18/Python-2.7.18.tgz
|
||||
tar xzf Python-2.7.18.tgz
|
||||
cd Python-2.7.18
|
||||
./configure --prefix=/usr/local/python2.7
|
||||
make -j8
|
||||
sudo make install
|
||||
sudo update-alternatives --install /usr/bin/python python /usr/local/python2.7/bin/python2.7 1
|
||||
sudo apt-get install -y ninja-build libicu-dev python2
|
||||
sudo update-alternatives --install /usr/bin/python python /usr/bin/python2 1
|
||||
- name: Build x64
|
||||
env:
|
||||
BUILD_OPTIONS: -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TLS_ACCESS_BY_ADDRESS=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
BUILD_OPTIONS: -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
run: |
|
||||
export CXXFLAGS="-I$GITHUB_WORKSPACE/icu64/include"
|
||||
export LDFLAGS="-L$GITHUB_WORKSPACE/icu64/lib -Wl,-rpath=$GITHUB_WORKSPACE/icu64/lib"
|
||||
export PKG_CONFIG_PATH=$GITHUB_WORKSPACE/icu64/lib/pkgconfig
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H. -Bout/release/x64 $BUILD_OPTIONS ${{ matrix.build_opt }}
|
||||
cmake -H. -Bout/release/x64 $BUILD_OPTIONS ${{ matrix.build_opt }}
|
||||
ninja -Cout/release/x64
|
||||
- name: Run release-x64 test
|
||||
env:
|
||||
GC_FREE_SPACE_DIVISOR: 1
|
||||
run: |
|
||||
export LD_LIBRARY_PATH=$GITHUB_WORKSPACE/icu64/lib
|
||||
# set locale
|
||||
sudo locale-gen en_US.UTF-8
|
||||
export LANG=en_US.UTF-8
|
||||
|
|
@ -420,7 +321,7 @@ jobs:
|
|||
timeout-minutes: 15
|
||||
|
||||
build-test-on-x86-x64-debug:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
|
@ -428,40 +329,35 @@ jobs:
|
|||
- name: Install Packages
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ninja-build gcc-multilib g++-multilib make g++ pkg-config automake libtool git build-essential checkinstall libncurses-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev libffi-dev
|
||||
- name: Build ICU(32)
|
||||
sudo apt-get install -y ninja-build gcc-multilib g++-multilib
|
||||
- name: Install ICU
|
||||
run: |
|
||||
git clone --depth 1 --single-branch -b release-78.1 https://github.com/unicode-org/icu.git $GITHUB_WORKSPACE/icu32-build/
|
||||
cd $GITHUB_WORKSPACE/icu32-build/icu4c/source
|
||||
LDFLAGS="-m32 -Wl,-rpath=$GITHUB_WORKSPACE/icu32/lib/" CFLAGS="-m32" CXXFLAGS="-m32" ./runConfigureICU Linux/gcc --prefix="$GITHUB_WORKSPACE/icu32/"
|
||||
make -j8
|
||||
make install
|
||||
- name: Build ICU(64)
|
||||
run: |
|
||||
git clone --depth 1 --single-branch -b release-78.1 https://github.com/unicode-org/icu.git $GITHUB_WORKSPACE/icu64-build/
|
||||
cd $GITHUB_WORKSPACE/icu64-build/icu4c/source
|
||||
LDFLAGS="-Wl,-rpath=$GITHUB_WORKSPACE/icu64/lib/" ./runConfigureICU Linux/gcc --prefix="$GITHUB_WORKSPACE/icu64/"
|
||||
make -j8
|
||||
make install
|
||||
wget http://mirrors.kernel.org/ubuntu/pool/main/i/icu/libicu-dev_70.1-2ubuntu1_i386.deb
|
||||
dpkg -X libicu-dev_70.1-2ubuntu1_i386.deb $GITHUB_WORKSPACE/icu32
|
||||
wget http://mirrors.kernel.org/ubuntu/pool/main/i/icu/libicu-dev_70.1-2ubuntu1_amd64.deb
|
||||
dpkg -X libicu-dev_70.1-2ubuntu1_amd64.deb $GITHUB_WORKSPACE/icu64
|
||||
- name: Build x86/x64
|
||||
env:
|
||||
BUILD_OPTIONS_X86: -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=x86 -DESCARGOT_MODE=debug -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TLS_ACCESS_BY_ADDRESS=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
BUILD_OPTIONS_X64: -DESCARGOT_MODE=debug -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TLS_ACCESS_BY_ADDRESS=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
BUILD_OPTIONS_X86: -DESCARGOT_MODE=debug -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_LIBICU_SUPPORT_WITH_DLOPEN=OFF -DESCARGOT_OUTPUT=shell -GNinja
|
||||
BUILD_OPTIONS_X64: -DESCARGOT_MODE=debug -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
run: |
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H. -Bout/debug/x86 $BUILD_OPTIONS_X86
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H. -Bout/debug/x64 $BUILD_OPTIONS_X64
|
||||
export CXXFLAGS="-I$GITHUB_WORKSPACE/icu32/usr/include"
|
||||
export LDFLAGS="-L$GITHUB_WORKSPACE/icu32/usr/lib/i386-linux-gnu -Wl,-rpath=$GITHUB_WORKSPACE/icu32/usr/lib/i386-linux-gnu"
|
||||
export PKG_CONFIG_PATH=$GITHUB_WORKSPACE/icu32/usr/lib/i386-linux-gnu/pkgconfig
|
||||
cmake -H. -Bout/debug/x86 $BUILD_OPTIONS_X86
|
||||
export CXXFLAGS="-I$GITHUB_WORKSPACE/icu64/usr/include"
|
||||
export LDFLAGS="-L$GITHUB_WORKSPACE/icu64/usr/lib/x86_64-linux-gnu -Wl,-rpath=$GITHUB_WORKSPACE/icu64/usr/lib/x86_64-linux-gnu"
|
||||
export PKG_CONFIG_PATH=$GITHUB_WORKSPACE/icu64/usr/lib/x86_64-linux-gnu/pkgconfig
|
||||
cmake -H. -Bout/debug/x64 $BUILD_OPTIONS_X64
|
||||
ninja -Cout/debug/x86
|
||||
ninja -Cout/debug/x64
|
||||
- name: Run debug-mode test
|
||||
run: |
|
||||
export LD_LIBRARY_PATH=$GITHUB_WORKSPACE/icu32/lib
|
||||
$RUNNER --arch=x86 --engine="$GITHUB_WORKSPACE/out/debug/x86/escargot" modifiedVendorTest regression-tests new-es intl sunspider-js
|
||||
export LD_LIBRARY_PATH=$GITHUB_WORKSPACE/icu64/lib
|
||||
$RUNNER --arch=x86_64 --engine="$GITHUB_WORKSPACE/out/debug/x64/escargot" modifiedVendorTest regression-tests new-es intl sunspider-js
|
||||
|
||||
build-test-on-self-hosted-linux:
|
||||
if: github.repository == 'Samsung/escargot'
|
||||
runs-on: [self-hosted, linux, x64, test]
|
||||
runs-on: [self-hosted, linux, x64]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
@ -469,36 +365,26 @@ jobs:
|
|||
submodules: true
|
||||
- name: Build
|
||||
env:
|
||||
BUILD_OPTIONS: -DESCARGOT_THREADING=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TLS_ACCESS_BY_ADDRESS=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
BUILD_OPTIONS: -DESCARGOT_HOST=linux -DESCARGOT_THREADING=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
run: |
|
||||
LDFLAGS=" -L/usr/icu78-32/lib/ -Wl,-rpath=/usr/icu78-32/lib/" PKG_CONFIG_PATH="/usr/icu78-32/lib/pkgconfig/" cmake -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H./ -Bbuild/out_linux -DESCARGOT_ARCH=x86 -DESCARGOT_MODE=debug -DESCARGOT_TCO_DEBUG=ON $BUILD_OPTIONS
|
||||
LDFLAGS=" -L/usr/icu78-32/lib/ -Wl,-rpath=/usr/icu78-32/lib/" PKG_CONFIG_PATH="/usr/icu78-32/lib/pkgconfig/" cmake -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H./ -Bbuild/out_linux_release -DESCARGOT_ARCH=x86 $BUILD_OPTIONS
|
||||
LDFLAGS=" -L/usr/icu78-32/lib/ -Wl,-rpath=/usr/icu78-32/lib/" PKG_CONFIG_PATH="/usr/icu78-32/lib/pkgconfig/" cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H./ -Bbuild/out_linux_release_clang -DESCARGOT_ARCH=x86 $BUILD_OPTIONS
|
||||
LDFLAGS=" -L/usr/icu32/lib/ -Wl,-rpath=/usr/icu32/lib/" PKG_CONFIG_PATH="/usr/icu32/lib/pkgconfig/" cmake -H./ -Bbuild/out_linux -DESCARGOT_ARCH=x86 -DESCARGOT_MODE=debug -DESCARGOT_TCO_DEBUG=ON $BUILD_OPTIONS
|
||||
LDFLAGS=" -L/usr/icu32/lib/ -Wl,-rpath=/usr/icu32/lib/" PKG_CONFIG_PATH="/usr/icu32/lib/pkgconfig/" cmake -H./ -Bbuild/out_linux_release -DESCARGOT_ARCH=x86 $BUILD_OPTIONS
|
||||
gcc -shared -m32 -fPIC -o backtrace-hooking-32.so tools/test/test262/backtrace-hooking.c
|
||||
LDFLAGS=" -L/usr/icu78-64/lib/ -Wl,-rpath=/usr/icu78-64/lib/" PKG_CONFIG_PATH="/usr/icu78-64/lib/pkgconfig/" cmake -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H./ -Bbuild/out_linux64 -DESCARGOT_ARCH=x64 -DESCARGOT_MODE=debug -DESCARGOT_TCO_DEBUG=ON $BUILD_OPTIONS
|
||||
LDFLAGS=" -L/usr/icu78-64/lib/ -Wl,-rpath=/usr/icu78-64/lib/" PKG_CONFIG_PATH="/usr/icu78-64/lib/pkgconfig/" cmake -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H./ -Bbuild/out_linux64_release -DESCARGOT_ARCH=x64 -DESCARGOT_MODE=release $BUILD_OPTIONS
|
||||
LDFLAGS=" -L/usr/icu78-64/lib/ -Wl,-rpath=/usr/icu78-64/lib/" PKG_CONFIG_PATH="/usr/icu78-64/lib/pkgconfig/" cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H./ -Bbuild/out_linux64_release_clang -DESCARGOT_ARCH=x64 -DESCARGOT_MODE=release $BUILD_OPTIONS
|
||||
LDFLAGS=" -L/usr/icu64/lib/ -Wl,-rpath=/usr/icu64/lib/" PKG_CONFIG_PATH="/usr/icu64/lib/pkgconfig/" cmake -H./ -Bbuild/out_linux64 -DESCARGOT_ARCH=x64 -DESCARGOT_MODE=debug -DESCARGOT_TCO_DEBUG=ON $BUILD_OPTIONS
|
||||
LDFLAGS=" -L/usr/icu64/lib/ -Wl,-rpath=/usr/icu64/lib/" PKG_CONFIG_PATH="/usr/icu64/lib/pkgconfig/" cmake -H./ -Bbuild/out_linux64_release -DESCARGOT_ARCH=x64 -DESCARGOT_MODE=release $BUILD_OPTIONS
|
||||
cmake --build build/out_linux/
|
||||
cmake --build build/out_linux64/
|
||||
cmake --build build/out_linux_release/
|
||||
cmake --build build/out_linux_release_clang/
|
||||
cmake --build build/out_linux64_release/
|
||||
cmake --build build/out_linux64_release_clang/
|
||||
- name: Test
|
||||
run: |
|
||||
LD_LIBRARY_PATH=/usr/icu78-32/lib/ GC_FREE_SPACE_DIVISOR=1 $RUNNER --arch=x86 --engine="${{ github.workspace }}/build/out_linux_release/escargot" test262
|
||||
LD_LIBRARY_PATH=/usr/icu78-32/lib/ GC_FREE_SPACE_DIVISOR=1 $RUNNER --arch=x86 --engine="${{ github.workspace }}/build/out_linux_release_clang/escargot" test262
|
||||
LD_LIBRARY_PATH=/usr/icu78-32/lib/ GC_FREE_SPACE_DIVISOR=1 ESCARGOT_LD_PRELOAD=${{ github.workspace }}/backtrace-hooking-32.so $RUNNER --arch=x86 --engine="${{ github.workspace }}/build/out_linux/escargot" test262
|
||||
LD_LIBRARY_PATH=/usr/icu78-64/lib/ GC_FREE_SPACE_DIVISOR=1 $RUNNER --arch=x86_64 --engine="${{ github.workspace }}/build/out_linux64_release/escargot" test262
|
||||
LD_LIBRARY_PATH=/usr/icu78-64/lib/ GC_FREE_SPACE_DIVISOR=1 $RUNNER --arch=x86_64 --engine="${{ github.workspace }}/build/out_linux64_release_clang/escargot" test262
|
||||
LD_LIBRARY_PATH=/usr/icu78-64/lib/ python tools/kangax/run-kangax.py --engine="${{ github.workspace }}/build/out_linux64/escargot"
|
||||
LD_LIBRARY_PATH=/usr/icu32/lib/ GC_FREE_SPACE_DIVISOR=1 tools/run-tests.py --arch=x86 --engine="${{ github.workspace }}/build/out_linux_release/escargot" test262
|
||||
LD_LIBRARY_PATH=/usr/icu32/lib/ GC_FREE_SPACE_DIVISOR=1 ESCARGOT_LD_PRELOAD=${{ github.workspace }}/backtrace-hooking-32.so tools/run-tests.py --arch=x86 --engine="${{ github.workspace }}/build/out_linux/escargot" test262
|
||||
LD_LIBRARY_PATH=/usr/icu64/lib/ GC_FREE_SPACE_DIVISOR=1 tools/run-tests.py --arch=x86_64 --engine="${{ github.workspace }}/build/out_linux64_release/escargot" test262
|
||||
LD_LIBRARY_PATH=/usr/icu64/lib/ python tools/kangax/run-kangax.py --engine="${{ github.workspace }}/build/out_linux64/escargot"
|
||||
|
||||
build-test-on-self-hosted-arm-linux:
|
||||
if: github.repository == 'Samsung/escargot'
|
||||
runs-on: [self-hosted, linux, arm, test]
|
||||
strategy:
|
||||
matrix:
|
||||
compiler: [ {cxx: g++, cc: gcc}, {cxx: clang++, cc: clang} ]
|
||||
runs-on: [self-hosted, linux, arm]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
@ -506,18 +392,14 @@ jobs:
|
|||
submodules: true
|
||||
- name: Build
|
||||
run: |
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H./ -Bout -DCMAKE_C_COMPILER=${{ matrix.compiler.cc }} -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cxx }} -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=arm32 -DESCARGOT_MODE=release -DESCARGOT_THREADING=ON -DESCARGOT_TEMPORAL=OFF -DESCARGOT_TCO=ON -DESCARGOT_TLS_ACCESS_BY_ADDRESS=${{ matrix.compiler.tls }} -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
cmake -H./ -Bout -DCMAKE_C_COMPILER=gcc-11 -DCMAKE_CXX_COMPILER=g++-11 -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=arm32 -DESCARGOT_MODE=release -DESCARGOT_THREADING=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
cmake --build ./out/
|
||||
- name: Test
|
||||
run: |
|
||||
GC_FREE_SPACE_DIVISOR=1 $RUNNER --engine="${{ github.workspace }}/out/escargot" --test262-extra-arg="--skip intl402 --skip sm --skip Temporal" new-es v8 spidermonkey chakracore test262
|
||||
GC_FREE_SPACE_DIVISOR=1 tools/run-tests.py --engine="${{ github.workspace }}/out/escargot" --test262-extra-arg="--skip intl402" new-es v8 spidermonkey chakracore test262
|
||||
|
||||
build-test-on-self-hosted-arm64-linux:
|
||||
if: github.repository == 'Samsung/escargot'
|
||||
runs-on: [self-hosted, linux, arm64, test]
|
||||
strategy:
|
||||
matrix:
|
||||
compiler: [ {cxx: g++-11, cc: gcc-11}, {cxx: clang++, cc: clang} ]
|
||||
runs-on: [self-hosted, linux, arm64]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
|
@ -525,36 +407,11 @@ jobs:
|
|||
submodules: true
|
||||
- name: Build
|
||||
run: |
|
||||
LDFLAGS=" -L/usr/icu78-64/lib/ -Wl,-rpath=/usr/icu78-64/lib/" PKG_CONFIG_PATH="/usr/icu78-64/lib/pkgconfig/" cmake -DCMAKE_C_COMPILER=${{ matrix.compiler.cc }} -DCMAKE_CXX_COMPILER=${{ matrix.compiler.cxx }} -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H./ -Bout -DESCARGOT_MODE=release -DESCARGOT_THREADING=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
cmake -H./ -Bout -DESCARGOT_MODE=release -DESCARGOT_THREADING=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
cmake --build ./out/
|
||||
- name: Test
|
||||
run: |
|
||||
export LD_LIBRARY_PATH=/usr/icu78-64/lib/
|
||||
GC_FREE_SPACE_DIVISOR=1 $RUNNER --engine="${{ github.workspace }}/out/escargot" --test262-extra-arg="--skip intl402 --skip sm" test262 chakracore spidermonkey v8 new-es
|
||||
|
||||
build-test-on-riscv64-release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Build in riscv64 container
|
||||
uses: uraimo/run-on-arch-action@v3
|
||||
with:
|
||||
arch: riscv64
|
||||
distro: ubuntu22.04
|
||||
|
||||
# Install deps into the container. With the token, the container will be cached
|
||||
# The image is cached publically like a package
|
||||
githubToken: ${{ github.token }}
|
||||
|
||||
install: |
|
||||
apt-get update
|
||||
apt-get install -y cmake build-essential ninja-build pkg-config python3 git libicu-dev
|
||||
run: |
|
||||
cmake -H. -Bout/riscv64 -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
ninja -Cout/riscv64
|
||||
python3 ./tools/run-tests.py --engine="./out/riscv64/escargot" new-es intl sunspider-js
|
||||
GC_FREE_SPACE_DIVISOR=1 tools/run-tests.py --engine="${{ github.workspace }}/out/escargot" --test262-extra-arg="--skip intl402" test262 chakracore spidermonkey v8 new-es
|
||||
|
||||
build-test-debugger:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -568,9 +425,9 @@ jobs:
|
|||
sudo apt-get install -y ninja-build
|
||||
- name: Build
|
||||
env:
|
||||
BUILD_OPTIONS: -DESCARGOT_MODE=debug -DESCARGOT_DEBUGGER=1 -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
BUILD_OPTIONS: -DESCARGOT_MODE=debug -DESCARGOT_DEBUGGER=1 -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
run: |
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H. -Bout/debugger $BUILD_OPTIONS
|
||||
cmake -H. -Bout/debugger $BUILD_OPTIONS
|
||||
ninja -Cout/debugger
|
||||
- name: Debugger Test
|
||||
run: |
|
||||
|
|
@ -589,12 +446,12 @@ jobs:
|
|||
sudo apt-get install -y ninja-build gcc-multilib g++-multilib
|
||||
- name: Build x86/x64
|
||||
env:
|
||||
BUILD_OPTIONS_X86: -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=x86 -DESCARGOT_MODE=debug -DESCARGOT_THREADING=ON -DESCARGOT_DEBUGGER=1 -DESCARGOT_USE_EXTENDED_API=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=cctest -GNinja
|
||||
BUILD_OPTIONS_X86: -DCMAKE_SYSTEM_PROCESSOR=x86 -DESCARGOT_MODE=debug -DESCARGOT_THREADING=ON -DESCARGOT_DEBUGGER=1 -DESCARGOT_USE_EXTENDED_API=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=cctest -GNinja
|
||||
BUILD_OPTIONS_X64: -DESCARGOT_MODE=debug -DESCARGOT_THREADING=1 -DESCARGOT_DEBUGGER=1 -DESCARGOT_USE_EXTENDED_API=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=cctest -GNinja
|
||||
run: |
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H. -Bout/cctest/x86 $BUILD_OPTIONS_X86
|
||||
cmake -H. -Bout/cctest/x86 $BUILD_OPTIONS_X86
|
||||
ninja -Cout/cctest/x86
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H. -Bout/cctest/x64 $BUILD_OPTIONS_X64
|
||||
cmake -H. -Bout/cctest/x64 $BUILD_OPTIONS_X64
|
||||
ninja -Cout/cctest/x64
|
||||
- name: Run Test
|
||||
run: |
|
||||
|
|
@ -609,28 +466,40 @@ jobs:
|
|||
submodules: true
|
||||
- name: Install Packages
|
||||
run: |
|
||||
# for i386 ICU
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ninja-build gcc-multilib g++-multilib
|
||||
sudo apt-get install -y libicu-dev:i386 # install i386 ICU
|
||||
- name: Install ICU
|
||||
run: |
|
||||
wget http://mirrors.kernel.org/ubuntu/pool/main/i/icu/libicu-dev_70.1-2ubuntu1_i386.deb
|
||||
dpkg -X libicu-dev_70.1-2ubuntu1_i386.deb $GITHUB_WORKSPACE/icu32
|
||||
wget http://mirrors.kernel.org/ubuntu/pool/main/i/icu/libicu-dev_70.1-2ubuntu1_amd64.deb
|
||||
dpkg -X libicu-dev_70.1-2ubuntu1_amd64.deb $GITHUB_WORKSPACE/icu64
|
||||
- name: Build x86
|
||||
env:
|
||||
BUILD_OPTIONS: -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=x86 -DESCARGOT_MODE=debug -DESCARGOT_CODE_CACHE=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
BUILD_OPTIONS: -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=x86 -DESCARGOT_MODE=debug -DESCARGOT_LIBICU_SUPPORT_WITH_DLOPEN=OFF -DESCARGOT_CODE_CACHE=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
run: |
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H. -Bout/codecache/x86 $BUILD_OPTIONS
|
||||
export CXXFLAGS="-I$GITHUB_WORKSPACE/icu32/usr/include"
|
||||
export LDFLAGS="-L$GITHUB_WORKSPACE/icu32/usr/lib/i386-linux-gnu -Wl,-rpath=$GITHUB_WORKSPACE/icu32/usr/lib/i386-linux-gnu"
|
||||
export PKG_CONFIG_PATH=$GITHUB_WORKSPACE/icu32/usr/lib/i386-linux-gnu/pkgconfig
|
||||
cmake -H. -Bout/codecache/x86 $BUILD_OPTIONS
|
||||
ninja -Cout/codecache/x86
|
||||
- name: Build x64
|
||||
env:
|
||||
BUILD_OPTIONS: -DESCARGOT_MODE=debug -DESCARGOT_CODE_CACHE=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
BUILD_OPTIONS: -DESCARGOT_MODE=debug -DESCARGOT_CODE_CACHE=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
run: |
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H. -Bout/codecache/x64 $BUILD_OPTIONS
|
||||
export CXXFLAGS="-I$GITHUB_WORKSPACE/icu64/usr/include"
|
||||
export LDFLAGS="-L$GITHUB_WORKSPACE/icu64/usr/lib/x86_64-linux-gnu -Wl,-rpath=$GITHUB_WORKSPACE/icu64/usr/lib/x86_64-linux-gnu"
|
||||
export PKG_CONFIG_PATH=$GITHUB_WORKSPACE/icu64/usr/lib/x86_64-linux-gnu/pkgconfig
|
||||
cmake -H. -Bout/codecache/x64 $BUILD_OPTIONS
|
||||
ninja -Cout/codecache/x64
|
||||
- name: Build x64 Release Mode
|
||||
env:
|
||||
BUILD_OPTIONS: -DESCARGOT_MODE=release -DESCARGOT_CODE_CACHE=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
BUILD_OPTIONS: -DESCARGOT_MODE=release -DESCARGOT_CODE_CACHE=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
run: |
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H. -Bout/codecache/release/x64 $BUILD_OPTIONS
|
||||
export CXXFLAGS="-I$GITHUB_WORKSPACE/icu64/usr/include"
|
||||
export LDFLAGS="-L$GITHUB_WORKSPACE/icu64/usr/lib/x86_64-linux-gnu -Wl,-rpath=$GITHUB_WORKSPACE/icu64/usr/lib/x86_64-linux-gnu"
|
||||
export PKG_CONFIG_PATH=$GITHUB_WORKSPACE/icu64/usr/lib/x86_64-linux-gnu/pkgconfig
|
||||
cmake -H. -Bout/codecache/release/x64 $BUILD_OPTIONS
|
||||
ninja -Cout/codecache/release/x64
|
||||
- name: Run x86 test
|
||||
run: |
|
||||
|
|
@ -657,7 +526,7 @@ jobs:
|
|||
- name: Handle error cases
|
||||
run: |
|
||||
$RUNNER --arch=x86_64 --engine="$GITHUB_WORKSPACE/out/codecache/x64/escargot" sunspider-js
|
||||
rm $HOME/Escargot-cache/2728638815_17149
|
||||
rm $HOME/Escargot-cache/3217641879501852439_17149
|
||||
$RUNNER --arch=x86_64 --engine="$GITHUB_WORKSPACE/out/codecache/x64/escargot" sunspider-js
|
||||
ls -1q $HOME/Escargot-cache/ | wc -l
|
||||
$RUNNER --arch=x86_64 --engine="$GITHUB_WORKSPACE/out/codecache/x64/escargot" sunspider-js
|
||||
|
|
@ -676,41 +545,20 @@ jobs:
|
|||
- name: Install Packages
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ninja-build gcc-multilib g++-multilib make g++ pkg-config automake libtool git build-essential checkinstall libncurses-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev libffi-dev
|
||||
- name: Build ICU(32)
|
||||
run: |
|
||||
git clone --depth 1 --single-branch -b release-78.1 https://github.com/unicode-org/icu.git $GITHUB_WORKSPACE/icu-build/
|
||||
cd $GITHUB_WORKSPACE/icu-build/icu4c/source
|
||||
LDFLAGS="-m32 -Wl,-rpath=$GITHUB_WORKSPACE/icu32/lib/" CFLAGS="-m32" CXXFLAGS="-m32" ./runConfigureICU Linux/gcc --prefix="$GITHUB_WORKSPACE/icu32/"
|
||||
make -j8
|
||||
make install
|
||||
- name: Build ICU(64)
|
||||
run: |
|
||||
git clone --depth 1 --single-branch -b release-78.1 https://github.com/unicode-org/icu.git $GITHUB_WORKSPACE/icu64-build/
|
||||
cd $GITHUB_WORKSPACE/icu64-build/icu4c/source
|
||||
LDFLAGS="-Wl,-rpath=$GITHUB_WORKSPACE/icu64/lib/" ./runConfigureICU Linux/gcc --prefix="$GITHUB_WORKSPACE/icu64/"
|
||||
make -j8
|
||||
make install
|
||||
sudo apt-get install -y ninja-build gcc-multilib g++-multilib
|
||||
- name: Build x86
|
||||
env:
|
||||
BUILD_OPTIONS: -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=x86 -DESCARGOT_MODE=debug -DESCARGOT_WASM=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
BUILD_OPTIONS: -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=x86 -DESCARGOT_MODE=debug -DESCARGOT_WASM=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
run: |
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H. -Bout/wasm/x86 $BUILD_OPTIONS
|
||||
cmake -H. -Bout/wasm/x86 $BUILD_OPTIONS
|
||||
ninja -Cout/wasm/x86
|
||||
- name: Build x64
|
||||
env:
|
||||
BUILD_OPTIONS: -DESCARGOT_MODE=debug -DESCARGOT_WASM=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
BUILD_OPTIONS: -DESCARGOT_MODE=debug -DESCARGOT_WASM=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
run: |
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H. -Bout/wasm/x64 $BUILD_OPTIONS
|
||||
cmake -H. -Bout/wasm/x64 $BUILD_OPTIONS
|
||||
ninja -Cout/wasm/x64
|
||||
- name: Run x86 test
|
||||
run: |
|
||||
export LD_LIBRARY_PATH=$GITHUB_WORKSPACE/icu32/lib
|
||||
$RUNNER --arch=x86 --engine="$GITHUB_WORKSPACE/out/wasm/x86/escargot" wasm-js
|
||||
run: $RUNNER --arch=x86 --engine="$GITHUB_WORKSPACE/out/wasm/x86/escargot" wasm-js
|
||||
- name: Run x64 test
|
||||
run: |
|
||||
export LD_LIBRARY_PATH=$GITHUB_WORKSPACE/icu64/lib
|
||||
$RUNNER --arch=x86_64 --engine="$GITHUB_WORKSPACE/out/wasm/x64/escargot" wasm-js
|
||||
- if: ${{ failure() }}
|
||||
uses: mxschmitt/action-tmate@v3
|
||||
timeout-minutes: 60
|
||||
run: $RUNNER --arch=x86_64 --engine="$GITHUB_WORKSPACE/out/wasm/x64/escargot" wasm-js
|
||||
|
|
|
|||
11
.github/workflows/gbs.conf
vendored
11
.github/workflows/gbs.conf
vendored
|
|
@ -1,11 +0,0 @@
|
|||
[general]
|
||||
profile = profile.tizen
|
||||
|
||||
[profile.tizen]
|
||||
repos = repo.tizen_base_reference,repo.tizen_reference
|
||||
|
||||
[repo.tizen_reference]
|
||||
url = https://download.tizen.org/snapshots/TIZEN/Tizen/Tizen-Unified/reference/repos/standard/packages/
|
||||
|
||||
[repo.tizen_base_reference]
|
||||
url = https://download.tizen.org/snapshots/TIZEN/Tizen/Tizen-Base/reference/repos/standard/packages/
|
||||
322
.github/workflows/release.yml
vendored
322
.github/workflows/release.yml
vendored
|
|
@ -1,322 +0,0 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
RUNNER: tools/run-tests.py
|
||||
BUILD_OPTIONS: -DESCARGOT_MODE=release -DESCARGOT_LIBICU_SUPPORT_WITH_DLOPEN=OFF -DESCARGOT_DEPLOY=ON -DESCARGOT_THREADING=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_OUTPUT=shell -GNinja
|
||||
|
||||
jobs:
|
||||
build-macOS:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-15, macos-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Packages
|
||||
run: |
|
||||
brew update
|
||||
brew install ninja icu4c zip
|
||||
- name: Build x64
|
||||
run: |
|
||||
# check cpu
|
||||
sysctl -a | grep machdep.cpu
|
||||
# add icu path to pkg_config_path
|
||||
export PKG_CONFIG_PATH="$(brew --prefix icu4c)/lib/pkgconfig"
|
||||
echo $PKG_CONFIG_PATH
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.10 -H. -Bout/ $BUILD_OPTIONS
|
||||
ninja -Cout/
|
||||
- name: Check
|
||||
run: |
|
||||
file out/escargot
|
||||
strip out/escargot
|
||||
|
||||
# set deploy directory
|
||||
mkdir -p deploy
|
||||
|
||||
# set escargot
|
||||
cp out/escargot ./deploy/.
|
||||
LIBS=$(otool -L ./deploy/escargot | grep "icu" | awk '{print $1}')
|
||||
for LIB in $LIBS; do
|
||||
BASENAME=$(basename "$LIB")
|
||||
install_name_tool -change "$LIB" "@executable_path/$BASENAME" deploy/escargot
|
||||
done
|
||||
|
||||
# set icu libs
|
||||
ICU_LIBS=("libicuuc" "libicui18n" "libicudata")
|
||||
ICU_SOURCE_PATH="$(brew --prefix icu4c)/lib"
|
||||
ICU_VERSION=$(find "$ICU_SOURCE_PATH" -name "libicuuc.*.dylib" | grep -oE '\.[0-9]+\.' | head -n 1 | tr -d '.')
|
||||
|
||||
if [ -z "$ICU_VERSION" ]; then
|
||||
echo "ICU version could not be detected."
|
||||
exit 1
|
||||
else
|
||||
echo "Detected ICU Version: $ICU_VERSION"
|
||||
fi
|
||||
|
||||
for LIB in "${ICU_LIBS[@]}"; do
|
||||
cp -a $ICU_SOURCE_PATH/$LIB.*.dylib ./deploy/.
|
||||
install_name_tool -id "@loader_path/$LIB.$ICU_VERSION.dylib" "./deploy/$LIB.$ICU_VERSION.dylib"
|
||||
done
|
||||
|
||||
# check results
|
||||
echo "Check results..."
|
||||
ls ./deploy
|
||||
otool -L ./deploy/escargot
|
||||
otool -L ./deploy/libicu*.dylib
|
||||
|
||||
# Ad-hoc sign
|
||||
set -e
|
||||
cd deploy
|
||||
for f in libicu*.dylib; do [ -e "$f" ] || continue; codesign -f -s - "$f"; done
|
||||
codesign --force --deep -s - ./escargot
|
||||
for f in ./escargot libicu*.dylib; do [ -e "$f" ] || continue; codesign --verify --strict --verbose=6 "$f"; done
|
||||
cd ..
|
||||
|
||||
# run test
|
||||
$RUNNER --engine="$GITHUB_WORKSPACE/deploy/escargot" new-es
|
||||
|
||||
# zip results
|
||||
if [ "${{ matrix.os }}" == "macos-15" ]; then
|
||||
zip -j escargot-mac64.zip deploy/*
|
||||
elif [ "${{ matrix.os }}" == "macos-latest" ]; then
|
||||
zip -j escargot-mac64arm.zip deploy/*
|
||||
fi
|
||||
- name: Upload mac64
|
||||
if: ${{ matrix.os == 'macos-15' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-artifact-mac64
|
||||
path: ./escargot-mac64.zip
|
||||
- name: Upload mac64arm
|
||||
if: ${{ matrix.os == 'macos-latest' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-artifact-mac64arm
|
||||
path: ./escargot-mac64arm.zip
|
||||
|
||||
build-linux:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Install Packages
|
||||
run: |
|
||||
# for i386 ICU
|
||||
sudo dpkg --add-architecture i386
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ninja-build libicu-dev gcc-multilib g++-multilib zip patchelf
|
||||
sudo apt-get install -y libicu-dev:i386 # install i386 ICU
|
||||
- name: Build x86/x64
|
||||
run: |
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H. -Bout/x86 -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=x86 -DESCARGOT_TEMPORAL=ON $BUILD_OPTIONS
|
||||
cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 -H. -Bout/x64 -DESCARGOT_TEMPORAL=ON $BUILD_OPTIONS
|
||||
ninja -Cout/x86
|
||||
ninja -Cout/x64
|
||||
- name: Check
|
||||
run: |
|
||||
file out/x86/escargot
|
||||
file out/x64/escargot
|
||||
strip out/x86/escargot
|
||||
strip out/x64/escargot
|
||||
# set locale
|
||||
sudo locale-gen en_US.UTF-8
|
||||
export LANG=en_US.UTF-8
|
||||
locale
|
||||
|
||||
# set deploy directory and copy escargot binary
|
||||
mkdir -p deploy-x86
|
||||
mkdir -p deploy-x64
|
||||
cp out/x86/escargot ./deploy-x86/.
|
||||
cp out/x64/escargot ./deploy-x64/.
|
||||
|
||||
# set icu libs
|
||||
ldd deploy-x86/escargot | grep "icu" | grep "=>" | awk '{print $3}' | xargs -I '{}' cp '{}' deploy-x86/
|
||||
ldd deploy-x64/escargot | grep "icu" | grep "=>" | awk '{print $3}' | xargs -I '{}' cp '{}' deploy-x64/
|
||||
for LIB in ./deploy-x86/libicu*; do
|
||||
patchelf --set-rpath '$ORIGIN' "$LIB"
|
||||
done
|
||||
for LIB in ./deploy-x64/libicu*; do
|
||||
patchelf --set-rpath '$ORIGIN' "$LIB"
|
||||
done
|
||||
|
||||
# check results
|
||||
echo "Check results..."
|
||||
ls ./deploy-x86
|
||||
ldd deploy-x86/escargot
|
||||
ldd deploy-x86/libicu*
|
||||
ls ./deploy-x64
|
||||
ldd deploy-x64/escargot
|
||||
ldd deploy-x64/libicu*
|
||||
|
||||
# run test
|
||||
$RUNNER --engine="$GITHUB_WORKSPACE/deploy-x86/escargot" new-es
|
||||
$RUNNER --engine="$GITHUB_WORKSPACE/deploy-x64/escargot" new-es
|
||||
|
||||
# zip results
|
||||
zip -j escargot-linux-x86.zip deploy-x86/*
|
||||
zip -j escargot-linux-x64.zip deploy-x64/*
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-artifact-linux
|
||||
path: escargot-linux-*.zip
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-2022
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86, x64]
|
||||
steps:
|
||||
- name: Set git cllf config
|
||||
run: |
|
||||
git config --global core.autocrlf input
|
||||
git config --global core.eol lf
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- uses: szenius/set-timezone@v2.0
|
||||
with:
|
||||
timezoneWindows: "Pacific Standard Time"
|
||||
- uses: lukka/get-cmake@latest
|
||||
with:
|
||||
cmakeVersion: "~3.25.0"
|
||||
- uses: GuillaumeFalourd/setup-windows10-sdk-action@v2
|
||||
with:
|
||||
sdk-version: 26100
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Download and Install Visual C++ Redistributable
|
||||
shell: powershell
|
||||
run: |
|
||||
$vcRedistUrl64 = "https://aka.ms/vs/17/release/vc_redist.x64.exe" # Or the appropriate URL for your target architecture/version
|
||||
$vcRedistPath64 = "$env:TEMP\vc_redist.x64.exe"
|
||||
$vcRedistUrl32 = "https://aka.ms/vs/17/release/vc_redist.x86.exe" # Or the appropriate URL for your target architecture/version
|
||||
$vcRedistPath32 = "$env:TEMP\vc_redist.x86.exe"
|
||||
|
||||
Write-Host "Downloading Visual C++ Redistributable from $vcRedistUrl64"
|
||||
Invoke-WebRequest -Uri $vcRedistUrl64 -OutFile $vcRedistPath64
|
||||
|
||||
Write-Host "Downloading Visual C++ Redistributable from $vcRedistUrl32"
|
||||
Invoke-WebRequest -Uri $vcRedistUrl32 -OutFile $vcRedistPath32
|
||||
|
||||
Write-Host "Installing Visual C++ Redistributable silently"
|
||||
Start-Process -FilePath $vcRedistPath64 -ArgumentList "/install /quiet /norestart" -Wait
|
||||
Start-Process -FilePath $vcRedistPath32 -ArgumentList "/install /quiet /norestart" -Wait
|
||||
Write-Host "Visual C++ Redistributable installation complete."
|
||||
- uses: ilammy/msvc-dev-cmd@v1.13.0
|
||||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
sdk: "10.0.26100.0"
|
||||
- name: Install zip if not available
|
||||
run: |
|
||||
if (-Not (Get-Command zip -ErrorAction SilentlyContinue)) {
|
||||
choco install zip -y
|
||||
}
|
||||
- name: Build ${{ matrix.arch }}
|
||||
run: |
|
||||
CMake -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_SYSTEM_VERSION:STRING="10.0" -DCMAKE_SYSTEM_PROCESSOR=${{ matrix.arch }} -DESCARGOT_ARCH=${{ matrix.arch }} -Bout/ -DESCARGOT_OUTPUT=shell -DESCARGOT_LIBICU_SUPPORT=ON -DESCARGOT_WASM=ON -DESCARGOT_THREADING=ON -DESCARGOT_TCO=ON -DESCARGOT_TEST=ON -DESCARGOT_SHADOWREALM=ON -G Ninja -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -DCMAKE_BUILD_TYPE=release
|
||||
CMake --build out/ --config Release
|
||||
- name: Check
|
||||
run: |
|
||||
python tools\run-tests.py --engine=%cd%\out\escargot.exe new-es
|
||||
rename out\escargot.exe escargot-win-${{ matrix.arch }}.exe
|
||||
zip -j escargot-win-${{ matrix.arch}}.zip out\escargot-win-${{ matrix.arch }}.exe
|
||||
shell: cmd
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-artifact-win-${{ matrix.arch }}
|
||||
path: escargot-win-${{ matrix.arch }}.zip
|
||||
|
||||
check-build-mac64:
|
||||
needs: [build-macOS]
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
pattern: build-artifact-mac64
|
||||
merge-multiple: true
|
||||
- name: Check
|
||||
run: |
|
||||
unzip artifacts/escargot-mac64.zip -d artifacts
|
||||
otool -L artifacts/escargot
|
||||
otool -L artifacts/*.dylib
|
||||
$RUNNER --engine="$GITHUB_WORKSPACE/artifacts/escargot" new-es
|
||||
|
||||
check-build-mac64arm:
|
||||
needs: [build-macOS]
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
pattern: build-artifact-mac64arm
|
||||
merge-multiple: true
|
||||
- name: Check
|
||||
run: |
|
||||
unzip artifacts/escargot-mac64arm.zip -d artifacts
|
||||
otool -L artifacts/escargot
|
||||
otool -L artifacts/*.dylib
|
||||
$RUNNER --engine="$GITHUB_WORKSPACE/artifacts/escargot" new-es
|
||||
|
||||
check-build-linux:
|
||||
needs: [build-linux]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
pattern: build-artifact-linux
|
||||
merge-multiple: true
|
||||
- name: Check
|
||||
run: |
|
||||
dpkg -l | grep libicu-dev
|
||||
mkdir -p result-x86
|
||||
mkdir -p result-x64
|
||||
unzip artifacts/escargot-linux-x86.zip -d result-x86
|
||||
unzip artifacts/escargot-linux-x64.zip -d result-x64
|
||||
ldd result-x86/escargot
|
||||
ldd result-x86/libicu*
|
||||
ldd result-x64/escargot
|
||||
ldd result-x64/libicu*
|
||||
$RUNNER --engine="$GITHUB_WORKSPACE/result-x86/escargot" new-es
|
||||
$RUNNER --engine="$GITHUB_WORKSPACE/result-x64/escargot" new-es
|
||||
|
||||
update-release:
|
||||
needs: [check-build-mac64, check-build-mac64arm, check-build-linux, build-windows]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
pattern: build-artifact-*
|
||||
merge-multiple: true
|
||||
- name: Upload to release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
artifacts/escargot-*
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -1,5 +1,5 @@
|
|||
#error_report
|
||||
/escargot
|
||||
escargot
|
||||
escargot.x*
|
||||
.project
|
||||
.cproject
|
||||
|
|
@ -18,7 +18,7 @@ cscope.out
|
|||
#memps
|
||||
escargot.asm
|
||||
escargot.elf
|
||||
out/*
|
||||
out
|
||||
build.ninja
|
||||
rules.ninja
|
||||
.ninja_deps
|
||||
|
|
@ -40,4 +40,3 @@ cmake_install.cmake
|
|||
EscargotInfo.h
|
||||
escargot.pc
|
||||
escargot_generated/*
|
||||
.vs/*
|
||||
|
|
@ -44,9 +44,12 @@ ENDIF()
|
|||
IF (NOT DEFINED ESCARGOT_OUTPUT)
|
||||
SET (ESCARGOT_OUTPUT "shell")
|
||||
ENDIF()
|
||||
IF (NOT DEFINED ESCARGOT_THREADING)
|
||||
SET (ESCARGOT_THREADING ON)
|
||||
ENDIF()
|
||||
|
||||
|
||||
MESSAGE(STATUS "Escargot Arch: " ${ESCARGOT_ARCH})
|
||||
MESSAGE(STATUS "Escargot Host: " ${ESCARGOT_HOST})
|
||||
MESSAGE(STATUS "Escargot Mode: " ${ESCARGOT_MODE})
|
||||
MESSAGE(STATUS "Escargot Output: " ${ESCARGOT_OUTPUT})
|
||||
|
||||
SET (ESCARGOT_TARGET escargot)
|
||||
SET (ESCARGOT_CCTEST_TARGET cctest)
|
||||
|
|
@ -60,31 +63,3 @@ INCLUDE (${PROJECT_SOURCE_DIR}/build/escargot.cmake)
|
|||
|
||||
# Pkgconfig
|
||||
CONFIGURE_FILE(${PROJECT_SOURCE_DIR}/packaging/escargot.pc.in ${CMAKE_BINARY_DIR}/escargot.pc @ONLY)
|
||||
|
||||
IF (ESCARGOT_TLS_ACCESS_BY_PTHREAD_KEY AND ESCARGOT_TLS_ACCESS_BY_ADDRESS)
|
||||
MESSAGE(FATAL_ERROR "You cannot enable ESCARGOT_TLS_ACCESS_BY_PTHREAD_KEY and ESCARGOT_TLS_ACCESS_BY_ADDRESS at same time")
|
||||
ENDIF()
|
||||
|
||||
MESSAGE(STATUS "Escargot Arch: " ${ESCARGOT_ARCH})
|
||||
MESSAGE(STATUS "Escargot Host: " ${ESCARGOT_HOST})
|
||||
MESSAGE(STATUS "Escargot Mode: " ${ESCARGOT_MODE})
|
||||
MESSAGE(STATUS "Escargot Output: " ${ESCARGOT_OUTPUT})
|
||||
MESSAGE(STATUS "--------------------------------------------------------------------------------")
|
||||
MESSAGE(STATUS "ESCARGOT_DEFINITIONS: " ${ESCARGOT_DEFINITIONS})
|
||||
MESSAGE(STATUS "ESCARGOT_CXXFLAGS: " ${ESCARGOT_CXXFLAGS})
|
||||
MESSAGE(STATUS "ESCARGOT_LDFLAGS: " ${ESCARGOT_LDFLAGS})
|
||||
MESSAGE(STATUS "ESCARGOT_INCDIRS: " ${ESCARGOT_INCDIRS})
|
||||
MESSAGE(STATUS "ESCARGOT_LIBRARIES: " ${ESCARGOT_LIBRARIES})
|
||||
MESSAGE(STATUS "ESCARGOT_LIBICU_SUPPORT: " ${ESCARGOT_LIBICU_SUPPORT})
|
||||
MESSAGE(STATUS "ESCARGOT_LIBICU_SUPPORT_WITH_DLOPEN: " ${ESCARGOT_LIBICU_SUPPORT_WITH_DLOPEN})
|
||||
MESSAGE(STATUS "ESCARGOT_SMALL_CONFIG: " ${ESCARGOT_SMALL_CONFIG})
|
||||
MESSAGE(STATUS "ESCARGOT_CODE_CACHE: " ${ESCARGOT_CODE_CACHE})
|
||||
MESSAGE(STATUS "ESCARGOT_WASM: " ${ESCARGOT_WASM})
|
||||
MESSAGE(STATUS "ESCARGOT_THREADING: " ${ESCARGOT_THREADING})
|
||||
MESSAGE(STATUS "ESCARGOT_TLS_ACCESS_BY_ADDRESS: " ${ESCARGOT_TLS_ACCESS_BY_ADDRESS})
|
||||
MESSAGE(STATUS "ESCARGOT_TLS_ACCESS_BY_PTHREAD_KEY: " ${ESCARGOT_TLS_ACCESS_BY_PTHREAD_KEY})
|
||||
MESSAGE(STATUS "ESCARGOT_EXPORT_ALL: " ${ESCARGOT_EXPORT_ALL})
|
||||
MESSAGE(STATUS "ESCARGOT_TCO: " ${ESCARGOT_TCO})
|
||||
MESSAGE(STATUS "ESCARGOT_TEMPORAL: " ${ESCARGOT_TEMPORAL})
|
||||
MESSAGE(STATUS "ESCARGOT_SHADOWREALM: " ${ESCARGOT_SHADOWREALM})
|
||||
MESSAGE(STATUS "ESCARGOT_TEST: " ${ESCARGOT_TEST})
|
||||
|
|
|
|||
|
|
@ -1,33 +1,30 @@
|
|||
MIT-style License
|
||||
|
||||
Copyright (c) 1988-1989 Hans-J. Boehm, Alan J. Demers
|
||||
Copyright (c) 1988, 1989 Hans-J. Boehm, Alan J. Demers
|
||||
Copyright (c) 1991-1996 by Xerox Corporation. All rights reserved.
|
||||
Copyright (c) 1996-1999 by Silicon Graphics. All rights reserved.
|
||||
Copyright (c) 1998 by Fergus Henderson. All rights reserved.
|
||||
Copyright (c) 1999-2001 by Red Hat, Inc. All rights reserved.
|
||||
Copyright (c) 1999-2011 Hewlett-Packard Development Company, L.P.
|
||||
Copyright (c) 2004-2005 Andrei Polushin
|
||||
Copyright (c) 2007 Free Software Foundation, Inc.
|
||||
Copyright (c) 2008-2022 Ivan Maidanski
|
||||
Copyright (c) 2011 Ludovic Courtes
|
||||
Copyright (c) 2018 Petter A. Urkedal
|
||||
Copyright (c) 1999-2004 Hewlett-Packard Development Company, L.P.
|
||||
|
||||
The file linux_threads.c is also
|
||||
Copyright (c) 1998 by Fergus Henderson. All rights reserved.
|
||||
|
||||
The files Makefile.am, and configure.in are
|
||||
Copyright (c) 2001 by Red Hat Inc. All rights reserved.
|
||||
|
||||
Several files supporting GNU-style builds are copyrighted by the Free
|
||||
Software Foundation, and carry a different license from that given
|
||||
below.
|
||||
|
||||
THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
|
||||
OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
|
||||
|
||||
Permission is hereby granted to use or copy this program
|
||||
for any purpose, provided the above notices are retained on all copies.
|
||||
for any purpose, provided the above notices are retained on all copies.
|
||||
Permission to modify the code and to distribute modified code is granted,
|
||||
provided the above notices are retained, and a notice that the code was
|
||||
modified is included with the above copyright notice.
|
||||
|
||||
|
||||
Several files (gc/gc_allocator.h, extra/msvc_dbg.c) come with slightly
|
||||
different licenses, though they are all similar in spirit (the exact
|
||||
licensing terms are given at the beginning of the corresponding source file).
|
||||
|
||||
A few of the files needed to use the GNU-style build procedure come with
|
||||
a modified GPL license that appears not to significantly restrict use of
|
||||
the collector, though use of those files for a purpose other than building
|
||||
the collector may require the resulting code to be covered by the GPL.
|
||||
slightly different licenses, though they are all similar in spirit. A few
|
||||
are GPL'ed, but with an exception that should cover all uses in the
|
||||
collector. (If you are concerned about such things, I recommend you look
|
||||
at the notice in config.guess or ltmain.sh.)
|
||||
|
||||
|
|
|
|||
185
README.md
185
README.md
|
|
@ -6,67 +6,13 @@
|
|||
[](https://scan.coverity.com/projects/samsung-escargot)
|
||||
[](https://codecov.io/gh/Samsung/escargot)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Escargot is a lightweight JavaScript engine developed by [Samsung](https://github.com/Samsung), designed specifically for resource-constrained environments. It is optimized for performance and low memory usage, making it ideal for use in embedded systems, IoT devices, and other applications where resources are limited.
|
||||
|
||||
Key features of Escargot include:
|
||||
* **ECMAScript Compliance**: Escargot supports a significant portion of the latest ECMAScript version ([ECMAScript 2025](https://262.ecma-international.org/16.0/)), ensuring compatibility with modern JavaScript standards while maintaining a lightweight footprint.
|
||||
* **Memory Efficiency**: The engine is designed with memory constraints in mind, making it suitable for devices with limited RAM and storage.
|
||||
* **Performance Optimization**: Escargot implements various optimization techniques to ensure fast execution of JavaScript code, even on low-power devices.
|
||||
* **Extensibility**: The engine can be customized and extended to meet the specific needs of different applications, providing flexibility for developers.
|
||||
|
||||
Escargot is an open-source project that allows developers to contribute to its development or use it in their own projects, while also powering several services in Samsung products. The engine's design prioritizes simplicity and efficiency, making it an excellent choice for developers working in embedded or resource-limited environments.
|
||||
|
||||
|
||||
## Contents 📋
|
||||
* [Building](#Building-)
|
||||
* [Linux](#Linux)
|
||||
* [macOS](#macOS)
|
||||
* [Android](#Android)
|
||||
* [Windows](#Windows)
|
||||
* [Testing](#Testing-)
|
||||
* [Contributing](#Contributing-)
|
||||
* [Research Papers](#Research-Papers-)
|
||||
* [License](#License-)
|
||||
|
||||
## Building 🛠️
|
||||
|
||||
### Supported Platforms and Architectures
|
||||
| **OS** | **Architecture** |
|
||||
|-|-|
|
||||
| **Linux(Ubuntu)** | x86/x64/arm/aarch64 |
|
||||
| macOS | x64/aarch64 |
|
||||
| Windows | Win32/x64 |
|
||||
| Android | x86/x64/arm/aarch64 |
|
||||
|
||||
### Build Options
|
||||
|
||||
The following build options are supported when generating build rules using cmake.
|
||||
|
||||
| **Option** | **Description** | **Flag** | **Value** | **Default** |
|
||||
|-|-|-|-|-|
|
||||
| **HOST** | Choose target platform | -DESCARGOT_HOST | linux/darwin/android/windows | |
|
||||
| **ARCH** | Choose target architecture | -DESCARGOT_ARCH | x64/x86/arm/aarch64 | |
|
||||
| **MODE** | Choose release/debug mode | -DESCARGOT_MODE | release/debug | release |
|
||||
| **OUTPUT** | Choose build output type | -DESCARGOT_OUTPUT | shared_lib/static_lib/shell/cctest | shell |
|
||||
| **LIBICU** | Include libicu library | -DESCARGOT_LIBICU_SUPPORT | ON/OFF | ON |
|
||||
| **WASM** | Enable WebAssembly support | -DESCARGOT_WASM | ON/OFF | OFF |
|
||||
| **CODE_CACHE** | Enable code cache | -DESCARGOT_CODE_CACHE | ON/OFF | OFF |
|
||||
| **TCO** | Enable tail call optimization | -DESCARGOT_TCO | ON/OFF | OFF |
|
||||
| **THREADING** | Enable threading features (e.g. Atomics, SharedArrayBuffer) | -DESCARGOT_THREADING | ON/OFF | ON |
|
||||
| **TLS_ADDRESS_OFFSET** | Enable thread local storge access optimization(offset) | -DESCARGOT_TLS_ACCESS_BY_ADDRESS | ON/OFF | OFF |
|
||||
| **TLS_PTHREAD_KEY** | Enable thread local storge access optimization(pthread_key) | -DESCARGOT_TLS_ACCESS_BY_PTHREAD_KEY | ON/OFF | OFF |
|
||||
| **TEMPORAL** | Enable Temporal support | -ESCARGOT_TEMPORAL | ON/OFF | OFF |
|
||||
| **SHADOWREALM** | Enable ShadowRealm support | -ESCARGOT_SHADOWREALM | ON/OFF | OFF |
|
||||
| **SMALL_CONFIG** | Enable aggressive memory optimizations for tiny devices | -DESCARGOT_SMALL_CONFIG | ON/OFF | OFF |
|
||||
| **TEST** | Enable additional features used only for testing | -DESCARGOT_TEST | ON/OFF | OFF |
|
||||
| **DEBUGGER** | Enable Debug server | -DESCARGOT_DEBUGGER | ON/OFF | OFF |
|
||||
|
||||
### Linux
|
||||
#### On Ubuntu Linux
|
||||
|
||||
General build prerequisites:
|
||||
```sh
|
||||
sudo apt-get install autoconf automake cmake libtool libicu-dev ninja-build pkg-config
|
||||
sudo apt-get install autoconf automake cmake libtool libicu-dev ninja-build
|
||||
```
|
||||
|
||||
Prerequisites for x86-64-to-x86 compilation:
|
||||
|
|
@ -75,120 +21,85 @@ sudo apt-get install gcc-multilib g++-multilib
|
|||
sudo apt-get install libicu-dev:i386
|
||||
```
|
||||
|
||||
Build Escargot:
|
||||
```sh
|
||||
git submodule update --init third_party # update submodules
|
||||
cmake -DESCARGOT_MODE=release -DESCARGOT_OUTPUT=shell -GNinja
|
||||
ninja
|
||||
```
|
||||
#### On macOS
|
||||
|
||||
### macOS
|
||||
|
||||
General build prerequisites:
|
||||
```sh
|
||||
brew install autoconf automake cmake icu4c libtool ninja pkg-config
|
||||
|
||||
# add icu path to pkg_config_path (x64)
|
||||
export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
# add icu path to pkg_config_path (arm64)
|
||||
export PKG_CONFIG_PATH="/opt/homebrew/opt/icu4c/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
```
|
||||
|
||||
Build Escargot:
|
||||
## Build Escargot
|
||||
|
||||
```sh
|
||||
git submodule update --init third_party # update submodules
|
||||
git clone https://github.com/Samsung/escargot.git
|
||||
cd escargot
|
||||
git submodule update --init third_party
|
||||
cmake -DESCARGOT_MODE=release -DESCARGOT_OUTPUT=shell -GNinja
|
||||
ninja
|
||||
```
|
||||
|
||||
### Android
|
||||
## Build Android version
|
||||
|
||||
Build prerequisites on Ubuntu:
|
||||
```sh
|
||||
sudo apt install openjdk-17-jdk # require java 17
|
||||
```
|
||||
|
||||
Build Escargot using gradle:
|
||||
```sh
|
||||
git submodule update --init third_party # update submodules
|
||||
git clone https://github.com/Samsung/escargot.git
|
||||
cd escargot
|
||||
git submodule update --init third_party
|
||||
export ANDROID_SDK_ROOT=.... # set your android SDK root first
|
||||
cd build/android/
|
||||
./gradlew bundleReleaseAar # build escargot AAR
|
||||
./gradlew bundleHostJar # bundle jar for host
|
||||
./gradlew javadocJar # create java doc
|
||||
./gradlew sourcesJar # create sources jar
|
||||
|
||||
./gradlew assembleDebug # build debug test shell
|
||||
./gradlew :escargot:connectedDebugAndroidTest # run escargot-jni tests on android device
|
||||
./gradlew :escargot:testDebugUnitTest # run escargot-jni tests on host
|
||||
./gradlew bundleHostJar # bundle jar for host
|
||||
```
|
||||
|
||||
### Windows
|
||||
## Build Windows version
|
||||
|
||||
Install VS2022 with cmake and ninja.
|
||||
Open [ x86 Native Tools Command Prompt for VS 2022 | x64 Native Tools Command Prompt for VS 2022 ]
|
||||
|
||||
```sh
|
||||
git submodule update --init third_party # update submodules
|
||||
git clone https://github.com/Samsung/escargot.git
|
||||
cd escargot
|
||||
git submodule update --init third_party
|
||||
|
||||
CMake -G "Visual Studio 17 2022" -DCMAKE_SYSTEM_NAME=[ Windows | WindowsStore ] -DCMAKE_SYSTEM_VERSION:STRING="10.0" -DCMAKE_SYSTEM_PROCESSOR=[ x86 | x64 ] -DCMAKE_GENERATOR_PLATFORM=[ Win32 | x64 ],version=10.0.18362.0 -DESCARGOT_ARCH=[ x86 | x64 ] -DESCARGOT_MODE=release -Bout -DESCARGOT_HOST=windows -DESCARGOT_OUTPUT=shell -DESCARGOT_LIBICU_SUPPORT=ON -DESCARGOT_THREADING=ON
|
||||
CMake -G "Visual Studio 17 2022" -DCMAKE_SYSTEM_NAME=[ Windows | WindowsStore ] -DCMAKE_SYSTEM_VERSION:STRING="10.0" -DCMAKE_SYSTEM_PROCESSOR=[ x86 | x64 ] -DCMAKE_GENERATOR_PLATFORM=[ Win32 | x64 ],version=10.0.18362.0 -DESCARGOT_ARCH=[ x86 | x64 ] -DESCARGOT_MODE=release -Bout -DESCARGOT_HOST=windows -DESCARGOT_OUTPUT=shell -DESCARGOT_LIBICU_SUPPORT=ON -DESCARGOT_LIBICU_SUPPORT_WITH_DLOPEN=OFF -DESCARGOT_THREADING=ON
|
||||
cd out
|
||||
msbuild ESCARGOT.sln /property:Configuration=Release /p:platform=[ Win32 | x64 ]
|
||||
```
|
||||
|
||||
## Debugger
|
||||
#### Build options
|
||||
|
||||
Make sure Escargot is built with the `-DESCARGOT_DEBUGGER=1` flag (off by default) enabled;
|
||||
then start Escargot with the `--start-debug-server` option.
|
||||
The following build options are supported when generating ninja rules using cmake.
|
||||
|
||||
### Connect using a debugger client
|
||||
* -DESCARGOT_HOST=[ linux | tizen_obs | darwin | android | windows ]<br>
|
||||
Compile Escargot for Linux, Tizen, macOS, or Windows platform
|
||||
* -DESCARGOT_ARCH=[ x64 | x86 | arm | i686 | aarch64 ]<br>
|
||||
Compile Escargot for each architecture
|
||||
* -DESCARGOT_MODE=[ debug | release ]<br>
|
||||
Compile Escargot for either release or debug mode
|
||||
* -DESCARGOT_OUTPUT=[ shared_lib | static_lib | shell | cctest ]<br>
|
||||
Define target output type
|
||||
* -DESCARGOT_LIBICU_SUPPORT=[ ON | OFF ]<br>
|
||||
Enable libicu library if set ON. (Optional, default = ON)
|
||||
* -DESCARGOT_THREADING=[ ON | OFF ]<br>
|
||||
Enable Threading support. (Optional, default = OFF)
|
||||
* -DESCARGOT_CODE_CACHE=[ ON | OFF ]<br>
|
||||
Enable Code cache support. (Optional, default = OFF)
|
||||
* -DESCARGOT_WASM=[ ON | OFF ]<br>
|
||||
Enable WASM support. (Optional, default = OFF)
|
||||
* -DESCARGOT_SMALL_CONFIG=[ ON | OFF ]<br>
|
||||
Enable Options for small devices. (Optional, default = OFF)
|
||||
|
||||
- Escargot python debugger
|
||||
- run `./tools/debugger/debugger.py`; It will automatically connect to a debug server on the default port `6501`
|
||||
- run `./tools/debugger/debugger.py --help` for a list of options
|
||||
- [Visual Studio Code extension](https://github.com/Samsung/escargot-vscode-extension/?tab=readme-ov-file#how-to-use)
|
||||
- Chrome Devtools `⚠️ Early in development ⚠️`
|
||||
- Initial setup:
|
||||
- Navigate to [chrome://inspect](chrome://inspect)
|
||||
- Make sure *Discover network targets* is enabled; click configure
|
||||
- Add `localhost:6501` as a target; click Done
|
||||
- Usage:
|
||||
- The started debug server will be listed in the *Remote Target* list (If it is not, the page may need to be reloaded using the browser reload button)
|
||||
- Click `inspect`
|
||||
- A new window with the Chrome Devtools debugger UI will open
|
||||
## Testing
|
||||
|
||||
## Testing ✅
|
||||
|
||||
Escargot supports various benchmark sets, which can be run using the [tools/run-tests.py](https://github.com/Samsung/escargot/blob/master/tools/run-tests.py) script.
|
||||
|
||||
| Benchmark | flag |
|
||||
| --- | --- |
|
||||
| SunSpider 1.0.2 | `sunspider` |
|
||||
| [Octane 2.0](https://github.com/chromium/octane.git) | `octane` |
|
||||
| [test262](https://github.com/tc39/test262.git) | `test262` |
|
||||
| [Web Tooling Benchmark](https://github.com/v8/web-tooling-benchmark) | `web-tooling-benchmark` |
|
||||
| SpiderMonkey (vendor-made) | `spidermonkey` |
|
||||
| ChakraCore (vendor-made) | `chakracore` |
|
||||
| V8 (vendor-made) | `v8` |
|
||||
|
||||
Run each benchmark separately or all together as shown below:
|
||||
First, get benchmarks and tests:
|
||||
```sh
|
||||
tools/run-tests.py --engine=./out/linux/x64/release/escargot web-tooling-benchmark
|
||||
tools/run-tests.py --engine=./out/linux/x64/release/escargot spidermonkey test262 v8
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
## Contributing 💡
|
||||
Escargot welcomes contributions from developers in any form, wheter it's code, documentation, bug reports, or suggestions. By contributing to the project, you agree to license your contributions under the [LGPL-2.1](https://github.com/Samsung/escargot/blob/master/LICENSE) license.
|
||||
### Benchmarks
|
||||
|
||||
#### ❗ Vulnerability Reporting
|
||||
⚠️ If you identify any vulnerabilities, please report them through the [Issues page](https://github.com/Samsung/escargot/issues). *Reports sent via other channels may not be considered or may be processed with delays*. Please note that our project assumes the execution of valid JavaScript source code only. Handling of invalid source code is not within the main scope of this project and might not be addressed.
|
||||
|
||||
## Research Papers 📝
|
||||
* [Dynamic code compression for JavaScript engine](https://doi.org/10.1002/spe.3186)
|
||||
Software: Practice and Experience Vol. 53 (5), pp. 1196-1217, 2023
|
||||
|
||||
* [Tail Call Optimization Tailored for Native Stack Utilization in JavaScript Runtimes](https://doi.org/10.1109/ACCESS.2024.3441750)
|
||||
IEEE Access Vol. 12, pp. 111801-111817, 2024
|
||||
|
||||
## License 📜
|
||||
Escargot is open-source software primarily licensed under [LGPL-2.1](https://github.com/Samsung/escargot/blob/master/LICENSE), with some components covered by other licenses. Complete license and copyright information can be found in the source code.
|
||||
Test run for each benchmark (Sunspider, Octane, V8, Chakracore, test262,
|
||||
SpiderMonkey, etc.):
|
||||
```sh
|
||||
tools/run-tests.py --arch=x86_64 spidermonkey test262 v8
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
v4.3.0
|
||||
v4.0.1
|
||||
|
|
|
|||
2
build/android/.idea/compiler.xml
generated
2
build/android/.idea/compiler.xml
generated
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="21" />
|
||||
<bytecodeTargetLevel target="17" />
|
||||
</component>
|
||||
</project>
|
||||
5
build/android/.idea/gradle.xml
generated
5
build/android/.idea/gradle.xml
generated
|
|
@ -4,9 +4,10 @@
|
|||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||
<option name="gradleJvm" value="jbr-17" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
|
|
|
|||
3
build/android/.idea/misc.xml
generated
3
build/android/.idea/misc.xml
generated
|
|
@ -1,7 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
|
|
|||
12
build/android/.idea/vcs.xml
generated
12
build/android/.idea/vcs.xml
generated
|
|
@ -2,17 +2,5 @@
|
|||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../test/kangax" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../test/octane" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../test/test262" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../test/vendortest" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../test/web-tooling-benchmark" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../third_party/GCutil" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../third_party/googletest" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../third_party/walrus" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../third_party/walrus/third_party/sljit" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../third_party/walrus/third_party/uvwasi" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../third_party/walrus/third_party/wasm-c-api" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../third_party/wasm/wabt" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id 'com.android.application' version '8.10.1' apply false
|
||||
id 'com.android.library' version '8.10.1' apply false
|
||||
id 'com.android.application' version '8.1.2' apply false
|
||||
id 'com.android.library' version '8.1.2' apply false
|
||||
}
|
||||
|
|
@ -1,29 +1,26 @@
|
|||
plugins {
|
||||
id 'com.android.library'
|
||||
id 'jacoco'
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
ext {
|
||||
force64Option = System.getProperty("ESCARGOT_BUILD_64BIT_FORCE_LARGE", "ON")
|
||||
pthreadKeyOption = System.getProperty("ESCARGOT_BUILD_TLS_ACCESS_BY_PTHREAD_KEY", "OFF")
|
||||
force64Option = System.getProperty("ESCARGOT_BUILD_64BIT_FORCE_LARGE", "OFF")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace 'com.samsung.lwe.escargot'
|
||||
compileSdk 36
|
||||
ndkVersion '28.1.13356709'
|
||||
compileSdk 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk 28
|
||||
targetSdk 36
|
||||
targetSdk 34
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON", "-DCMAKE_VERBOSE_MAKEFILE=ON", "-DESCARGOT_HOST=android", "-DESCARGOT_OUTPUT=static_lib", "-DENABLE_SHELL=OFF",
|
||||
"-DESCARGOT_BUILD_64BIT_FORCE_LARGE="+project.ext.force64Option, "-DESCARGOT_TLS_ACCESS_BY_PTHREAD_KEY="+project.ext.pthreadKeyOption
|
||||
arguments "-DCMAKE_VERBOSE_MAKEFILE=ON", "-DESCARGOT_HOST=android", "-DESCARGOT_OUTPUT=static_lib", "-DENABLE_SHELL=OFF",
|
||||
"-DESCARGOT_BUILD_64BIT_FORCE_LARGE="+project.ext.force64Option
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -40,6 +37,7 @@ android {
|
|||
|
||||
debug {
|
||||
testCoverageEnabled true
|
||||
debuggable = true
|
||||
jniDebuggable = true
|
||||
ndk {
|
||||
debuggable = true
|
||||
|
|
@ -57,6 +55,7 @@ android {
|
|||
|
||||
releaseShell {
|
||||
initWith(buildTypes.release)
|
||||
debuggable = true
|
||||
jniDebuggable = true
|
||||
ndk {
|
||||
debuggable = true
|
||||
|
|
@ -79,42 +78,16 @@ android {
|
|||
externalNativeBuild {
|
||||
cmake {
|
||||
path "src/main/cpp/CMakeLists.txt"
|
||||
version "3.18.1"
|
||||
}
|
||||
}
|
||||
|
||||
ndkVersion '27.0.12077973'
|
||||
ndkVersion '25.2.9519653'
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
publishing {
|
||||
singleVariant("release") {
|
||||
// if you don't want sources/javadoc, remove these lines
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
release(MavenPublication) {
|
||||
afterEvaluate {
|
||||
from components.release
|
||||
}
|
||||
artifactId "escargot-android"
|
||||
groupId "com.samsung.lwe.escargot"
|
||||
version "X.X.X.20XXXXXX.X.XXXXXXXX"
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url "/XXXXXX/android/releases/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def dirForNativeNoNDK = project.layout.buildDirectory.get().dir("native_nondk")
|
||||
|
|
@ -128,9 +101,7 @@ task buildCMakeNativeNoNDK(type: Exec) {
|
|||
dependsOn createNativeNoNDK
|
||||
workingDir dirForNativeNoNDK
|
||||
if (org.gradle.internal.os.OperatingSystem.current().isLinux()) {
|
||||
commandLine "/usr/bin/env", "cmake", "-DESCARGOT_HOST=linux", "-DESCARGOT_ARCH=x64", "-DESCARGOT_OUTPUT=static_lib",
|
||||
"-DESCARGOT_BUILD_64BIT_FORCE_LARGE="+project.ext.force64Option,
|
||||
"-DUNDER_NDK=OFF", srcForNativeNoNDK.absolutePath
|
||||
commandLine "/usr/bin/env", "cmake", "-DESCARGOT_HOST=linux", "-DESCARGOT_ARCH=x64", "-DESCARGOT_OUTPUT=static_lib", "-DUNDER_NDK=OFF", srcForNativeNoNDK.absolutePath
|
||||
} else if (org.gradle.internal.os.OperatingSystem.current().isMacOsX()) {
|
||||
var javaHome = new ByteArrayOutputStream().withStream { os ->
|
||||
exec {
|
||||
|
|
@ -140,18 +111,8 @@ task buildCMakeNativeNoNDK(type: Exec) {
|
|||
}
|
||||
os.toString().trim()
|
||||
}
|
||||
var icu4cPath = new ByteArrayOutputStream().withStream { os ->
|
||||
exec {
|
||||
commandLine "sh", "-c", "brew --prefix icu4c"
|
||||
standardOutput = os
|
||||
}
|
||||
os.toString().trim()
|
||||
}
|
||||
var pkgConfigPath = icu4cPath + "/lib/pkgconfig"
|
||||
environment("PKG_CONFIG_PATH", pkgConfigPath)
|
||||
commandLine "/usr/bin/env", "cmake", "-DESCARGOT_HOST=darwin", "-DESCARGOT_ARCH=x64", "-DESCARGOT_OUTPUT=static_lib", "-DUNDER_NDK=OFF",
|
||||
"-DESCARGOT_BUILD_64BIT_FORCE_LARGE="+project.ext.force64Option,
|
||||
"-DESCARGOT_LIBICU_SUPPORT_WITH_DLOPEN=OFF",
|
||||
environment("PKG_CONFIG_PATH", "/usr/local/opt/icu4c/lib/pkgconfig")
|
||||
commandLine "/usr/bin/env", "cmake", "-DESCARGOT_HOST=darwin", "-DESCARGOT_ARCH=x64", "-DESCARGOT_OUTPUT=static_lib", "-DUNDER_NDK=OFF", "-DESCARGOT_LIBICU_SUPPORT_WITH_DLOPEN=OFF",
|
||||
"-DJAVA_HOME=" + javaHome, "-DJAVA_INCLUDE_PATH=" + javaHome + "/include",
|
||||
"-DJAVA_INCLUDE_PATH2=" + javaHome + "/include/darwin", "-DJAVA_AWT_INCLUDE_PATH=" + javaHome + "/include",
|
||||
srcForNativeNoNDK.absolutePath
|
||||
|
|
@ -197,13 +158,13 @@ project.afterEvaluate {
|
|||
}
|
||||
|
||||
task clearHostJar(type: Delete) {
|
||||
delete 'build/libs/escargot.jar'
|
||||
delete 'build/outputs/escargot.jar'
|
||||
}
|
||||
|
||||
task bundleHostJar(type: Jar) {
|
||||
dependsOn buildGMakeNativeNoNDK
|
||||
dependsOn assemble
|
||||
from(zipTree('build/intermediates/aar_main_jar/release/syncReleaseLibJars/classes.jar'))
|
||||
from(zipTree('build/intermediates/aar_main_jar/debug/classes.jar'))
|
||||
from(dirForNativeNoNDK.asFile.toString() + "/libescargot-jni.so")
|
||||
from(dirForNativeNoNDK.asFile.toString() + "/libescargot-jni.dylib")
|
||||
rename("libescargot-jni.dylib", "libescargot-jni.so")
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@ project(escargot-jni)
|
|||
option(UNDER_NDK "Build under the Android NDK" ON)
|
||||
option(ENABLE_SHELL "Enable shell" OFF)
|
||||
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fexceptions -Wno-conversion-null -fPIC -ftls-model=local-dynamic ")
|
||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -ftls-model=local-dynamic ")
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fexceptions -Wno-conversion-null")
|
||||
|
||||
if (NOT UNDER_NDK)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libstdc++ -g3")
|
||||
|
|
@ -34,10 +33,7 @@ endif ()
|
|||
|
||||
if (UNDER_NDK)
|
||||
FIND_LIBRARY(LOG_LIBRARY log)
|
||||
# enable 16KB page size (require version of Android NDK >= 27)
|
||||
TARGET_LINK_OPTIONS(escargot PRIVATE "-Wl,-z,max-page-size=16384")
|
||||
TARGET_LINK_OPTIONS(escargot-jni PRIVATE "-Wl,-z,max-page-size=16384")
|
||||
else ()
|
||||
else (UNDER_NDK)
|
||||
FIND_PACKAGE(JNI REQUIRED)
|
||||
INCLUDE_DIRECTORIES(${JNI_INCLUDE_DIRS})
|
||||
endif ()
|
||||
|
|
@ -46,14 +42,15 @@ TARGET_LINK_LIBRARIES (escargot-jni PRIVATE escargot ${LOG_LIBRARY})
|
|||
|
||||
if (ENABLE_SHELL)
|
||||
SET(CMAKE_EXE_LINKER_FLAGS -Wl,-export-dynamic) # export symbol of Shell.cpp
|
||||
ADD_COMPILE_OPTIONS(-DNDEBUG -g3 -DESCARGOT_ENABLE_TEST -fPIC)
|
||||
ADD_COMPILE_OPTIONS(-DNDEBUG -g3 -DESCARGOT_ENABLE_TEST)
|
||||
ADD_LINK_OPTIONS(-static-libstdc++)
|
||||
ADD_EXECUTABLE(escargot-shell ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../src/shell/Shell.cpp)
|
||||
TARGET_INCLUDE_DIRECTORIES(escargot-shell PRIVATE ADD_SUBDIRECTORY
|
||||
(${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../third_party/GCutil/include)
|
||||
(${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../third_party/GCutil/)
|
||||
(${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../third_party/GCutil/bdwgc/include)
|
||||
(${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../src))
|
||||
TARGET_LINK_LIBRARIES (escargot-shell PRIVATE escargot ${LOG_LIBRARY})
|
||||
|
||||
ADD_EXECUTABLE(test-data-runner ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../../tools/test/test-data-runner/test-data-runner.cpp)
|
||||
TARGET_COMPILE_OPTIONS(test-data-runner PRIVATE -std=c++11 -fPIC)
|
||||
endif ()
|
||||
TARGET_COMPILE_OPTIONS(test-data-runner PRIVATE -std=c++11)
|
||||
endif ()
|
||||
|
|
@ -330,7 +330,7 @@ ScriptObjectExtraData* ensureScriptObjectExtraData(ObjectRef* ref)
|
|||
data = new ScriptObjectExtraData;
|
||||
ref->setExtraData(data);
|
||||
|
||||
Memory::gcRegisterFinalizer(ref, [](void* self, void* data) {
|
||||
Memory::gcRegisterFinalizer(ref, [](void* self) {
|
||||
ScriptObjectExtraData* extraData = reinterpret_cast<ScriptObjectExtraData*>(reinterpret_cast<ObjectRef*>(self)->extraData());
|
||||
auto env = fetchJNIEnvFromCallback();
|
||||
if (env) {
|
||||
|
|
@ -343,8 +343,8 @@ ScriptObjectExtraData* ensureScriptObjectExtraData(ObjectRef* ref)
|
|||
extraData->userData = nullptr;
|
||||
}
|
||||
}
|
||||
}, nullptr);
|
||||
});
|
||||
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#Fri May 30 15:07:57 KST 2025
|
||||
#Wed Oct 11 09:35:59 KST 2023
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
./gradlew publish
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
./gradlew bundleHostJar
|
||||
MAVEN_OPTS="-Dmaven.repo.local=${MAVEN_LOCAL_PATH}/mac/releases/" mvn install:install-file -Dfile=${PWD}/escargot/build/libs/escargot.jar -DgroupId=com.samsung.lwe.escargot -DartifactId=escargot-mac -Dversion=X.X.X.20XXXXXX.X.XXXXXXX -Dpackaging=jar
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
./gradlew bundleHostJar
|
||||
MAVEN_OPTS="-Dmaven.repo.local=${MAVEN_LOCAL_PATH}/ubuntu/releases/" mvn install:install-file -Dfile=${PWD}/escargot/build/libs/escargot.jar -DgroupId=com.samsung.lwe.escargot -DartifactId=escargot-ubuntu -Dversion=X.X.X.20XXXXXX.X.XXXXXXX -Dpackaging=jar
|
||||
|
|
@ -84,32 +84,31 @@ ENDIF()
|
|||
# FLAGS FOR ADDITIONAL FUNCTION
|
||||
#######################################################
|
||||
IF (ESCARGOT_LIBICU_SUPPORT)
|
||||
IF (ESCARGOT_DEPLOY)
|
||||
# Build for deployment (include ICU library)
|
||||
SET (CMAKE_INSTALL_RPATH "$ORIGIN")
|
||||
SET (CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
SET (CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
ENDIF()
|
||||
|
||||
IF (ESCARGOT_LIBICU_SUPPORT_WITH_DLOPEN)
|
||||
SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_ICU -DENABLE_INTL -DENABLE_RUNTIME_ICU_BINDER)
|
||||
SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_INTL_DISPLAYNAMES -DENABLE_INTL_NUMBERFORMAT -DENABLE_INTL_PLURALRULES)
|
||||
SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_INTL_RELATIVETIMEFORMAT -DENABLE_INTL_LISTFORMAT -DENABLE_INTL_DURATIONFORMAT)
|
||||
SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_INTL_SEGMENTER)
|
||||
SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_INTL_RELATIVETIMEFORMAT -DENABLE_INTL_LISTFORMAT)
|
||||
ELSE()
|
||||
IF (NOT ${ESCARGOT_HOST} STREQUAL "windows")
|
||||
# windows icu cannot support these feature yet.(~10.0.18362)
|
||||
SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_INTL_DISPLAYNAMES -DENABLE_INTL_NUMBERFORMAT -DENABLE_INTL_PLURALRULES)
|
||||
SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_INTL_RELATIVETIMEFORMAT -DENABLE_INTL_LISTFORMAT -DENABLE_INTL_DURATIONFORMAT)
|
||||
SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_INTL_SEGMENTER)
|
||||
SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_INTL_RELATIVETIMEFORMAT -DENABLE_INTL_LISTFORMAT)
|
||||
|
||||
PKG_CHECK_MODULES(ICU REQUIRED icu-uc icu-i18n)
|
||||
PKG_CHECK_MODULES (ICUI18N REQUIRED icu-i18n)
|
||||
PKG_CHECK_MODULES (ICUUC REQUIRED icu-uc)
|
||||
ENDIF()
|
||||
|
||||
MESSAGE(STATUS "ICU Libraries: ${ICU_LIBRARIES}")
|
||||
SET (ESCARGOT_LDFLAGS ${ESCARGOT_LDFLAGS} ${ICU_LDFLAGS})
|
||||
SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_ICU -DENABLE_INTL)
|
||||
SET (ESCARGOT_INCDIRS ${ESCARGOT_INCDIRS} ${ICU_INCLUDE_DIRS})
|
||||
SET (ESCARGOT_LIBRARIES ${ESCARGOT_LIBRARIES} ${ICU_LIBRARIES})
|
||||
|
||||
IF (${ESCARGOT_HOST} STREQUAL "darwin")
|
||||
FOREACH (ICU_LDFLAG ${ICUI18N_LDFLAGS} ${ICUUC_LDFLAGS})
|
||||
SET (ESCARGOT_LDFLAGS ${ESCARGOT_LDFLAGS} ${ICU_LDFLAG})
|
||||
ENDFOREACH()
|
||||
ELSE()
|
||||
SET (ESCARGOT_LIBRARIES ${ESCARGOT_LIBRARIES} ${ICUI18N_LIBRARIES} ${ICUUC_LIBRARIES})
|
||||
ENDIF()
|
||||
SET (ESCARGOT_INCDIRS ${ESCARGOT_INCDIRS} ${ICUI18N_INCLUDE_DIRS} ${ICUUC_INCLUDE_DIRS})
|
||||
SET (ESCARGOT_CXXFLAGS ${ESCARGOT_CXXFLAGS} ${ICUI18N_CFLAGS_OTHER} ${ICUUC_CFLAGS_OTHER})
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
|
|
@ -139,10 +138,6 @@ ENDIF()
|
|||
|
||||
IF (ESCARGOT_WASM)
|
||||
SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_WASM)
|
||||
IF (NOT DEFINED ESCARGOT_THREADING)
|
||||
# threading should be enabled for WASM (WASM threading feature)
|
||||
SET (ESCARGOT_THREADING ON)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
IF (ESCARGOT_THREADING)
|
||||
|
|
@ -165,21 +160,6 @@ ENDIF()
|
|||
|
||||
IF (ESCARGOT_TEMPORAL)
|
||||
SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_TEMPORAL)
|
||||
IF (NOT ESCARGOT_LIBICU_SUPPORT)
|
||||
MESSAGE (FATAL_ERROR "Temporal feature needs ICU")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
IF (ESCARGOT_SHADOWREALM)
|
||||
SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_SHADOWREALM)
|
||||
ENDIF()
|
||||
|
||||
IF (ESCARGOT_TLS_ACCESS_BY_ADDRESS)
|
||||
SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_TLS_ACCESS_BY_ADDRESS)
|
||||
ENDIF()
|
||||
|
||||
IF (ESCARGOT_TLS_ACCESS_BY_PTHREAD_KEY)
|
||||
SET (ESCARGOT_DEFINITIONS ${ESCARGOT_DEFINITIONS} -DENABLE_TLS_ACCESS_BY_PTHREAD_KEY)
|
||||
ENDIF()
|
||||
|
||||
IF (ESCARGOT_TEST)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ CMAKE_MINIMUM_REQUIRED (VERSION 2.8.12 FATAL_ERROR)
|
|||
SET (ESCARGOT_INCDIRS
|
||||
${ESCARGOT_INCDIRS}
|
||||
${ESCARGOT_ROOT}/src/
|
||||
${ESCARGOT_THIRD_PARTY_ROOT}
|
||||
${ESCARGOT_THIRD_PARTY_ROOT}/checked_arithmetic/
|
||||
${ESCARGOT_THIRD_PARTY_ROOT}/double_conversion/
|
||||
${ESCARGOT_THIRD_PARTY_ROOT}/lz4/
|
||||
|
|
@ -11,7 +10,6 @@ SET (ESCARGOT_INCDIRS
|
|||
${ESCARGOT_THIRD_PARTY_ROOT}/yarr/
|
||||
${ESCARGOT_THIRD_PARTY_ROOT}/runtime_icu_binder/
|
||||
${ESCARGOT_THIRD_PARTY_ROOT}/robin_map/include/
|
||||
${ESCARGOT_THIRD_PARTY_ROOT}/xsum/
|
||||
)
|
||||
|
||||
IF (${ESCARGOT_MODE} STREQUAL "debug")
|
||||
|
|
@ -55,7 +53,6 @@ FILE (GLOB_RECURSE ESCARGOT_SRC ${ESCARGOT_ROOT}/src/*.cpp)
|
|||
FILE (GLOB YARR_SRC ${ESCARGOT_THIRD_PARTY_ROOT}/yarr/*.cpp)
|
||||
FILE (GLOB DOUBLE_CONVERSION_SRC ${ESCARGOT_THIRD_PARTY_ROOT}/double_conversion/*.cc)
|
||||
FILE (GLOB LZ4_SRC ${ESCARGOT_THIRD_PARTY_ROOT}/lz4/*.cpp)
|
||||
FILE (GLOB XSUM_SRC ${ESCARGOT_THIRD_PARTY_ROOT}/xsum/*.cpp)
|
||||
|
||||
IF (NOT ${ESCARGOT_OUTPUT} MATCHES "shell")
|
||||
LIST (REMOVE_ITEM ESCARGOT_SRC ${ESCARGOT_ROOT}/src/shell/Shell.cpp)
|
||||
|
|
@ -74,7 +71,6 @@ SET (ESCARGOT_SRC_LIST
|
|||
${YARR_SRC}
|
||||
${DOUBLE_CONVERSION_SRC}
|
||||
${LZ4_SRC}
|
||||
${XSUM_SRC}
|
||||
${CCTEST_SRC}
|
||||
)
|
||||
|
||||
|
|
@ -91,55 +87,17 @@ SET (GCUTIL_CFLAGS ${ESCARGOT_THIRDPARTY_CFLAGS} ${PROFILER_FLAGS})
|
|||
SET (GCUTIL_CFLAGS_FROM_EXTERNAL ${ESCARGOT_CFLAGS_FROM_EXTERNAL})
|
||||
|
||||
IF (ESCARGOT_SMALL_CONFIG)
|
||||
SET (GCUTIL_CFLAGS ${GCUTIL_CFLAGS} -DSMALL_CONFIG)
|
||||
SET (GCUTIL_CFLAGS ${GCUTIL_CFLAGS} -DSMALL_CONFIG -DMAX_HEAP_SECTS=512)
|
||||
ENDIF()
|
||||
IF (ESCARGOT_THREADING)
|
||||
SET (GCUTIL_ENABLE_THREADING ON)
|
||||
ENDIF()
|
||||
IF (ESCARGOT_TLS_ACCESS_BY_ADDRESS)
|
||||
SET (GCUTIL_ENABLE_TLS_ACCESS_BY_ADDRESS ON)
|
||||
ENDIF()
|
||||
IF (ESCARGOT_TLS_ACCESS_BY_PTHREAD_KEY)
|
||||
SET (GCUTIL_ENABLE_TLS_ACCESS_BY_PTHREAD_KEY ON)
|
||||
ENDIF()
|
||||
|
||||
SET (GCUTIL_MODE ${ESCARGOT_MODE})
|
||||
|
||||
ADD_SUBDIRECTORY (third_party/GCutil)
|
||||
SET (ESCARGOT_LIBRARIES ${ESCARGOT_LIBRARIES} gc-lib)
|
||||
|
||||
#######################################################
|
||||
# SIMDUTF
|
||||
#######################################################
|
||||
ADD_LIBRARY (libsimdutf STATIC
|
||||
${ESCARGOT_THIRD_PARTY_ROOT}/simdutf/simdutf.cpp)
|
||||
TARGET_INCLUDE_DIRECTORIES (libsimdutf PUBLIC ${ESCARGOT_THIRD_PARTY_ROOT}/simdutf)
|
||||
SET (LIBSIMDUTF_CXXFLAGS
|
||||
${ESCARGOT_CXXFLAGS}
|
||||
${ESCARGOT_THIRDPARTY_CFLAGS}
|
||||
${CXXFLAGS_FROM_ENV}
|
||||
# disable simd optimization
|
||||
-DSIMDUTF_IMPLEMENTATION_ARM64=0
|
||||
-DSIMDUTF_IMPLEMENTATION_ICELAKE=0
|
||||
-DSIMDUTF_IMPLEMENTATION_HASWELL=0
|
||||
-DSIMDUTF_IMPLEMENTATION_WESTMERE=0
|
||||
-DSIMDUTF_IMPLEMENTATION_PPC64=0
|
||||
-DSIMDUTF_IMPLEMENTATION_RVV=0
|
||||
-DSIMDUTF_IMPLEMENTATION_LSX=0
|
||||
-DSIMDUTF_IMPLEMENTATION_FALLBACK=1
|
||||
# ${ESCARGOT_CXXFLAGS_FROM_EXTERNAL} already included in ${CXXFLAGS_FROM_ENV}
|
||||
)
|
||||
|
||||
IF (${ESCARGOT_MODE} STREQUAL "debug")
|
||||
SET (LIBSIMDUTF_CXXFLAGS ${ESCARGOT_CXXFLAGS_DEBUG} ${LIBSIMDUTF_CXXFLAGS})
|
||||
ELSEIF (${ESCARGOT_MODE} STREQUAL "release")
|
||||
SET (LIBSIMDUTF_CXXFLAGS ${ESCARGOT_CXXFLAGS_RELEASE} ${LIBSIMDUTF_CXXFLAGS})
|
||||
ENDIF()
|
||||
|
||||
TARGET_COMPILE_OPTIONS (libsimdutf PRIVATE ${LIBSIMDUTF_CXXFLAGS})
|
||||
|
||||
SET (ESCARGOT_LIBRARIES ${ESCARGOT_LIBRARIES} libsimdutf)
|
||||
|
||||
#######################################################
|
||||
# LIBBF
|
||||
#######################################################
|
||||
|
|
@ -166,12 +124,12 @@ SET (ESCARGOT_LIBRARIES ${ESCARGOT_LIBRARIES} libbf)
|
|||
#######################################################
|
||||
# RUNTIME ICU BINDER
|
||||
#######################################################
|
||||
SET (RIB_CFLAGS ${ESCARGOT_THIRDPARTY_CFLAGS})
|
||||
SET (RIB_MODE ${ESCARGOT_MODE})
|
||||
SET (RIB_CFLAGS_FROM_EXTERNAL ${ESCARGOT_CFLAGS_FROM_EXTERNAL})
|
||||
ADD_SUBDIRECTORY (third_party/runtime_icu_binder)
|
||||
|
||||
IF (ESCARGOT_LIBICU_SUPPORT_WITH_DLOPEN)
|
||||
SET (RIB_CFLAGS ${ESCARGOT_THIRDPARTY_CFLAGS})
|
||||
SET (RIB_MODE ${ESCARGOT_MODE})
|
||||
SET (RIB_CFLAGS_FROM_EXTERNAL ${ESCARGOT_CFLAGS_FROM_EXTERNAL})
|
||||
|
||||
ADD_SUBDIRECTORY (third_party/runtime_icu_binder)
|
||||
SET (ESCARGOT_LIBRARIES ${ESCARGOT_LIBRARIES} runtime-icu-binder-static)
|
||||
ENDIF()
|
||||
|
||||
|
|
@ -188,7 +146,6 @@ IF (ESCARGOT_WASM)
|
|||
SET (WALRUS_MODE ${ESCARGOT_MODE})
|
||||
SET (WALRUS_OUTPUT "shared_lib")
|
||||
SET (WALRUS_WASI OFF) # WASI should be OFF
|
||||
SET (WALRUS_EXTENDED_FEATURES ON) # enable extended features
|
||||
|
||||
IF (${ESCARGOT_MODE} STREQUAL "release")
|
||||
SET (WALRUS_CXXFLAGS ${WALRUS_CXXFLAGS} ${ESCARGOT_CXXFLAGS_RELEASE})
|
||||
|
|
@ -200,120 +157,6 @@ IF (ESCARGOT_WASM)
|
|||
SET (ESCARGOT_LIBRARIES ${ESCARGOT_LIBRARIES} walrus)
|
||||
ENDIF()
|
||||
|
||||
MAKE_DIRECTORY(${CMAKE_BINARY_DIR}/escargot_generated/tmp)
|
||||
|
||||
# Generate UnicodeIdentifierTables.cpp
|
||||
MAKE_DIRECTORY(${CMAKE_BINARY_DIR}/escargot_generated/parser)
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND python3 ${PROJECT_SOURCE_DIR}/tools/code_generators/gen_unicode.py --derived_core_properties ${PROJECT_SOURCE_DIR}/tools/unicode_data/DerivedCoreProperties.txt --dst ${CMAKE_BINARY_DIR}/escargot_generated/tmp/UnicodeIdentifierTables.cpp
|
||||
RESULT_VARIABLE GENERATE_RESULT
|
||||
OUTPUT_VARIABLE GENERATE_OUTPUT
|
||||
ERROR_VARIABLE GENERATE_ERROR
|
||||
)
|
||||
|
||||
IF (NOT GENERATE_RESULT EQUAL 0)
|
||||
MESSAGE(STATUS "Output:\n${GENERATE_OUTPUT}")
|
||||
MESSAGE(FATAL_ERROR "${GENERATE_ERROR}")
|
||||
ENDIF()
|
||||
|
||||
EXECUTE_PROCESS (COMMAND ${CMAKE_COMMAND} -E compare_files ${CMAKE_BINARY_DIR}/escargot_generated/tmp/UnicodeIdentifierTables.cpp ${CMAKE_BINARY_DIR}/escargot_generated/parser/UnicodeIdentifierTables.cpp
|
||||
RESULT_VARIABLE COMPARE_RESULT
|
||||
OUTPUT_VARIABLE COMPARE_OUTPUT
|
||||
ERROR_VARIABLE COMPARE_ERROR
|
||||
)
|
||||
|
||||
IF (NOT ${COMPARE_RESULT} EQUAL 0)
|
||||
FILE (COPY ${CMAKE_BINARY_DIR}/escargot_generated/tmp/UnicodeIdentifierTables.cpp DESTINATION ${CMAKE_BINARY_DIR}/escargot_generated/parser/)
|
||||
ENDIF()
|
||||
|
||||
SET (ESCARGOT_SRC_LIST ${ESCARGOT_SRC_LIST} ${CMAKE_BINARY_DIR}/escargot_generated/parser/UnicodeIdentifierTables.cpp)
|
||||
|
||||
# Generate YarrCanonicalizeUnicode.cpp
|
||||
MAKE_DIRECTORY(${CMAKE_BINARY_DIR}/escargot_generated/yarr)
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND python3 ${PROJECT_SOURCE_DIR}/tools/code_generators/generateYarrCanonicalizeUnicode.py ${PROJECT_SOURCE_DIR}/tools/unicode_data/CaseFolding.txt ${CMAKE_BINARY_DIR}/escargot_generated/tmp/YarrCanonicalizeUnicode.cpp
|
||||
RESULT_VARIABLE GENERATE_RESULT
|
||||
OUTPUT_VARIABLE GENERATE_OUTPUT
|
||||
ERROR_VARIABLE GENERATE_ERROR
|
||||
)
|
||||
|
||||
IF (NOT GENERATE_RESULT EQUAL 0)
|
||||
MESSAGE(STATUS "Output:\n${GENERATE_OUTPUT}")
|
||||
MESSAGE(FATAL_ERROR "${GENERATE_ERROR}")
|
||||
ENDIF()
|
||||
|
||||
FILE(READ ${CMAKE_BINARY_DIR}/escargot_generated/tmp/YarrCanonicalizeUnicode.cpp UNICODE_FILE_CONTENTS)
|
||||
STRING(REPLACE "config.h" "WTFBridge.h" UNICODE_FILE_CONTENTS "${UNICODE_FILE_CONTENTS}")
|
||||
STRING(REPLACE "constexpr const" "const" UNICODE_FILE_CONTENTS "${UNICODE_FILE_CONTENTS}")
|
||||
STRING(REPLACE "constexpr size_t UNICODE" "const size_t UNICODE" UNICODE_FILE_CONTENTS "${UNICODE_FILE_CONTENTS}")
|
||||
STRING(REPLACE "constexpr CanonicalizationRange unicodeRangeInfo" "const CanonicalizationRange unicodeRangeInfo" UNICODE_FILE_CONTENTS "${UNICODE_FILE_CONTENTS}")
|
||||
FILE(WRITE ${CMAKE_BINARY_DIR}/escargot_generated/tmp/YarrCanonicalizeUnicode.cpp "${UNICODE_FILE_CONTENTS}")
|
||||
|
||||
EXECUTE_PROCESS (COMMAND ${CMAKE_COMMAND} -E compare_files ${CMAKE_BINARY_DIR}/escargot_generated/tmp/YarrCanonicalizeUnicode.cpp ${CMAKE_BINARY_DIR}/escargot_generated/yarr/YarrCanonicalizeUnicode.cpp
|
||||
RESULT_VARIABLE COMPARE_RESULT
|
||||
OUTPUT_VARIABLE COMPARE_OUTPUT
|
||||
ERROR_VARIABLE COMPARE_ERROR
|
||||
)
|
||||
|
||||
IF (NOT ${COMPARE_RESULT} EQUAL 0)
|
||||
FILE (COPY ${CMAKE_BINARY_DIR}/escargot_generated/tmp/YarrCanonicalizeUnicode.cpp DESTINATION ${CMAKE_BINARY_DIR}/escargot_generated/yarr/)
|
||||
ENDIF()
|
||||
|
||||
SET(ESCARGOT_SRC_LIST ${ESCARGOT_SRC_LIST} ${CMAKE_BINARY_DIR}/escargot_generated/yarr/YarrCanonicalizeUnicode.cpp)
|
||||
|
||||
# yarr/UnicodePatternTables.h
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND python3 ${PROJECT_SOURCE_DIR}/tools/code_generators/generateYarrUnicodePropertyTables.py ${PROJECT_SOURCE_DIR}/tools/unicode_data ${CMAKE_BINARY_DIR}/escargot_generated/tmp/UnicodePatternTables.h
|
||||
RESULT_VARIABLE GENERATE_RESULT
|
||||
OUTPUT_VARIABLE GENERATE_OUTPUT
|
||||
ERROR_VARIABLE GENERATE_ERROR
|
||||
)
|
||||
|
||||
IF (NOT GENERATE_RESULT EQUAL 0)
|
||||
MESSAGE(STATUS "Output:\n${GENERATE_OUTPUT}")
|
||||
MESSAGE(FATAL_ERROR "${GENERATE_ERROR}")
|
||||
ENDIF()
|
||||
|
||||
EXECUTE_PROCESS (COMMAND ${CMAKE_COMMAND} -E compare_files ${CMAKE_BINARY_DIR}/escargot_generated/tmp/UnicodePatternTables.h ${CMAKE_BINARY_DIR}/escargot_generated/yarr/UnicodePatternTables.h
|
||||
RESULT_VARIABLE COMPARE_RESULT
|
||||
OUTPUT_VARIABLE COMPARE_OUTPUT
|
||||
ERROR_VARIABLE COMPARE_ERROR
|
||||
)
|
||||
|
||||
IF (NOT ${COMPARE_RESULT} EQUAL 0)
|
||||
FILE (COPY ${CMAKE_BINARY_DIR}/escargot_generated/tmp/UnicodePatternTables.h DESTINATION ${CMAKE_BINARY_DIR}/escargot_generated/yarr/)
|
||||
ENDIF()
|
||||
|
||||
SET (ESCARGOT_INCDIRS
|
||||
${ESCARGOT_INCDIRS}
|
||||
${CMAKE_BINARY_DIR}/escargot_generated/yarr/
|
||||
)
|
||||
|
||||
# YarrCanonicalizeUCS2.cpp
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND python3 ${PROJECT_SOURCE_DIR}/tools/code_generators/generateYarrCanonicalizeUCS2.py ${PROJECT_SOURCE_DIR}/tools/unicode_data/UnicodeData.txt ${CMAKE_BINARY_DIR}/escargot_generated/tmp/YarrCanonicalizeUCS2.cpp
|
||||
RESULT_VARIABLE GENERATE_RESULT
|
||||
OUTPUT_VARIABLE GENERATE_OUTPUT
|
||||
ERROR_VARIABLE GENERATE_ERROR
|
||||
)
|
||||
|
||||
IF (NOT GENERATE_RESULT EQUAL 0)
|
||||
MESSAGE(STATUS "Output:\n${GENERATE_OUTPUT}")
|
||||
MESSAGE(FATAL_ERROR "${GENERATE_ERROR}")
|
||||
ENDIF()
|
||||
|
||||
EXECUTE_PROCESS (COMMAND ${CMAKE_COMMAND} -E compare_files ${CMAKE_BINARY_DIR}/escargot_generated/tmp/YarrCanonicalizeUCS2.cpp ${CMAKE_BINARY_DIR}/escargot_generated/yarr/YarrCanonicalizeUCS2.cpp
|
||||
RESULT_VARIABLE COMPARE_RESULT
|
||||
OUTPUT_VARIABLE COMPARE_OUTPUT
|
||||
ERROR_VARIABLE COMPARE_ERROR
|
||||
)
|
||||
|
||||
IF (NOT ${COMPARE_RESULT} EQUAL 0)
|
||||
FILE (COPY ${CMAKE_BINARY_DIR}/escargot_generated/tmp/YarrCanonicalizeUCS2.cpp DESTINATION ${CMAKE_BINARY_DIR}/escargot_generated/yarr/)
|
||||
ENDIF()
|
||||
|
||||
SET(ESCARGOT_SRC_LIST ${ESCARGOT_SRC_LIST} ${CMAKE_BINARY_DIR}/escargot_generated/yarr/YarrCanonicalizeUCS2.cpp)
|
||||
|
||||
# BUILD
|
||||
IF (${ESCARGOT_OUTPUT} STREQUAL "shell")
|
||||
ADD_EXECUTABLE (${ESCARGOT_TARGET} ${ESCARGOT_SRC_LIST})
|
||||
|
|
|
|||
|
|
@ -9,9 +9,6 @@ SET (ESCARGOT_THIRDPARTY_CFLAGS)
|
|||
SET (ESCARGOT_BUILD_32BIT OFF)
|
||||
SET (ESCARGOT_BUILD_64BIT OFF)
|
||||
SET (ESCARGOT_BUILD_64BIT_LARGE OFF)
|
||||
IF (ESCARGOT_ASAN)
|
||||
SET (ESCARGOT_BUILD_64BIT_LARGE ON)
|
||||
ENDIF()
|
||||
|
||||
# clang-cl defines ${CMAKE_CXX_COMPILER_ID} "Clang" and ${CMAKE_CXX_COMPILER_FRONTEND_VARIANT} "MSVC"
|
||||
SET (COMPILER_CLANG_CL OFF)
|
||||
|
|
@ -25,18 +22,13 @@ ENDIF()
|
|||
|
||||
# Default options per compiler
|
||||
IF (${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC" OR ${COMPILER_CLANG_CL})
|
||||
SET (ESCARGOT_COMPILER_ID "MSVC")
|
||||
SET (ESCARGOT_CXXFLAGS /std:c++17 /fp:strict /Zc:__cplusplus /EHs /source-charset:utf-8 /MP /D_CRT_SECURE_NO_WARNINGS /DGC_NOT_DLL /D_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING /wd4244 /wd4267 /wd4805 /wd4018 /wd4172 /wd4146)
|
||||
SET (ESCARGOT_CXXFLAGS /std:c++17 /fp:strict /Zc:__cplusplus /EHs /source-charset:utf-8 /MP /D_CRT_SECURE_NO_WARNINGS /DGC_NOT_DLL /D_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING /wd4244 /wd4267 /wd4805 /wd4018 /wd4172)
|
||||
SET (ESCARGOT_CXXFLAGS_RELEASE /O2 /Oy-)
|
||||
SET (ESCARGOT_THIRDPARTY_CFLAGS /D_CRT_SECURE_NO_WARNINGS /DGC_NOT_DLL /Oy- /wd4146 /EHs)
|
||||
IF (${COMPILER_CLANG_CL})
|
||||
SET (ESCARGOT_CXXFLAGS ${ESCARGOT_CXXFLAGS} /EHs -Wno-invalid-offsetof -Wno-inline-new-delete -fintegrated-cc1)
|
||||
ENDIF()
|
||||
IF (ESCARGOT_SMALL_CONFIG)
|
||||
SET (ESCARGOT_CXXFLAGS_RELEASE ${ESCARGOT_CXXFLAGS_RELEASE} /Os)
|
||||
ENDIF()
|
||||
ELSEIF (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
|
||||
SET (ESCARGOT_COMPILER_ID "GCC")
|
||||
SET (ESCARGOT_CXXFLAGS
|
||||
${ESCARGOT_CXXFLAGS}
|
||||
-std=c++11 -g3
|
||||
|
|
@ -51,26 +43,13 @@ ELSEIF (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
|
|||
-Wno-unused-but-set-variable -Wno-unused-but-set-parameter
|
||||
-Wno-deprecated-declarations -Wno-unused-function
|
||||
)
|
||||
|
||||
IF (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16)
|
||||
SET (ESCARGOT_CXXFLAGS ${ESCARGOT_CXXFLAGS} -Wno-maybe-uninitialized)
|
||||
ENDIF()
|
||||
|
||||
IF (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9)
|
||||
SET (ESCARGOT_CXXFLAGS ${ESCARGOT_CXXFLAGS} -Wno-attributes -Wno-class-memaccess -Wno-deprecated-copy -Wno-cast-function-type -Wno-stringop-truncation -Wno-pessimizing-move -Wno-mismatched-new-delete -Wno-overloaded-virtual -Wno-dangling-pointer)
|
||||
endif()
|
||||
SET (ESCARGOT_CXXFLAGS_DEBUG -O0 -Wall -Wextra -Werror)
|
||||
SET (ESCARGOT_CXXFLAGS_RELEASE -O2 -fno-stack-protector -fno-omit-frame-pointer)
|
||||
IF (ESCARGOT_SMALL_CONFIG)
|
||||
IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS_EQUAL 9)
|
||||
# BUG?) -Os option has unknown memory conflicts (might be related with gcc version)
|
||||
# enable this option only for old gcc version
|
||||
SET (ESCARGOT_CXXFLAGS_RELEASE ${ESCARGOT_CXXFLAGS_RELEASE} -Os)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
SET (ESCARGOT_THIRDPARTY_CFLAGS -w -g3 -fdata-sections -ffunction-sections -fno-omit-frame-pointer -fvisibility=hidden)
|
||||
ELSEIF (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") #include Clang and AppleClang both
|
||||
SET (ESCARGOT_COMPILER_ID "CLANG")
|
||||
ELSEIF (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
|
||||
SET (ESCARGOT_CXXFLAGS
|
||||
${ESCARGOT_CXXFLAGS}
|
||||
-std=c++11 -g3
|
||||
|
|
@ -81,20 +60,12 @@ ELSEIF (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") #include Clang and AppleClang
|
|||
-fvisibility=hidden
|
||||
-fno-fast-math -fno-unsafe-math-optimizations -fdenormal-fp-math=ieee
|
||||
-Wno-type-limits -Wno-unused-result -Wno-unused-variable -Wno-invalid-offsetof -Wno-unused-function
|
||||
-Wno-deprecated-declarations -Wno-parentheses-equality -Wno-dynamic-class-memaccess -Wno-deprecated-register
|
||||
-Wno-deprecated-declarations -Wno-unsupported-floating-point-opt -Wno-parentheses-equality -Wno-dynamic-class-memaccess -Wno-deprecated-register
|
||||
-Wno-expansion-to-defined -Wno-return-type -Wno-overloaded-virtual -Wno-unused-private-field -Wno-deprecated-copy -Wno-atomic-alignment
|
||||
-Wno-ambiguous-reversed-operator -Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion -Wno-braced-scalar-init -Wno-unused-parameter -Wno-deprecated-literal-operator -Wno-cast-function-type-mismatch
|
||||
-Wno-unknown-warning-option
|
||||
-Wno-ambiguous-reversed-operator -Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion -Wno-braced-scalar-init -Wno-unused-parameter
|
||||
)
|
||||
IF (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 10)
|
||||
# this feature supported after clang version 11
|
||||
SET (ESCARGOT_CXXFLAGS ${ESCARGOT_CXXFLAGS} -Wno-unsupported-floating-point-opt)
|
||||
endif()
|
||||
SET (ESCARGOT_CXXFLAGS_DEBUG -O0 -Wall -Wextra -Werror)
|
||||
SET (ESCARGOT_CXXFLAGS_RELEASE -O2 -fno-stack-protector -fno-omit-frame-pointer)
|
||||
IF (ESCARGOT_SMALL_CONFIG)
|
||||
SET (ESCARGOT_CXXFLAGS_RELEASE ${ESCARGOT_CXXFLAGS_RELEASE} -Os)
|
||||
ENDIF()
|
||||
SET (ESCARGOT_THIRDPARTY_CFLAGS -w -g3 -fdata-sections -ffunction-sections -fno-omit-frame-pointer -fvisibility=hidden)
|
||||
ELSE()
|
||||
MESSAGE (FATAL_ERROR ${CMAKE_CXX_COMPILER_ID} " is Unsupported Compiler")
|
||||
|
|
@ -177,13 +148,8 @@ ELSEIF (${ESCARGOT_HOST} STREQUAL "android")
|
|||
# bdwgc android amd64 cannot support keeping back ptrs
|
||||
SET (ESCARGOT_THIRDPARTY_CFLAGS ${ESCARGOT_THIRDPARTY_CFLAGS} -UKEEP_BACK_PTRS -USAVE_CALL_COUNT -UDBG_HDRS_ALL)
|
||||
ENDIF()
|
||||
ELSEIF (${ESCARGOT_HOST} STREQUAL "darwin")
|
||||
ELSEIF (${ESCARGOT_HOST} STREQUAL "darwin" AND ${ESCARGOT_ARCH} STREQUAL "x64")
|
||||
FIND_PACKAGE (PkgConfig REQUIRED)
|
||||
IF ((NOT ${ESCARGOT_ARCH} STREQUAL "x64") AND (NOT ${ESCARGOT_ARCH} STREQUAL "aarch64"))
|
||||
MESSAGE (FATAL_ERROR ${ESCARGOT_ARCH} " is unsupported")
|
||||
ENDIF()
|
||||
# recent macOS supports only ICU version 75 that requires c++17 or above
|
||||
SET (ESCARGOT_CXXFLAGS ${ESCARGOT_CXXFLAGS} -std=c++17)
|
||||
SET (ESCARGOT_LDFLAGS -lpthread -Wl,-dead_strip)
|
||||
SET (ESCARGOT_BUILD_64BIT_LARGE ON)
|
||||
# bdwgc mac cannot support pthread_getattr_np
|
||||
|
|
@ -192,6 +158,8 @@ ELSEIF (${ESCARGOT_HOST} STREQUAL "darwin")
|
|||
IF (NOT DEFINED ESCARGOT_LIBICU_SUPPORT_WITH_DLOPEN)
|
||||
SET (ESCARGOT_LIBICU_SUPPORT_WITH_DLOPEN OFF)
|
||||
ENDIF()
|
||||
# add pkg_config_path icu install path of brew
|
||||
SET (ENV{PKG_CONFIG_PATH} "/usr/local/opt/icu4c/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}")
|
||||
ELSEIF (${ESCARGOT_HOST} STREQUAL "windows")
|
||||
SET (ESCARGOT_LDFLAGS ${ESCARGOT_LDFLAGS} icu.lib)
|
||||
IF ((${ESCARGOT_ARCH} STREQUAL "x64") OR (${ESCARGOT_ARCH} STREQUAL "x86_64"))
|
||||
|
|
|
|||
|
|
@ -115,15 +115,9 @@ Requires(postun): /sbin/ldconfig
|
|||
%define enable_shell 0
|
||||
%endif
|
||||
|
||||
%if 0%{?enable_small_config:1}
|
||||
%else
|
||||
%define enable_small_config 0
|
||||
%endif
|
||||
|
||||
# build requirements
|
||||
BuildRequires: cmake
|
||||
BuildRequires: ninja
|
||||
BuildRequires: python3
|
||||
BuildRequires: pkgconfig(dlog)
|
||||
BuildRequires: pkgconfig(bundle)
|
||||
|
||||
|
|
@ -260,17 +254,14 @@ CFLAGS+=' -Os '
|
|||
CXXFLAGS+=' -Os '
|
||||
%endif
|
||||
|
||||
CFLAGS+=' -Wno-shadow '
|
||||
CXXFLAGS+=' -Wno-shadow '
|
||||
|
||||
%if "%{?enable_test}" == "1"
|
||||
cmake CMakeLists.txt -H./ -Bbuild/out_tizen_%{rpm} -DLIBDIR=%{_libdir} -DINCLUDEDIR=%{_includedir} -DTIZEN_MAJOR_VERSION='%{tizen_version_major}' \
|
||||
-DESCARGOT_ARCH='%{tizen_arch}' -DESCARGOT_WASM='%{enable_wasm}' -DESCARGOT_DEBUGGER='%{enable_debugger}' -DESCARGOT_SMALL_CONFIG='%{enable_small_config}' \
|
||||
-DESCARGOT_THREADING=ON -DESCARGOT_TCO=ON -DESCARGOT_MODE=release -DESCARGOT_HOST=tizen -DESCARGOT_OUTPUT=shared_lib -DESCARGOT_TEST=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -DESCARGOT_TLS_ACCESS_BY_ADDRESS=ON -G Ninja
|
||||
-DESCARGOT_ARCH='%{tizen_arch}' -DESCARGOT_WASM='%{enable_wasm}' -DESCARGOT_DEBUGGER='%{enable_debugger}' \
|
||||
-DESCARGOT_THREADING=ON -DESCARGOT_TCO=ON -DESCARGOT_MODE=release -DESCARGOT_HOST=tizen -DESCARGOT_OUTPUT=shared_lib -DESCARGOT_TEST=ON -DESCARGOT_TEMPORAL=ON -DESCARGOT_TCO=ON -G Ninja
|
||||
%else
|
||||
cmake CMakeLists.txt -H./ -Bbuild/out_tizen_%{rpm} -DLIBDIR=%{_libdir} -DINCLUDEDIR=%{_includedir} -DTIZEN_MAJOR_VERSION='%{tizen_version_major}' \
|
||||
-DESCARGOT_ARCH='%{tizen_arch}' -DESCARGOT_WASM='%{enable_wasm}' -DESCARGOT_DEBUGGER='%{enable_debugger}' -DESCARGOT_SMALL_CONFIG='%{enable_small_config}' \
|
||||
-DESCARGOT_THREADING=ON -DESCARGOT_TLS_ACCESS_BY_ADDRESS=ON -DESCARGOT_MODE=release -DESCARGOT_HOST=tizen -DESCARGOT_OUTPUT=shared_lib -G Ninja
|
||||
-DESCARGOT_ARCH='%{tizen_arch}' -DESCARGOT_WASM='%{enable_wasm}' -DESCARGOT_DEBUGGER='%{enable_debugger}' \
|
||||
-DESCARGOT_THREADING=ON -DESCARGOT_MODE=release -DESCARGOT_HOST=tizen -DESCARGOT_OUTPUT=shared_lib -G Ninja
|
||||
%endif
|
||||
|
||||
cmake --build build/out_tizen_%{rpm}
|
||||
|
|
@ -281,7 +272,7 @@ cmake --build build/out_tizen_%{rpm}
|
|||
CXXFLAGS+=' -DESCARGOT_ENABLE_TEST '
|
||||
%endif
|
||||
|
||||
g++ src/shell/Shell.cpp -std=c++11 -Lbuild/out_tizen_%{rpm} -Isrc/ -Ithird_party/GCutil/include -o build/out_tizen_%{rpm}/escargot -O2 -DNDEBUG -Wl,-rpath=\$ORIGIN -Wl,-rpath=%{_libdir}/escargot ${CXXFLAGS} -lescargot -lpthread
|
||||
g++ src/shell/Shell.cpp -std=c++11 -Lbuild/out_tizen_%{rpm} -Isrc/ -Ithird_party/GCutil -Ithird_party/GCutil/bdwgc/include -o build/out_tizen_%{rpm}/escargot -O2 -DNDEBUG -Wl,-rpath=\$ORIGIN -Wl,-rpath=%{_libdir}/escargot ${CXXFLAGS} -lescargot -lpthread
|
||||
g++ tools/test/test-data-runner/test-data-runner.cpp -o build/out_tizen_%{rpm}/test-data-runner -std=c++11 ${CXXFLAGS} -lpthread
|
||||
%endif
|
||||
|
||||
|
|
@ -332,7 +323,6 @@ cp packaging/escargot.conf %{buildroot}%{_sysconfdir}/ld.so.conf.d/
|
|||
%files profile_tv
|
||||
%manifest packaging/%{name}.manifest
|
||||
%{_libdir}/escargot/libescargot.so*
|
||||
%{_sysconfdir}/ld.so.conf.d/*.conf
|
||||
%license LICENSE.BSD-2-Clause LICENSE.LGPL-2.1+ LICENSE.MPL-2.0 LICENSE.Apache-2.0 LICENSE.BSD-3-Clause LICENSE.MIT LICENSE.BOEHM-GC
|
||||
%endif
|
||||
|
||||
|
|
@ -340,7 +330,6 @@ cp packaging/escargot.conf %{buildroot}%{_sysconfdir}/ld.so.conf.d/
|
|||
%files profile_headless
|
||||
%manifest packaging/%{name}.manifest
|
||||
%{_libdir}/escargot/libescargot.so*
|
||||
%{_sysconfdir}/ld.so.conf.d/*.conf
|
||||
%license LICENSE.BSD-2-Clause LICENSE.LGPL-2.1+ LICENSE.MPL-2.0 LICENSE.Apache-2.0 LICENSE.BSD-3-Clause LICENSE.MIT LICENSE.BOEHM-GC
|
||||
%endif
|
||||
|
||||
|
|
|
|||
|
|
@ -264,16 +264,6 @@ if (f.type == Type::B) { puts("failed in msvc."); }
|
|||
#define HAVE_BUILTIN_ATOMIC_FUNCTIONS
|
||||
#endif
|
||||
|
||||
#if defined(COMPILER_CLANG)
|
||||
#if __has_feature(address_sanitizer)
|
||||
#define ASAN_ENABLED
|
||||
#endif
|
||||
#elif defined(COMPILER_GCC)
|
||||
#if defined(__SANITIZE_ADDRESS__)
|
||||
#define ASAN_ENABLED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(COMPILER_MSVC)
|
||||
#include <stddef.h>
|
||||
#endif
|
||||
|
|
@ -295,15 +285,14 @@ extern "C" {
|
|||
|
||||
#ifdef ENABLE_ICU
|
||||
#if defined(ENABLE_RUNTIME_ICU_BINDER)
|
||||
typedef unsigned char LChar;
|
||||
#include "RuntimeICUBinder.h"
|
||||
#include "ICUPolyfill.h"
|
||||
#else
|
||||
#define U_SHOW_CPLUSPLUS_API 0
|
||||
#define U_SHOW_CPLUSPLUS_HEADER_API 0
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <icu.h>
|
||||
#else
|
||||
#include <unicode/utypes.h>
|
||||
#include <unicode/locid.h>
|
||||
#include <unicode/uchar.h>
|
||||
#include <unicode/ustring.h>
|
||||
|
|
@ -318,16 +307,13 @@ extern "C" {
|
|||
#include <unicode/unum.h> // for Intl
|
||||
#include <unicode/upluralrules.h> // for Intl
|
||||
#include <unicode/unumberformatter.h> // for Intl
|
||||
#include <unicode/unumberrangeformatter.h> // for Intl
|
||||
#include <unicode/ureldatefmt.h> // for Intl
|
||||
#include <unicode/uformattedvalue.h> // for Intl
|
||||
#include <unicode/ucurr.h> // for Intl
|
||||
#include <unicode/uloc.h> // for Intl
|
||||
#include <unicode/uldnames.h> // for Intl
|
||||
#include <unicode/ulistformatter.h> // for Intl
|
||||
#include <unicode/ures.h> // for Intl
|
||||
#include <unicode/udateintervalformat.h> // for Intl
|
||||
#include <unicode/ubrk.h> // for Intl
|
||||
|
||||
// FIXME replace these vzone decl into include
|
||||
// I declare vzone api because there is no header file in include folder
|
||||
extern "C" {
|
||||
|
|
@ -339,11 +325,6 @@ UBool vzone_getTZURL(VZone* zone, UChar*& url, int32_t& urlLength);
|
|||
void vzone_getOffset3(VZone* zone, UDate date, UBool local, int32_t& rawOffset,
|
||||
int32_t& dstOffset, UErrorCode& ec);
|
||||
int32_t vzone_getRawOffset(VZone* zone);
|
||||
|
||||
// uresimp.h
|
||||
U_CAPI UResourceBundle* U_EXPORT2
|
||||
ures_findResource(const char* pathToResource,
|
||||
UResourceBundle* fillIn, UErrorCode* status);
|
||||
}
|
||||
#endif // !defined(OS_WINDOWS_UWP)
|
||||
|
||||
|
|
@ -356,13 +337,11 @@ typedef unsigned char LChar;
|
|||
typedef int32_t UChar32;
|
||||
|
||||
// macros from icu
|
||||
#define U16_IS_LEAD(c) (((c) & 0xfffffc00) == 0xd800)
|
||||
#define U16_IS_TRAIL(c) (((c) & 0xfffffc00) == 0xdc00)
|
||||
#define U_IS_SURROGATE(c) (((c) & 0xfffff800) == 0xd800)
|
||||
#define U16_IS_SURROGATE(c) U_IS_SURROGATE(c)
|
||||
#define U16_IS_LEAD(c) (((c)&0xfffffc00) == 0xd800)
|
||||
#define U16_IS_TRAIL(c) (((c)&0xfffffc00) == 0xdc00)
|
||||
#define U16_SURROGATE_OFFSET ((0xd800 << 10UL) + 0xdc00 - 0x10000)
|
||||
#define U16_GET_SUPPLEMENTARY(lead, trail) \
|
||||
(((UChar32)(lead) << 10UL) + (UChar32)(trail) - U16_SURROGATE_OFFSET)
|
||||
(((UChar32)(lead) << 10UL) + (UChar32)(trail)-U16_SURROGATE_OFFSET)
|
||||
|
||||
#define U16_NEXT(s, i, length, c) \
|
||||
{ \
|
||||
|
|
@ -595,47 +574,26 @@ typedef uint16_t LexicalBlockIndex;
|
|||
#endif
|
||||
|
||||
#include <tsl/robin_set.h>
|
||||
template <class Key, class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key>,
|
||||
bool StoreHash = false,
|
||||
class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>>
|
||||
using HashSet = tsl::robin_set<Key, Hash, KeyEqual, Allocator, StoreHash, GrowthPolicy>;
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
#if defined(ESCARGOT_SMALL_CONFIG)
|
||||
template <class Key, class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key>,
|
||||
bool StoreHash = false,
|
||||
class GrowthPolicy = tsl::rh::mod_growth_policy<std::ratio<5, 4>>>
|
||||
using HashSet = tsl::robin_set<Key, Hash, KeyEqual, Allocator, StoreHash, GrowthPolicy>;
|
||||
|
||||
template <class Key, class T, class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key, T>>,
|
||||
bool StoreHash = false,
|
||||
class GrowthPolicy = tsl::rh::mod_growth_policy<std::ratio<5, 4>>>
|
||||
using HashMap = tsl::robin_map<Key, T, Hash, KeyEqual, Allocator, StoreHash, GrowthPolicy>;
|
||||
#else
|
||||
template <class Key, class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<Key>,
|
||||
bool StoreHash = false,
|
||||
class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>>
|
||||
using HashSet = tsl::robin_set<Key, Hash, KeyEqual, Allocator, StoreHash, GrowthPolicy>;
|
||||
|
||||
template <class Key, class T, class Hash = std::hash<Key>,
|
||||
class KeyEqual = std::equal_to<Key>,
|
||||
class Allocator = std::allocator<std::pair<Key, T>>,
|
||||
bool StoreHash = false,
|
||||
class GrowthPolicy = tsl::rh::power_of_two_growth_policy<2>>
|
||||
using HashMap = tsl::robin_map<Key, T, Hash, KeyEqual, Allocator, StoreHash, GrowthPolicy>;
|
||||
#endif
|
||||
} // namespace Escargot
|
||||
|
||||
|
||||
#include "EscargotInfo.h"
|
||||
#include "heap/Heap.h"
|
||||
#include "util/Util.h"
|
||||
#include "util/Optional.h"
|
||||
#include "util/Variant.h"
|
||||
#include "runtime/ThreadLocal.h"
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@
|
|||
#include "runtime/BooleanObject.h"
|
||||
#include "runtime/RegExpObject.h"
|
||||
#include "runtime/ModuleNamespaceObject.h"
|
||||
#include "runtime/Job.h"
|
||||
#include "runtime/JobQueue.h"
|
||||
#include "runtime/PromiseObject.h"
|
||||
#include "runtime/ProxyObject.h"
|
||||
|
|
@ -130,9 +129,9 @@ public:
|
|||
return m_platform->onMallocArrayBufferObjectDataBuffer(sizeInByte);
|
||||
}
|
||||
|
||||
virtual void onFreeArrayBufferObjectDataBuffer(void* buffer, size_t sizeInByte, void* deleterData) override
|
||||
virtual void onFreeArrayBufferObjectDataBuffer(void* buffer, size_t sizeInByte) override
|
||||
{
|
||||
m_platform->onFreeArrayBufferObjectDataBuffer(buffer, sizeInByte, deleterData);
|
||||
m_platform->onFreeArrayBufferObjectDataBuffer(buffer, sizeInByte);
|
||||
}
|
||||
|
||||
virtual void* onReallocArrayBufferObjectDataBuffer(void* oldBuffer, size_t oldSizeInByte, size_t newSizeInByte) override
|
||||
|
|
@ -243,11 +242,15 @@ void PlatformRef::notifyHostImportModuleDynamicallyResult(ContextRef* relatedCon
|
|||
mp->m_value = toImpl(result.error.value());
|
||||
Evaluator::execute(relatedContext, [](ExecutionStateRef* state, ValueRef* error, PromiseObjectRef* promise) -> ValueRef* {
|
||||
promise->reject(state, promise);
|
||||
return ValueRef::createUndefined(); }, result.error.value(), promise);
|
||||
return ValueRef::createUndefined();
|
||||
},
|
||||
result.error.value(), promise);
|
||||
} else {
|
||||
Evaluator::execute(relatedContext, [](ExecutionStateRef* state, ValueRef* error, PromiseObjectRef* promise) -> ValueRef* {
|
||||
promise->fulfill(state, promise);
|
||||
return ValueRef::createUndefined(); }, result.error.value(), promise);
|
||||
return ValueRef::createUndefined();
|
||||
},
|
||||
result.error.value(), promise);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -270,15 +273,15 @@ void Globals::initialize(PlatformRef* platform)
|
|||
|
||||
void Globals::finalize()
|
||||
{
|
||||
RELEASE_ASSERT(!!g_globalsInited);
|
||||
g_globalsInited = false;
|
||||
// finalize global value or context including thread-local variables
|
||||
// this function should be invoked once at the end of the program
|
||||
RELEASE_ASSERT(!!g_globalsInited);
|
||||
ThreadLocal::finalize();
|
||||
|
||||
// Global::finalize should be called at the end of program
|
||||
// because it holds Platform which could be used in other Object's finalizer
|
||||
Global::finalize();
|
||||
g_globalsInited = false;
|
||||
}
|
||||
|
||||
bool Globals::isInitialized()
|
||||
|
|
@ -297,12 +300,11 @@ void Globals::initializeThread()
|
|||
|
||||
void Globals::finalizeThread()
|
||||
{
|
||||
RELEASE_ASSERT(!!g_globalsInited);
|
||||
g_globalsInited = false;
|
||||
Global::finalizeGC();
|
||||
// finalize thread-local variables
|
||||
// this function should be invoked once at the end of sub-thread
|
||||
RELEASE_ASSERT(!!g_globalsInited);
|
||||
ThreadLocal::finalize();
|
||||
g_globalsInited = false;
|
||||
}
|
||||
|
||||
bool Globals::supportsThreading()
|
||||
|
|
@ -349,216 +351,32 @@ void Memory::gcFree(void* ptr)
|
|||
GC_FREE(ptr);
|
||||
}
|
||||
|
||||
typedef TightVector<std::pair<Memory::GCAllocatedMemoryFinalizer, void*>,
|
||||
GCUtil::gc_malloc_allocator<std::pair<Memory::GCAllocatedMemoryFinalizer, void*>>>
|
||||
FinalizerData;
|
||||
|
||||
static void generalPublicFinalizer(void* obj, void* clientData)
|
||||
void Memory::gcRegisterFinalizer(void* ptr, GCAllocatedMemoryFinalizer callback)
|
||||
{
|
||||
FinalizerData* d = reinterpret_cast<FinalizerData*>(clientData);
|
||||
for (auto item : *d) {
|
||||
item.first(obj, item.second);
|
||||
}
|
||||
}
|
||||
|
||||
void Memory::gcRegisterFinalizer(void* ptr, GCAllocatedMemoryFinalizer callback, void* data)
|
||||
{
|
||||
GC_finalization_proc ofn = nullptr;
|
||||
void* ocd = nullptr;
|
||||
|
||||
GC_REGISTER_FINALIZER_NO_ORDER(ptr, generalPublicFinalizer,
|
||||
nullptr, &ofn, &ocd);
|
||||
|
||||
if (ofn != generalPublicFinalizer) {
|
||||
FinalizerData* v = new FinalizerData;
|
||||
v->push_back(std::make_pair(callback, data));
|
||||
|
||||
GC_REGISTER_FINALIZER_NO_ORDER(ptr, generalPublicFinalizer,
|
||||
v, nullptr, nullptr);
|
||||
if (callback) {
|
||||
GC_REGISTER_FINALIZER_NO_ORDER(ptr, [](void* obj, void* data) {
|
||||
((GCAllocatedMemoryFinalizer)data)(obj);
|
||||
},
|
||||
(void*)callback, nullptr, nullptr);
|
||||
} else {
|
||||
FinalizerData* d = reinterpret_cast<FinalizerData*>(ocd);
|
||||
d->push_back(std::make_pair(callback, data));
|
||||
GC_REGISTER_FINALIZER_NO_ORDER(ptr, generalPublicFinalizer,
|
||||
ocd, nullptr, nullptr);
|
||||
GC_REGISTER_FINALIZER_NO_ORDER(ptr, nullptr, nullptr, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void Memory::gcUnregisterFinalizer(void* ptr, Memory::GCAllocatedMemoryFinalizer callback, void* data)
|
||||
static void ObjectRefFinalizer(PointerValue* self, void* fn)
|
||||
{
|
||||
GC_finalization_proc ofn = nullptr;
|
||||
void* ocd = nullptr;
|
||||
|
||||
GC_REGISTER_FINALIZER_NO_ORDER(ptr, nullptr,
|
||||
nullptr, &ofn, &ocd);
|
||||
|
||||
if (ofn == generalPublicFinalizer) {
|
||||
FinalizerData* d = reinterpret_cast<FinalizerData*>(ocd);
|
||||
size_t i = 0;
|
||||
for (auto item : *d) {
|
||||
if (item.first == callback || item.second == data) {
|
||||
d->erase(i);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
GC_REGISTER_FINALIZER_NO_ORDER(ptr, ofn,
|
||||
ocd, nullptr, nullptr);
|
||||
Memory::GCAllocatedMemoryFinalizer cb = (Memory::GCAllocatedMemoryFinalizer)fn;
|
||||
cb(self);
|
||||
}
|
||||
|
||||
bool Memory::gcHasFinalizer(void* ptr)
|
||||
void Memory::gcRegisterFinalizer(ObjectRef* ptr, GCAllocatedMemoryFinalizer callback)
|
||||
{
|
||||
GC_finalization_proc ofn = nullptr;
|
||||
void* ocd = nullptr;
|
||||
GC_REGISTER_FINALIZER_NO_ORDER(ptr, nullptr,
|
||||
nullptr, &ofn, &ocd);
|
||||
|
||||
if (ofn == generalPublicFinalizer) {
|
||||
FinalizerData* d = reinterpret_cast<FinalizerData*>(ocd);
|
||||
bool ret = !!d->size();
|
||||
GC_REGISTER_FINALIZER_NO_ORDER(ptr, ofn,
|
||||
ocd, nullptr, nullptr);
|
||||
return ret;
|
||||
}
|
||||
return false;
|
||||
toImpl(ptr)->addFinalizer(ObjectRefFinalizer, (void*)callback);
|
||||
}
|
||||
|
||||
bool Memory::gcHasFinalizer(void* ptr, GCAllocatedMemoryFinalizer callback, void* data)
|
||||
void Memory::gcUnregisterFinalizer(ObjectRef* ptr, GCAllocatedMemoryFinalizer callback)
|
||||
{
|
||||
GC_finalization_proc ofn = nullptr;
|
||||
void* ocd = nullptr;
|
||||
GC_REGISTER_FINALIZER_NO_ORDER(ptr, nullptr,
|
||||
nullptr, &ofn, &ocd);
|
||||
|
||||
if (ofn == generalPublicFinalizer) {
|
||||
FinalizerData* d = reinterpret_cast<FinalizerData*>(ocd);
|
||||
bool ret = false;
|
||||
|
||||
for (auto item : *d) {
|
||||
if (item.first == callback || item.second == data) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GC_REGISTER_FINALIZER_NO_ORDER(ptr, ofn,
|
||||
ocd, nullptr, nullptr);
|
||||
return ret;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Memory::gcRegisterFinalizer(ValueRef* ptr, GCAllocatedMemoryFinalizer callback, void* data)
|
||||
{
|
||||
if (ptr->isObject()) {
|
||||
Memory::gcRegisterFinalizer(ptr->asObject(), callback, data);
|
||||
} else if (ptr->isSymbol()) {
|
||||
Memory::gcRegisterFinalizer(ptr->asSymbol(), callback, data);
|
||||
} else {
|
||||
Memory::gcRegisterFinalizer(reinterpret_cast<void*>(ptr), callback, data);
|
||||
}
|
||||
}
|
||||
|
||||
void Memory::gcUnregisterFinalizer(ValueRef* ptr, GCAllocatedMemoryFinalizer callback, void* data)
|
||||
{
|
||||
if (ptr->isObject()) {
|
||||
Memory::gcUnregisterFinalizer(ptr->asObject(), callback, data);
|
||||
} else if (ptr->isSymbol()) {
|
||||
Memory::gcUnregisterFinalizer(ptr->asSymbol(), callback, data);
|
||||
} else {
|
||||
Memory::gcUnregisterFinalizer(reinterpret_cast<void*>(ptr), callback, data);
|
||||
}
|
||||
}
|
||||
|
||||
bool Memory::gcHasFinalizer(ValueRef* ptr)
|
||||
{
|
||||
if (ptr->isObject()) {
|
||||
return Memory::gcHasFinalizer(ptr->asObject());
|
||||
} else if (ptr->isSymbol()) {
|
||||
return Memory::gcHasFinalizer(ptr->asSymbol());
|
||||
} else {
|
||||
return Memory::gcHasFinalizer(reinterpret_cast<void*>(ptr));
|
||||
}
|
||||
}
|
||||
|
||||
bool Memory::gcHasFinalizer(ValueRef* ptr, GCAllocatedMemoryFinalizer callback, void* data)
|
||||
{
|
||||
if (ptr->isObject()) {
|
||||
return Memory::gcHasFinalizer(ptr->asObject(), callback, data);
|
||||
} else if (ptr->isSymbol()) {
|
||||
return Memory::gcHasFinalizer(ptr->asSymbol(), callback, data);
|
||||
} else {
|
||||
return Memory::gcHasFinalizer(reinterpret_cast<void*>(ptr), callback, data);
|
||||
}
|
||||
}
|
||||
|
||||
void Memory::gcRegisterFinalizer(ObjectRef* ptr, GCAllocatedMemoryFinalizer callback, void* data)
|
||||
{
|
||||
toImpl(ptr)->addFinalizer(reinterpret_cast<FinalizerFunction>(callback), data);
|
||||
}
|
||||
|
||||
void Memory::gcUnregisterFinalizer(ObjectRef* ptr, GCAllocatedMemoryFinalizer callback, void* data)
|
||||
{
|
||||
toImpl(ptr)->removeFinalizer(reinterpret_cast<FinalizerFunction>(callback), data);
|
||||
}
|
||||
|
||||
bool Memory::gcHasFinalizer(ObjectRef* ptr)
|
||||
{
|
||||
auto obj = toImpl(ptr);
|
||||
if (obj->hasExtendedExtraData()) {
|
||||
auto* f = obj->ensureExtendedExtraData();
|
||||
return f->m_finalizer.size() && f->m_finalizer.size() != f->m_removedFinalizerCount;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Memory::gcHasFinalizer(ObjectRef* ptr, GCAllocatedMemoryFinalizer callback, void* data)
|
||||
{
|
||||
auto obj = toImpl(ptr);
|
||||
if (obj->hasExtendedExtraData()) {
|
||||
auto& fs = obj->ensureExtendedExtraData()->m_finalizer;
|
||||
for (auto& f : fs) {
|
||||
if (f.first == reinterpret_cast<FinalizerFunction>(callback) && f.second == data) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Memory::gcRegisterFinalizer(SymbolRef* ptr, GCAllocatedMemoryFinalizer callback, void* data)
|
||||
{
|
||||
toImpl(ptr)->addFinalizer(reinterpret_cast<FinalizerFunction>(callback), data);
|
||||
}
|
||||
|
||||
void Memory::gcUnregisterFinalizer(SymbolRef* ptr, GCAllocatedMemoryFinalizer callback, void* data)
|
||||
{
|
||||
toImpl(ptr)->removeFinalizer(reinterpret_cast<FinalizerFunction>(callback), data);
|
||||
}
|
||||
|
||||
bool Memory::gcHasFinalizer(SymbolRef* ptr)
|
||||
{
|
||||
auto symbol = toImpl(ptr);
|
||||
if (symbol->hasFinalizerData()) {
|
||||
auto* f = symbol->finalizerData();
|
||||
return f->m_finalizer.size() && f->m_finalizer.size() != f->m_removedFinalizerCount;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Memory::gcHasFinalizer(SymbolRef* ptr, GCAllocatedMemoryFinalizer callback, void* data)
|
||||
{
|
||||
auto symbol = toImpl(ptr);
|
||||
if (symbol->hasFinalizerData()) {
|
||||
auto& fs = symbol->finalizerData()->m_finalizer;
|
||||
for (auto& f : fs) {
|
||||
if (f.first == reinterpret_cast<FinalizerFunction>(callback) && f.second == data) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
toImpl(ptr)->removeFinalizer(ObjectRefFinalizer, (void*)callback);
|
||||
}
|
||||
|
||||
void Memory::gc()
|
||||
|
|
@ -566,16 +384,6 @@ void Memory::gc()
|
|||
GC_gcollect_and_unmap();
|
||||
}
|
||||
|
||||
void Memory::disableGC()
|
||||
{
|
||||
GC_disable();
|
||||
}
|
||||
|
||||
void Memory::enableGC()
|
||||
{
|
||||
GC_enable();
|
||||
}
|
||||
|
||||
void Memory::setGCFrequency(size_t value)
|
||||
{
|
||||
GC_set_free_space_divisor(value);
|
||||
|
|
@ -656,60 +464,6 @@ bool Memory::removeGCEventListener(GCEventType type, OnGCEventListener l, void*
|
|||
return false;
|
||||
}
|
||||
|
||||
#define GC_ATOMIC_UNCOLLECTABLE_KIND 3 // see mark.c
|
||||
|
||||
void PersistentRefHolderBase::setWeak()
|
||||
{
|
||||
if (!m_holder) {
|
||||
return;
|
||||
}
|
||||
|
||||
int kind = GC_get_kind_and_size(m_holder, NULL);
|
||||
if (kind == GC_ATOMIC_UNCOLLECTABLE_KIND) {
|
||||
return;
|
||||
}
|
||||
|
||||
void** newHolder = reinterpret_cast<void**>(GC_MALLOC_ATOMIC_UNCOLLECTABLE(sizeof(size_t)));
|
||||
*newHolder = *m_holder;
|
||||
GC_FREE(m_holder);
|
||||
m_holder = newHolder;
|
||||
|
||||
GC_GENERAL_REGISTER_DISAPPEARING_LINK_SAFE(m_holder, *m_holder);
|
||||
}
|
||||
|
||||
void PersistentRefHolderBase::clearWeak()
|
||||
{
|
||||
if (!m_holder) {
|
||||
return;
|
||||
}
|
||||
|
||||
GC_unregister_disappearing_link(m_holder);
|
||||
|
||||
int kind = GC_get_kind_and_size(m_holder, NULL);
|
||||
if (kind != GC_ATOMIC_UNCOLLECTABLE_KIND) {
|
||||
return;
|
||||
}
|
||||
|
||||
void** newHolder = reinterpret_cast<void**>(GC_MALLOC_UNCOLLECTABLE(sizeof(size_t)));
|
||||
*newHolder = *m_holder;
|
||||
GC_FREE(m_holder);
|
||||
m_holder = newHolder;
|
||||
}
|
||||
|
||||
bool PersistentRefHolderBase::isWeak()
|
||||
{
|
||||
if (!m_holder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int kind = GC_get_kind_and_size(m_holder, NULL);
|
||||
if (kind == GC_ATOMIC_UNCOLLECTABLE_KIND) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// I store ref count as EncodedValue. this can prevent what bdwgc can see ref count as address (EncodedValue store integer value as odd)
|
||||
using PersistentValueRefMapImpl = HashMap<ValueRef*, EncodedValue, std::hash<void*>, std::equal_to<void*>, GCUtil::gc_malloc_allocator<std::pair<ValueRef* const, EncodedValue>>>;
|
||||
|
||||
|
|
@ -925,7 +679,7 @@ StringRef* StringRef::createReloadableString(VMInstanceRef* instance, bool is8Bi
|
|||
|
||||
StringRef* StringRef::emptyString()
|
||||
{
|
||||
return toRef(String::emptyString());
|
||||
return toRef(String::emptyString);
|
||||
}
|
||||
|
||||
char16_t StringRef::charAt(size_t idx)
|
||||
|
|
@ -1218,10 +972,10 @@ bool BigIntRef::isNegative()
|
|||
}
|
||||
|
||||
Evaluator::StackTraceData::StackTraceData()
|
||||
: srcName(toRef(String::emptyString()))
|
||||
, sourceCode(toRef(String::emptyString()))
|
||||
: srcName(toRef(String::emptyString))
|
||||
, sourceCode(toRef(String::emptyString))
|
||||
, loc(SIZE_MAX, SIZE_MAX, SIZE_MAX)
|
||||
, functionName(toRef(String::emptyString()))
|
||||
, functionName(toRef(String::emptyString))
|
||||
, callee(nullptr)
|
||||
, isFunction(false)
|
||||
, isConstructor(false)
|
||||
|
|
@ -1265,13 +1019,7 @@ StringRef* Evaluator::EvaluatorResult::resultOrErrorToString(ContextRef* ctx) co
|
|||
if (isSuccessful()) {
|
||||
return result->toStringWithoutException(ctx);
|
||||
} else {
|
||||
// Check if error value is valid before dereferencing
|
||||
// In some edge cases (e.g., nested eval throw with finally allocation),
|
||||
// the error value might be invalid or null
|
||||
if (error.hasValue()) {
|
||||
return ((ValueRef*)error.value())->toStringWithoutException(ctx);
|
||||
}
|
||||
return StringRef::emptyString();
|
||||
return ((ValueRef*)error.value())->toStringWithoutException(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1319,7 +1067,7 @@ Evaluator::EvaluatorResult Evaluator::executeFunction(ContextRef* ctx, ValueRef*
|
|||
|
||||
auto result = sb.run([](ExecutionState& state, void* data) -> Value {
|
||||
DataSender* sender = (DataSender*)data;
|
||||
ValueRef* (*runner)(ExecutionStateRef* state, void* passedData) = (ValueRef * (*)(ExecutionStateRef * state, void* passedData)) sender->fn;
|
||||
ValueRef* (*runner)(ExecutionStateRef * state, void* passedData) = (ValueRef * (*)(ExecutionStateRef * state, void* passedData)) sender->fn;
|
||||
return toImpl(runner(toRef(&state), sender->data));
|
||||
},
|
||||
&sender);
|
||||
|
|
@ -1342,7 +1090,7 @@ Evaluator::EvaluatorResult Evaluator::executeFunction(ContextRef* ctx, ValueRef*
|
|||
|
||||
auto result = sb.run([](ExecutionState& state, void* data) -> Value {
|
||||
DataSender* sender = (DataSender*)data;
|
||||
ValueRef* (*runner)(ExecutionStateRef* state, void* passedData, void* passedData2) = (ValueRef * (*)(ExecutionStateRef * state, void* passedData, void* passedData2)) sender->fn;
|
||||
ValueRef* (*runner)(ExecutionStateRef * state, void* passedData, void* passedData2) = (ValueRef * (*)(ExecutionStateRef * state, void* passedData, void* passedData2)) sender->fn;
|
||||
return toImpl(runner(toRef(&state), sender->data, sender->data2));
|
||||
},
|
||||
&sender);
|
||||
|
|
@ -1366,7 +1114,9 @@ Evaluator::EvaluatorResult Evaluator::executeFunction(ExecutionStateRef* parentS
|
|||
auto result = sb.run(*toImpl(parentState), [](ExecutionState& state, void* data) -> Value {
|
||||
DataSender* sender = (DataSender*)data;
|
||||
ValueRef* (*runner)(ExecutionStateRef * state, void* passedData, void* passedData2) = (ValueRef * (*)(ExecutionStateRef * state, void* passedData, void* passedData2)) sender->fn;
|
||||
return toImpl(runner(toRef(&state), sender->data, sender->data2)); }, &sender);
|
||||
return toImpl(runner(toRef(&state), sender->data, sender->data2));
|
||||
},
|
||||
&sender);
|
||||
return toEvaluatorResultRef(result);
|
||||
}
|
||||
|
||||
|
|
@ -1453,16 +1203,6 @@ void VMInstanceRef::unregisterPromiseRejectCallback()
|
|||
toImpl(this)->unregisterPromiseRejectCallback();
|
||||
}
|
||||
|
||||
size_t VMInstanceRef::config()
|
||||
{
|
||||
return toImpl(this)->config();
|
||||
}
|
||||
|
||||
void VMInstanceRef::setConfig(size_t s)
|
||||
{
|
||||
toImpl(this)->setConfig(s);
|
||||
}
|
||||
|
||||
void VMInstanceRef::enterIdleMode()
|
||||
{
|
||||
toImpl(this)->enterIdleMode();
|
||||
|
|
@ -1492,25 +1232,6 @@ Evaluator::EvaluatorResult VMInstanceRef::executePendingJob()
|
|||
return toEvaluatorResultRef(result);
|
||||
}
|
||||
|
||||
void VMInstanceRef::enqueueEvaluateJob(ContextRef* relalatedContext, EvaluateJobCallback callback, void* data)
|
||||
{
|
||||
struct Holder : public gc {
|
||||
EvaluateJobCallback callback;
|
||||
void* data;
|
||||
Holder(EvaluateJobCallback callback, void* data)
|
||||
: callback(callback)
|
||||
, data(data)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
auto job = new EvaluateJob(toImpl(relalatedContext), [](ExecutionState& state, void* data) -> Value {
|
||||
Holder* holder = reinterpret_cast<Holder*>(data);
|
||||
return toImpl(holder->callback(toRef(&state), holder->data)); }, new Holder(callback, data));
|
||||
|
||||
toImpl(this)->enqueueJob(job);
|
||||
}
|
||||
|
||||
bool VMInstanceRef::hasPendingJobFromAnotherThread()
|
||||
{
|
||||
return toImpl(this)->hasPendingJobFromAnotherThread();
|
||||
|
|
@ -1990,15 +1711,13 @@ bool DebuggerOperationsRef::updateBreakpoint(WeakCodeRef* weakCodeRef, uint32_t
|
|||
|
||||
class DebuggerC : public Debugger {
|
||||
public:
|
||||
virtual void init(const char* options, Context* context) override {}
|
||||
virtual void parseCompleted(String* source, String* srcName, size_t originLineOffset, String* error = nullptr) override;
|
||||
virtual bool stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) override;
|
||||
virtual void stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) override;
|
||||
virtual void byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock) override;
|
||||
virtual void exceptionCaught(String* message, SavedStackTraceDataVector& exceptionTrace) override;
|
||||
virtual void consoleOut(String* output) override;
|
||||
virtual String* getClientSource(String** sourceName) override;
|
||||
virtual bool getWaitBeforeExitClient() override;
|
||||
virtual void deleteClient() override;
|
||||
|
||||
DebuggerC(DebuggerOperationsRef::DebuggerClient* debuggerClient, Context* context)
|
||||
: m_debuggerClient(debuggerClient)
|
||||
|
|
@ -2063,7 +1782,7 @@ static LexicalEnvironment* getFunctionLexEnv(ExecutionState* state)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool DebuggerC::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state)
|
||||
void DebuggerC::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state)
|
||||
{
|
||||
DebuggerOperationsRef::BreakpointOperations operations(reinterpret_cast<DebuggerOperationsRef::WeakCodeRef*>(byteCodeBlock), toRef(state), offset);
|
||||
|
||||
|
|
@ -2098,16 +1817,11 @@ bool DebuggerC::stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset,
|
|||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DebuggerC::byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock)
|
||||
{
|
||||
if (m_debuggerClient) {
|
||||
// Debugger could be removed earlier when this function called
|
||||
// e.g. release global objects such as Context and VMInstance at the end of execution
|
||||
m_debuggerClient->codeReleased(reinterpret_cast<DebuggerOperationsRef::WeakCodeRef*>(byteCodeBlock));
|
||||
}
|
||||
m_debuggerClient->codeReleased(reinterpret_cast<DebuggerOperationsRef::WeakCodeRef*>(byteCodeBlock));
|
||||
}
|
||||
|
||||
void DebuggerC::exceptionCaught(String* message, SavedStackTraceDataVector& exceptionTrace)
|
||||
|
|
@ -2133,15 +1847,6 @@ bool DebuggerC::getWaitBeforeExitClient()
|
|||
return false;
|
||||
}
|
||||
|
||||
void DebuggerC::deleteClient()
|
||||
{
|
||||
if (m_debuggerClient) {
|
||||
// delete DebuggerClient that was created and delivered from the user
|
||||
delete m_debuggerClient;
|
||||
m_debuggerClient = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool DebuggerC::processEvents(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, bool isBlockingRequest)
|
||||
{
|
||||
UNUSED_PARAMETER(state);
|
||||
|
|
@ -2264,28 +1969,28 @@ ObjectPropertyDescriptorRef::~ObjectPropertyDescriptorRef()
|
|||
}
|
||||
|
||||
ObjectPropertyDescriptorRef::ObjectPropertyDescriptorRef(void* src)
|
||||
: m_privateData(new(GC) ObjectPropertyDescriptor(*((ObjectPropertyDescriptor*)src)))
|
||||
: m_privateData(new (GC) ObjectPropertyDescriptor(*((ObjectPropertyDescriptor*)src)))
|
||||
{
|
||||
}
|
||||
|
||||
ObjectPropertyDescriptorRef::ObjectPropertyDescriptorRef(ValueRef* value)
|
||||
: m_privateData(new(GC) ObjectPropertyDescriptor(toImpl(value), ObjectPropertyDescriptor::ValuePresent))
|
||||
: m_privateData(new (GC) ObjectPropertyDescriptor(toImpl(value), ObjectPropertyDescriptor::ValuePresent))
|
||||
{
|
||||
}
|
||||
|
||||
ObjectPropertyDescriptorRef::ObjectPropertyDescriptorRef(ValueRef* value, bool writable)
|
||||
: m_privateData(new(GC) ObjectPropertyDescriptor(toImpl(value),
|
||||
(ObjectPropertyDescriptor::PresentAttribute)((writable ? ObjectPropertyDescriptor::WritablePresent : ObjectPropertyDescriptor::NonWritablePresent) | ObjectPropertyDescriptor::ValuePresent)))
|
||||
: m_privateData(new (GC) ObjectPropertyDescriptor(toImpl(value),
|
||||
(ObjectPropertyDescriptor::PresentAttribute)((writable ? ObjectPropertyDescriptor::WritablePresent : ObjectPropertyDescriptor::NonWritablePresent) | ObjectPropertyDescriptor::ValuePresent)))
|
||||
{
|
||||
}
|
||||
|
||||
ObjectPropertyDescriptorRef::ObjectPropertyDescriptorRef(ValueRef* getter, ValueRef* setter)
|
||||
: m_privateData(new(GC) ObjectPropertyDescriptor(JSGetterSetter(toImpl(getter), toImpl(setter)), ObjectPropertyDescriptor::NotPresent))
|
||||
: m_privateData(new (GC) ObjectPropertyDescriptor(JSGetterSetter(toImpl(getter), toImpl(setter)), ObjectPropertyDescriptor::NotPresent))
|
||||
{
|
||||
}
|
||||
|
||||
ObjectPropertyDescriptorRef::ObjectPropertyDescriptorRef(const ObjectPropertyDescriptorRef& src)
|
||||
: m_privateData(new(GC) ObjectPropertyDescriptor(*((ObjectPropertyDescriptor*)src.m_privateData)))
|
||||
: m_privateData(new (GC) ObjectPropertyDescriptor(*((ObjectPropertyDescriptor*)src.m_privateData)))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -2553,7 +2258,9 @@ bool ObjectRef::defineNativeDataAccessorProperty(ExecutionStateRef* state, Value
|
|||
{
|
||||
ObjectPropertyNativeGetterSetterData* innerData = new ObjectPropertyNativeGetterSetterData(publicData->m_isWritable, publicData->m_isEnumerable, publicData->m_isConfigurable, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
NativeDataAccessorPropertyData* publicData = reinterpret_cast<NativeDataAccessorPropertyData*>(privateDataFromObjectPrivateArea.payload());
|
||||
return toImpl(publicData->m_getter(toRef(&state), toRef(self), toRef(receiver), publicData)); }, nullptr, actsLikeJSGetterSetter);
|
||||
return toImpl(publicData->m_getter(toRef(&state), toRef(self), toRef(receiver), publicData));
|
||||
},
|
||||
nullptr, actsLikeJSGetterSetter);
|
||||
|
||||
if (!publicData->m_isWritable) {
|
||||
innerData->m_setter = nullptr;
|
||||
|
|
@ -2713,21 +2420,6 @@ void ObjectRef::setLength(ExecutionStateRef* pState, uint64_t newLength)
|
|||
toImpl(this)->set(state, state.context()->staticStrings().length, Value(newLength), toImpl(this));
|
||||
}
|
||||
|
||||
IteratorObjectRef* ObjectRef::values(ExecutionStateRef* state)
|
||||
{
|
||||
return toRef(toImpl(this)->values(*toImpl(state)));
|
||||
}
|
||||
|
||||
IteratorObjectRef* ObjectRef::keys(ExecutionStateRef* state)
|
||||
{
|
||||
return toRef(toImpl(this)->keys(*toImpl(state)));
|
||||
}
|
||||
|
||||
IteratorObjectRef* ObjectRef::entries(ExecutionStateRef* state)
|
||||
{
|
||||
return toRef(toImpl(this)->entries(*toImpl(state)));
|
||||
}
|
||||
|
||||
bool ObjectRef::isExtensible(ExecutionStateRef* state)
|
||||
{
|
||||
return toImpl(this)->isExtensible(*toImpl(state));
|
||||
|
|
@ -2776,17 +2468,14 @@ void ObjectRef::removeFromHiddenClassChain(ExecutionStateRef* state)
|
|||
toImpl(this)->markThisObjectDontNeedStructureTransitionTable();
|
||||
}
|
||||
|
||||
void ObjectRef::preparePropertyStorageForPropertyAddition(size_t t)
|
||||
{
|
||||
toImpl(this)->preparePropertyStorage(t + toImpl(this)->ownPropertyCountOnStructure());
|
||||
}
|
||||
|
||||
void ObjectRef::enumerateObjectOwnProperties(ExecutionStateRef* state, const std::function<bool(ExecutionStateRef* state, ValueRef* propertyName, bool isWritable, bool isEnumerable, bool isConfigurable)>& cb, bool shouldSkipSymbolKey)
|
||||
{
|
||||
toImpl(this)->enumeration(*toImpl(state), [](ExecutionState& state, Object* self, const ObjectPropertyName& name, const ObjectStructurePropertyDescriptor& desc, void* data) -> bool {
|
||||
const std::function<bool(ExecutionStateRef * state, ValueRef * propertyName, bool isWritable, bool isEnumerable, bool isConfigurable)>* cb
|
||||
= (const std::function<bool(ExecutionStateRef * state, ValueRef * propertyName, bool isWritable, bool isEnumerable, bool isConfigurable)>*)data;
|
||||
return (*cb)(toRef(&state), toRef(name.toPlainValue()), desc.isWritable(), desc.isEnumerable(), desc.isConfigurable()); }, (void*)&cb, shouldSkipSymbolKey);
|
||||
return (*cb)(toRef(&state), toRef(name.toPlainValue()), desc.isWritable(), desc.isEnumerable(), desc.isConfigurable());
|
||||
},
|
||||
(void*)&cb, shouldSkipSymbolKey);
|
||||
}
|
||||
|
||||
FunctionObjectRef* GlobalObjectRef::object()
|
||||
|
|
@ -2894,16 +2583,6 @@ ObjectRef* GlobalObjectRef::aggregateErrorPrototype()
|
|||
return toRef(toImpl(this)->aggregateErrorPrototype());
|
||||
}
|
||||
|
||||
FunctionObjectRef* GlobalObjectRef::suppressedError()
|
||||
{
|
||||
return toRef(toImpl(this)->suppressedError());
|
||||
}
|
||||
|
||||
ObjectRef* GlobalObjectRef::suppressedErrorPrototype()
|
||||
{
|
||||
return toRef(toImpl(this)->suppressedErrorPrototype());
|
||||
}
|
||||
|
||||
FunctionObjectRef* GlobalObjectRef::string()
|
||||
{
|
||||
return toRef(toImpl(this)->string());
|
||||
|
|
@ -3114,16 +2793,6 @@ ObjectRef* GlobalObjectRef::uint8ClampedArrayPrototype()
|
|||
return toRef(toImpl(this)->uint8ClampedArrayPrototype());
|
||||
}
|
||||
|
||||
ObjectRef* GlobalObjectRef::float16Array()
|
||||
{
|
||||
return toRef(toImpl(this)->float16Array());
|
||||
}
|
||||
|
||||
ObjectRef* GlobalObjectRef::float16ArrayPrototype()
|
||||
{
|
||||
return toRef(toImpl(this)->float16ArrayPrototype());
|
||||
}
|
||||
|
||||
ObjectRef* GlobalObjectRef::float32Array()
|
||||
{
|
||||
return toRef(toImpl(this)->float32Array());
|
||||
|
|
@ -3393,7 +3062,7 @@ void ContextRef::throwException(ValueRef* exceptionValue)
|
|||
toImpl(this)->throwException(s, toImpl(exceptionValue));
|
||||
}
|
||||
|
||||
bool ContextRef::initDebuggerClient(DebuggerOperationsRef::DebuggerClient* debuggerClient)
|
||||
bool ContextRef::initDebugger(DebuggerOperationsRef::DebuggerClient* debuggerClient)
|
||||
{
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
Context* context = toImpl(this);
|
||||
|
|
@ -3409,23 +3078,10 @@ bool ContextRef::initDebuggerClient(DebuggerOperationsRef::DebuggerClient* debug
|
|||
#endif /* ESCARGOT_DEBUGGER */
|
||||
}
|
||||
|
||||
bool ContextRef::disableDebugger()
|
||||
bool ContextRef::initDebuggerRemote(const char* options)
|
||||
{
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
Context* context = toImpl(this);
|
||||
if (context->debugger()) {
|
||||
context->debugger()->deleteClient();
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ContextRef::initDebugger(const char* options)
|
||||
{
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
return toImpl(this)->initDebugger(options);
|
||||
return toImpl(this)->initDebuggerRemote(options);
|
||||
#else /* !ESCARGOT_DEBUGGER */
|
||||
return false;
|
||||
#endif /* ESCARGOT_DEBUGGER */
|
||||
|
|
@ -3449,24 +3105,6 @@ bool ContextRef::isWaitBeforeExit()
|
|||
#endif /* ESCARGOT_DEBUGGER */
|
||||
}
|
||||
|
||||
bool ContextRef::isDebuggerRestartTrue()
|
||||
{
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
return isDebuggerRunning() && toImpl(this)->debugger()->getRestart();
|
||||
#else /* !ESCARGOT_DEBUGGER */
|
||||
return false;
|
||||
#endif /* ESCARGOT_DEBUGGER */
|
||||
}
|
||||
|
||||
void ContextRef::setDebuggerRestart()
|
||||
{
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
if (isDebuggerRunning()) {
|
||||
toImpl(this)->debugger()->setRestart(false);
|
||||
}
|
||||
#endif /* ESCARGOT_DEBUGGER */
|
||||
}
|
||||
|
||||
void ContextRef::printDebugger(StringRef* output)
|
||||
{
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
|
|
@ -3884,21 +3522,6 @@ ValueRef* ValueRef::construct(ExecutionStateRef* state, const size_t argc, Value
|
|||
return toRef(Object::construct(*toImpl(state), o, argc, newArgv));
|
||||
}
|
||||
|
||||
void ValueRef::callConstructor(ExecutionStateRef* state, ObjectRef* receiver, const size_t argc, ValueRef** argv)
|
||||
{
|
||||
auto impl = toImpl(this);
|
||||
if (UNLIKELY(!impl.isObject())) {
|
||||
ErrorObject::throwBuiltinError(*toImpl(state), ErrorCode::TypeError, ErrorObject::Messages::NOT_Callable);
|
||||
}
|
||||
Object* o = impl.asObject();
|
||||
Value* newArgv = ALLOCA(sizeof(Value) * argc, Value);
|
||||
for (size_t i = 0; i < argc; i++) {
|
||||
newArgv[i] = toImpl(argv[i]);
|
||||
}
|
||||
|
||||
Object::callConstructor(*toImpl(state), o, toImpl(receiver), argc, newArgv);
|
||||
}
|
||||
|
||||
ValueRef* ValueRef::create(bool value)
|
||||
{
|
||||
return reinterpret_cast<ValueRef*>(EncodedValue(Value(value)).payload());
|
||||
|
|
@ -4102,6 +3725,19 @@ ArrayObjectRef* ArrayObjectRef::create(ExecutionStateRef* state, ValueVectorRef*
|
|||
return toRef(ret);
|
||||
}
|
||||
|
||||
IteratorObjectRef* ArrayObjectRef::values(ExecutionStateRef* state)
|
||||
{
|
||||
return toRef(toImpl(this)->values(*toImpl(state)));
|
||||
}
|
||||
IteratorObjectRef* ArrayObjectRef::keys(ExecutionStateRef* state)
|
||||
{
|
||||
return toRef(toImpl(this)->keys(*toImpl(state)));
|
||||
}
|
||||
IteratorObjectRef* ArrayObjectRef::entries(ExecutionStateRef* state)
|
||||
{
|
||||
return toRef(toImpl(this)->entries(*toImpl(state)));
|
||||
}
|
||||
|
||||
COMPILE_ASSERT((int)ErrorCode::None == (int)ErrorObjectRef::Code::None, "");
|
||||
COMPILE_ASSERT((int)ErrorCode::ReferenceError == (int)ErrorObjectRef::Code::ReferenceError, "");
|
||||
COMPILE_ASSERT((int)ErrorCode::TypeError == (int)ErrorObjectRef::Code::TypeError, "");
|
||||
|
|
@ -4155,12 +3791,6 @@ AggregateErrorObjectRef* AggregateErrorObjectRef::create(ExecutionStateRef* stat
|
|||
return toRef((AggregateErrorObject*)ErrorObject::createError(*toImpl(state), ErrorCode::AggregateError, toImpl(errorMessage)));
|
||||
}
|
||||
|
||||
SuppressedErrorObjectRef* SuppressedErrorObjectRef::create(ExecutionStateRef* state, StringRef* errorMessage, ValueRef* error, ValueRef* suppressed)
|
||||
{
|
||||
Object* proto = toImpl(state)->context()->globalObject()->suppressedErrorPrototype();
|
||||
return toRef(new SuppressedErrorObject(*toImpl(state), proto, toImpl(errorMessage), true, false, toImpl(error), toImpl(suppressed)));
|
||||
}
|
||||
|
||||
DateObjectRef* DateObjectRef::create(ExecutionStateRef* state)
|
||||
{
|
||||
return toRef(new DateObject(*toImpl(state)));
|
||||
|
|
@ -4414,7 +4044,7 @@ void ArrayBufferViewRef::setBuffer(ArrayBufferRef* bo, size_t byteOffset, size_t
|
|||
|
||||
void ArrayBufferViewRef::setBuffer(ArrayBufferRef* bo, size_t byteOffset, size_t byteLength)
|
||||
{
|
||||
toImpl(this)->setBuffer(toImpl(bo), byteOffset, byteLength, 0);
|
||||
toImpl(this)->setBuffer(toImpl(bo), byteOffset, byteLength);
|
||||
}
|
||||
|
||||
uint8_t* ArrayBufferViewRef::rawBuffer()
|
||||
|
|
@ -4478,12 +4108,6 @@ Int32ArrayObjectRef* Int32ArrayObjectRef::create(ExecutionStateRef* state)
|
|||
return toRef(new Int32ArrayObject(*toImpl(state)));
|
||||
}
|
||||
|
||||
Float16ArrayObjectRef* Float16ArrayObjectRef::create(ExecutionStateRef* state)
|
||||
{
|
||||
ASSERT(state != nullptr);
|
||||
return toRef(new Float16ArrayObject(*toImpl(state)));
|
||||
}
|
||||
|
||||
Float32ArrayObjectRef* Float32ArrayObjectRef::create(ExecutionStateRef* state)
|
||||
{
|
||||
ASSERT(state != nullptr);
|
||||
|
|
@ -4634,6 +4258,21 @@ size_t SetObjectRef::size(ExecutionStateRef* state)
|
|||
return toImpl(this)->size(*toImpl(state));
|
||||
}
|
||||
|
||||
IteratorObjectRef* SetObjectRef::values(ExecutionStateRef* state)
|
||||
{
|
||||
return toRef(toImpl(this)->values(*toImpl(state)));
|
||||
}
|
||||
|
||||
IteratorObjectRef* SetObjectRef::keys(ExecutionStateRef* state)
|
||||
{
|
||||
return toRef(toImpl(this)->keys(*toImpl(state)));
|
||||
}
|
||||
|
||||
IteratorObjectRef* SetObjectRef::entries(ExecutionStateRef* state)
|
||||
{
|
||||
return toRef(toImpl(this)->entries(*toImpl(state)));
|
||||
}
|
||||
|
||||
WeakSetObjectRef* WeakSetObjectRef::create(ExecutionStateRef* state)
|
||||
{
|
||||
return toRef(new WeakSetObject(*toImpl(state)));
|
||||
|
|
@ -4699,6 +4338,21 @@ size_t MapObjectRef::size(ExecutionStateRef* state)
|
|||
return toImpl(this)->size(*toImpl(state));
|
||||
}
|
||||
|
||||
IteratorObjectRef* MapObjectRef::values(ExecutionStateRef* state)
|
||||
{
|
||||
return toRef(toImpl(this)->values(*toImpl(state)));
|
||||
}
|
||||
|
||||
IteratorObjectRef* MapObjectRef::keys(ExecutionStateRef* state)
|
||||
{
|
||||
return toRef(toImpl(this)->keys(*toImpl(state)));
|
||||
}
|
||||
|
||||
IteratorObjectRef* MapObjectRef::entries(ExecutionStateRef* state)
|
||||
{
|
||||
return toRef(toImpl(this)->entries(*toImpl(state)));
|
||||
}
|
||||
|
||||
WeakMapObjectRef* WeakMapObjectRef::create(ExecutionStateRef* state)
|
||||
{
|
||||
return toRef(new WeakMapObject(*toImpl(state)));
|
||||
|
|
@ -4789,7 +4443,9 @@ void TemplateRef::setNativeDataAccessorProperty(ValueRef* propertyName, ObjectRe
|
|||
|
||||
ObjectPropertyNativeGetterSetterData* innerData = new ObjectPropertyNativeGetterSetterData(isWritable, isEnumerable, isConfigurable, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectRef::NativeDataAccessorPropertyData* publicData = reinterpret_cast<ObjectRef::NativeDataAccessorPropertyData*>(privateDataFromObjectPrivateArea.payload());
|
||||
return toImpl(publicData->m_getter(toRef(&state), toRef(self), toRef(receiver), publicData)); }, nullptr, actsLikeJSGetterSetter);
|
||||
return toImpl(publicData->m_getter(toRef(&state), toRef(self), toRef(receiver), publicData));
|
||||
},
|
||||
nullptr, actsLikeJSGetterSetter);
|
||||
|
||||
if (!isWritable) {
|
||||
innerData->m_setter = nullptr;
|
||||
|
|
@ -4809,9 +4465,12 @@ void TemplateRef::setNativeDataAccessorProperty(ValueRef* propertyName, ObjectRe
|
|||
|
||||
void TemplateRef::setNativeDataAccessorProperty(ValueRef* propertyName, ObjectRef::NativeDataAccessorPropertyData* publicData, bool actsLikeJSGetterSetter)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* innerData = new ObjectPropertyNativeGetterSetterData(publicData->m_isWritable, publicData->m_isEnumerable, publicData->m_isConfigurable, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* innerData = new ObjectPropertyNativeGetterSetterData(publicData->m_isWritable, publicData->m_isEnumerable, publicData->m_isConfigurable,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectRef::NativeDataAccessorPropertyData* publicData = reinterpret_cast<ObjectRef::NativeDataAccessorPropertyData*>(privateDataFromObjectPrivateArea.payload());
|
||||
return toImpl(publicData->m_getter(toRef(&state), toRef(self), toRef(receiver), publicData)); }, nullptr);
|
||||
return toImpl(publicData->m_getter(toRef(&state), toRef(self), toRef(receiver), publicData));
|
||||
},
|
||||
nullptr);
|
||||
innerData->m_actsLikeJSGetterSetter = actsLikeJSGetterSetter;
|
||||
if (!publicData->m_isWritable) {
|
||||
innerData->m_setter = nullptr;
|
||||
|
|
@ -5153,10 +4812,6 @@ ValueRef* SerializerRef::deserializeFrom(ContextRef* context, std::istringstream
|
|||
{
|
||||
std::unique_ptr<SerializedValue> value = Serializer::deserializeFrom(input);
|
||||
|
||||
if (!value) {
|
||||
return ValueRef::createUndefined();
|
||||
}
|
||||
|
||||
SandBox sb(toImpl(context));
|
||||
auto result = sb.run([](ExecutionState& state, void* data) -> Value {
|
||||
std::unique_ptr<SerializedValue>* value = (std::unique_ptr<SerializedValue>*)data;
|
||||
|
|
|
|||
|
|
@ -84,14 +84,12 @@
|
|||
F(RangeErrorObject) \
|
||||
F(ReferenceErrorObject) \
|
||||
F(SyntaxErrorObject) \
|
||||
F(SuppressedErrorObject) \
|
||||
F(TypeErrorObject) \
|
||||
F(URIErrorObject)
|
||||
|
||||
#define ESCARGOT_TYPEDARRAY_REF_LIST(F) \
|
||||
F(BigInt64ArrayObject) \
|
||||
F(BigUint64ArrayObject) \
|
||||
F(Float16ArrayObject) \
|
||||
F(Float32ArrayObject) \
|
||||
F(Float64ArrayObject) \
|
||||
F(Int16ArrayObject) \
|
||||
|
|
@ -154,42 +152,23 @@ public:
|
|||
static void* gcMallocUncollectable(size_t siz); // allocate memory it can hold gc-allocated pointer & it is never collect by gc
|
||||
static void* gcMallocAtomicUncollectable(size_t siz); // allocate memory it can not hold gc-allocated pointer & it is never collect by gc
|
||||
static void gcFree(void* ptr);
|
||||
typedef void (*GCAllocatedMemoryFinalizer)(void* self, void* callbackData);
|
||||
typedef void (*GCAllocatedMemoryFinalizer)(void* self);
|
||||
|
||||
// gcRegisterFinalizer
|
||||
// 1. if you want to free memory explicitly, you must remove registered finalizer
|
||||
// if there was no finalizer, you can just free memory
|
||||
// ex) void* gcPointer;
|
||||
// Memory::gcRegisterFinalizer(gcPointer, callback...);
|
||||
// gcRegisterFinalizer(gcPointer, ....);
|
||||
// ......
|
||||
// Memory::gcUnregisterFinalizer(gcPointer, callback...); // this removes finalizer
|
||||
// gcRegisterFinalizer(gcPointer, nullptr); // this removes finalizer
|
||||
// Memory::gcFree(gcPointer);
|
||||
// 2. the data parameter will be considered accessible by gc.
|
||||
static void gcRegisterFinalizer(void* ptr, GCAllocatedMemoryFinalizer callback, void* data);
|
||||
static void gcUnregisterFinalizer(void* ptr, GCAllocatedMemoryFinalizer callback, void* data);
|
||||
static bool gcHasFinalizer(void* ptr);
|
||||
static bool gcHasFinalizer(void* ptr, GCAllocatedMemoryFinalizer callback, void* data);
|
||||
static void gcRegisterFinalizer(void* ptr, GCAllocatedMemoryFinalizer callback);
|
||||
|
||||
static void gcRegisterFinalizer(ValueRef* ptr, GCAllocatedMemoryFinalizer callback, void* data);
|
||||
static void gcUnregisterFinalizer(ValueRef* ptr, GCAllocatedMemoryFinalizer callback, void* data);
|
||||
static bool gcHasFinalizer(ValueRef* ptr);
|
||||
static bool gcHasFinalizer(ValueRef* ptr, GCAllocatedMemoryFinalizer callback, void* data);
|
||||
|
||||
static void gcRegisterFinalizer(ObjectRef* ptr, GCAllocatedMemoryFinalizer callback, void* data);
|
||||
static void gcUnregisterFinalizer(ObjectRef* ptr, GCAllocatedMemoryFinalizer callback, void* data);
|
||||
static bool gcHasFinalizer(ObjectRef* ptr);
|
||||
static bool gcHasFinalizer(ObjectRef* ptr, GCAllocatedMemoryFinalizer callback, void* data);
|
||||
|
||||
static void gcRegisterFinalizer(SymbolRef* ptr, GCAllocatedMemoryFinalizer callback, void* data);
|
||||
static void gcUnregisterFinalizer(SymbolRef* ptr, GCAllocatedMemoryFinalizer callback, void* data);
|
||||
static bool gcHasFinalizer(SymbolRef* ptr);
|
||||
static bool gcHasFinalizer(SymbolRef* ptr, GCAllocatedMemoryFinalizer callback, void* data);
|
||||
static void gcRegisterFinalizer(ObjectRef* ptr, GCAllocatedMemoryFinalizer callback);
|
||||
static void gcUnregisterFinalizer(ObjectRef* ptr, GCAllocatedMemoryFinalizer callback);
|
||||
|
||||
static void gc();
|
||||
|
||||
static void disableGC();
|
||||
static void enableGC();
|
||||
|
||||
static size_t heapSize(); // Return the number of bytes in the heap. Excludes bdwgc private data structures. Excludes the unmapped memory
|
||||
static size_t totalSize(); // Return the total number of bytes allocated in this process
|
||||
|
||||
|
|
@ -210,39 +189,9 @@ public:
|
|||
static void setGCFrequency(size_t value = 1);
|
||||
};
|
||||
|
||||
class ESCARGOT_EXPORT PersistentRefHolderBase {
|
||||
public:
|
||||
void setWeak();
|
||||
void clearWeak();
|
||||
bool isWeak();
|
||||
|
||||
protected:
|
||||
PersistentRefHolderBase()
|
||||
{
|
||||
m_holder = nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void initHolderSpace(T* initialValue)
|
||||
{
|
||||
m_holder = reinterpret_cast<void**>(Memory::gcMallocUncollectable(sizeof(T*)));
|
||||
*(reinterpret_cast<T**>(m_holder)) = initialValue;
|
||||
}
|
||||
|
||||
void destoryHolderSpace()
|
||||
{
|
||||
if (m_holder) {
|
||||
Memory::gcFree(m_holder);
|
||||
}
|
||||
m_holder = nullptr;
|
||||
}
|
||||
|
||||
void** m_holder;
|
||||
};
|
||||
|
||||
// NOTE only {stack, kinds of PersistentHolders} are root set. if you store the data you need on other space, you may lost your data
|
||||
template <typename T>
|
||||
class ESCARGOT_EXPORT PersistentRefHolder : public PersistentRefHolderBase {
|
||||
class ESCARGOT_EXPORT PersistentRefHolder {
|
||||
public:
|
||||
~PersistentRefHolder()
|
||||
{
|
||||
|
|
@ -251,6 +200,7 @@ public:
|
|||
|
||||
PersistentRefHolder()
|
||||
{
|
||||
m_holder = nullptr;
|
||||
}
|
||||
|
||||
PersistentRefHolder(T* ptr)
|
||||
|
|
@ -290,22 +240,18 @@ public:
|
|||
|
||||
operator T*()
|
||||
{
|
||||
return m_holder ? *(reinterpret_cast<T**>(m_holder)) : nullptr;
|
||||
return *m_holder;
|
||||
}
|
||||
|
||||
T* get()
|
||||
{
|
||||
if (m_holder) {
|
||||
return *(reinterpret_cast<T**>(m_holder));
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
return *m_holder;
|
||||
}
|
||||
|
||||
T* release()
|
||||
{
|
||||
if (m_holder) {
|
||||
T* ptr = *(reinterpret_cast<T**>(m_holder));
|
||||
T* ptr = *m_holder;
|
||||
destoryHolderSpace();
|
||||
return ptr;
|
||||
}
|
||||
|
|
@ -314,8 +260,25 @@ public:
|
|||
|
||||
T* operator->()
|
||||
{
|
||||
return *(reinterpret_cast<T**>(m_holder));
|
||||
return *m_holder;
|
||||
}
|
||||
|
||||
private:
|
||||
void initHolderSpace(T* initialValue)
|
||||
{
|
||||
m_holder = (T**)Memory::gcMallocUncollectable(sizeof(T*));
|
||||
*m_holder = initialValue;
|
||||
}
|
||||
|
||||
void destoryHolderSpace()
|
||||
{
|
||||
if (m_holder) {
|
||||
Memory::gcFree(m_holder);
|
||||
}
|
||||
m_holder = nullptr;
|
||||
}
|
||||
|
||||
T** m_holder;
|
||||
};
|
||||
|
||||
class PersistentValueRefMap {
|
||||
|
|
@ -618,14 +581,14 @@ public:
|
|||
template <typename... Args, typename F>
|
||||
static EvaluatorResult execute(ContextRef* ctx, F&& closure, Args... args)
|
||||
{
|
||||
typedef ValueRef* (*Closure)(ExecutionStateRef* state, Args...);
|
||||
typedef ValueRef* (*Closure)(ExecutionStateRef * state, Args...);
|
||||
return executeImpl(ctx, Closure(closure), args...);
|
||||
}
|
||||
|
||||
template <typename... Args, typename F>
|
||||
static EvaluatorResult execute(ExecutionStateRef* parent, F&& closure, Args... args)
|
||||
{
|
||||
typedef ValueRef* (*Closure)(ExecutionStateRef* state, Args...);
|
||||
typedef ValueRef* (*Closure)(ExecutionStateRef * state, Args...);
|
||||
return executeImpl(parent, Closure(closure), args...);
|
||||
}
|
||||
|
||||
|
|
@ -637,7 +600,7 @@ private:
|
|||
template <typename P, typename... Args>
|
||||
static EvaluatorResult executeImpl(P* p, ValueRef* (*fn)(ExecutionStateRef* state, Args...), Args... args)
|
||||
{
|
||||
typedef ValueRef* (*Closure)(ExecutionStateRef* state, Args...);
|
||||
typedef ValueRef* (*Closure)(ExecutionStateRef * state, Args...);
|
||||
std::tuple<ExecutionStateRef*, Args...> tuple = std::tuple<ExecutionStateRef*, Args...>(nullptr, args...);
|
||||
|
||||
return executeFunction(p, [](ExecutionStateRef* state, void* tuplePtr, void* fnPtr) -> ValueRef* {
|
||||
|
|
@ -646,7 +609,9 @@ private:
|
|||
|
||||
std::get<0>(*tuple) = state;
|
||||
|
||||
return EvaluatorUtil::applyTupleIntoArgumentsOfVariadicTemplateFunction(fn, *tuple); }, &tuple, (void*)fn);
|
||||
return EvaluatorUtil::applyTupleIntoArgumentsOfVariadicTemplateFunction(fn, *tuple);
|
||||
},
|
||||
&tuple, (void*)fn);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -729,21 +694,10 @@ public:
|
|||
void registerPromiseRejectCallback(PromiseRejectCallback);
|
||||
void unregisterPromiseRejectCallback();
|
||||
|
||||
enum class ConfigFlag {
|
||||
CompressCompressibleStringsWhileGC = 1,
|
||||
PruneCompiledByteCodesWhileGC = 1 << 1,
|
||||
|
||||
CompressCompressibleStringsEnterIdle = 1 << 3,
|
||||
PruneCompiledByteCodesEnterIdle = 1 << 4,
|
||||
UnloadReloadableStringsEnterIdle = 1 << 5,
|
||||
};
|
||||
size_t config();
|
||||
void setConfig(size_t s);
|
||||
|
||||
// this function enforce do gc,
|
||||
// remove every compiled bytecodes(if enabled),
|
||||
// remove every compiled bytecodes,
|
||||
// remove regexp cache,
|
||||
// and compress every comressible strings(if enabled) if we can
|
||||
// and compress every comressible strings if we can
|
||||
void enterIdleMode();
|
||||
// force clear every caches related with context
|
||||
// you can call this function if you don't want to use every alive contexts
|
||||
|
|
@ -762,13 +716,9 @@ public:
|
|||
SymbolRef* replaceSymbol();
|
||||
SymbolRef* splitSymbol();
|
||||
SymbolRef* asyncIteratorSymbol();
|
||||
SymbolRef* disposeSymbol();
|
||||
SymbolRef* asyncDisposeSymbol();
|
||||
|
||||
bool hasPendingJob();
|
||||
Evaluator::EvaluatorResult executePendingJob();
|
||||
typedef ValueRef* (*EvaluateJobCallback)(ExecutionStateRef* state, void* data);
|
||||
void enqueueEvaluateJob(ContextRef* relatedContext, EvaluateJobCallback callback, void* data);
|
||||
|
||||
bool hasPendingJobFromAnotherThread();
|
||||
bool waitEventFromAnotherThread(unsigned timeoutInMillisecond = 0); // zero means infinity
|
||||
|
|
@ -920,7 +870,6 @@ public:
|
|||
// Base class for debugger callbacks
|
||||
class DebuggerClient {
|
||||
public:
|
||||
virtual ~DebuggerClient() {};
|
||||
virtual void parseCompleted(StringRef* source, StringRef* srcName, std::vector<DebuggerOperationsRef::BreakpointLocationsInfo*>& breakpointLocationsVector) = 0;
|
||||
virtual void parseError(StringRef* source, StringRef* srcName, StringRef* error) = 0;
|
||||
virtual void codeReleased(WeakCodeRef* weakCodeRef) = 0;
|
||||
|
|
@ -950,18 +899,15 @@ public:
|
|||
bool canThrowException();
|
||||
void throwException(ValueRef* exceptionValue);
|
||||
|
||||
bool initDebuggerClient(DebuggerOperationsRef::DebuggerClient* debuggerClient);
|
||||
bool disableDebugger();
|
||||
bool initDebugger(DebuggerOperationsRef::DebuggerClient* debuggerClient);
|
||||
// available options(separator is ';')
|
||||
// "--port=6501", default for TCP debugger
|
||||
bool initDebugger(const char* options);
|
||||
bool initDebuggerRemote(const char* options);
|
||||
bool isDebuggerRunning();
|
||||
bool isWaitBeforeExit();
|
||||
bool isDebuggerRestartTrue();
|
||||
void printDebugger(StringRef* output);
|
||||
void pumpDebuggerEvents();
|
||||
void setAsAlwaysStopState();
|
||||
void setDebuggerRestart();
|
||||
StringRef* getClientSource(StringRef** sourceName);
|
||||
|
||||
typedef OptionalRef<ValueRef> (*VirtualIdentifierCallback)(ExecutionStateRef* state, ValueRef* name);
|
||||
|
|
@ -1088,8 +1034,6 @@ public:
|
|||
bool instanceOf(ExecutionStateRef* state, const ValueRef* other) const;
|
||||
ValueRef* call(ExecutionStateRef* state, ValueRef* receiver, const size_t argc, ValueRef** argv);
|
||||
ValueRef* construct(ExecutionStateRef* state, const size_t argc, ValueRef** argv); // same with new expression in js
|
||||
// call constrictor with already created object
|
||||
void callConstructor(ExecutionStateRef* state, ObjectRef* receiver, const size_t argc, ValueRef** argv);
|
||||
};
|
||||
|
||||
class ESCARGOT_EXPORT ValueVectorRef {
|
||||
|
|
@ -1472,10 +1416,6 @@ public:
|
|||
uint64_t length(ExecutionStateRef* state);
|
||||
void setLength(ExecutionStateRef* state, uint64_t newLength);
|
||||
|
||||
IteratorObjectRef* values(ExecutionStateRef* state);
|
||||
IteratorObjectRef* keys(ExecutionStateRef* state);
|
||||
IteratorObjectRef* entries(ExecutionStateRef* state);
|
||||
|
||||
bool isExtensible(ExecutionStateRef* state);
|
||||
bool preventExtensions(ExecutionStateRef* state);
|
||||
bool setIntegrityLevel(ExecutionStateRef* state, bool isSealed);
|
||||
|
|
@ -1487,8 +1427,6 @@ public:
|
|||
|
||||
void removeFromHiddenClassChain();
|
||||
|
||||
void preparePropertyStorageForPropertyAddition(size_t expandCount);
|
||||
|
||||
// DEPRECATED! this function will be removed
|
||||
void removeFromHiddenClassChain(ExecutionStateRef* state);
|
||||
};
|
||||
|
|
@ -1516,8 +1454,6 @@ public:
|
|||
ObjectRef* evalErrorPrototype();
|
||||
FunctionObjectRef* aggregateError();
|
||||
ObjectRef* aggregateErrorPrototype();
|
||||
FunctionObjectRef* suppressedError();
|
||||
ObjectRef* suppressedErrorPrototype();
|
||||
FunctionObjectRef* string();
|
||||
ObjectRef* stringPrototype();
|
||||
FunctionObjectRef* number();
|
||||
|
|
@ -1562,8 +1498,6 @@ public:
|
|||
ObjectRef* uint32ArrayPrototype();
|
||||
ObjectRef* uint8ClampedArray();
|
||||
ObjectRef* uint8ClampedArrayPrototype();
|
||||
ObjectRef* float16Array();
|
||||
ObjectRef* float16ArrayPrototype();
|
||||
ObjectRef* float32Array();
|
||||
ObjectRef* float32ArrayPrototype();
|
||||
ObjectRef* float64Array();
|
||||
|
|
@ -1677,6 +1611,9 @@ public:
|
|||
static ArrayObjectRef* create(ExecutionStateRef* state);
|
||||
static ArrayObjectRef* create(ExecutionStateRef* state, const uint64_t size);
|
||||
static ArrayObjectRef* create(ExecutionStateRef* state, ValueVectorRef* source);
|
||||
IteratorObjectRef* values(ExecutionStateRef* state);
|
||||
IteratorObjectRef* keys(ExecutionStateRef* state);
|
||||
IteratorObjectRef* entries(ExecutionStateRef* state);
|
||||
};
|
||||
|
||||
class ESCARGOT_EXPORT ErrorObjectRef : public ObjectRef {
|
||||
|
|
@ -1690,7 +1627,6 @@ public:
|
|||
URIError,
|
||||
EvalError,
|
||||
AggregateError,
|
||||
SuppressedError,
|
||||
};
|
||||
static ErrorObjectRef* create(ExecutionStateRef* state, ErrorObjectRef::Code code, StringRef* errorMessage);
|
||||
void updateStackTraceData(ExecutionStateRef* state); // update stacktrace data
|
||||
|
|
@ -1731,11 +1667,6 @@ public:
|
|||
static AggregateErrorObjectRef* create(ExecutionStateRef* state, StringRef* errorMessage);
|
||||
};
|
||||
|
||||
class ESCARGOT_EXPORT SuppressedErrorObjectRef : public ErrorObjectRef {
|
||||
public:
|
||||
static SuppressedErrorObjectRef* create(ExecutionStateRef* state, StringRef* errorMessage, ValueRef* error, ValueRef* suppressed);
|
||||
};
|
||||
|
||||
class ESCARGOT_EXPORT DateObjectRef : public ObjectRef {
|
||||
public:
|
||||
static DateObjectRef* create(ExecutionStateRef* state);
|
||||
|
|
@ -1790,14 +1721,12 @@ class ESCARGOT_EXPORT RegExpObjectRef : public ObjectRef {
|
|||
public:
|
||||
enum RegExpObjectOption {
|
||||
None = 0 << 0,
|
||||
HasIndices = 1 << 0,
|
||||
Global = 1 << 1,
|
||||
IgnoreCase = 1 << 2,
|
||||
MultiLine = 1 << 3,
|
||||
DotAll = 1 << 4,
|
||||
Unicode = 1 << 5,
|
||||
UnicodeSets = 1 << 6,
|
||||
Sticky = 1 << 7,
|
||||
Global = 1 << 0,
|
||||
IgnoreCase = 1 << 1,
|
||||
MultiLine = 1 << 2,
|
||||
Sticky = 1 << 3,
|
||||
Unicode = 1 << 4,
|
||||
DotAll = 1 << 5,
|
||||
};
|
||||
|
||||
struct ESCARGOT_EXPORT RegexMatchResult {
|
||||
|
|
@ -1916,11 +1845,6 @@ public:
|
|||
static Uint8ClampedArrayObjectRef* create(ExecutionStateRef* state);
|
||||
};
|
||||
|
||||
class ESCARGOT_EXPORT Float16ArrayObjectRef : public ArrayBufferViewRef {
|
||||
public:
|
||||
static Float16ArrayObjectRef* create(ExecutionStateRef* state);
|
||||
};
|
||||
|
||||
class ESCARGOT_EXPORT Float32ArrayObjectRef : public ArrayBufferViewRef {
|
||||
public:
|
||||
static Float32ArrayObjectRef* create(ExecutionStateRef* state);
|
||||
|
|
@ -1981,6 +1905,9 @@ public:
|
|||
bool deleteOperation(ExecutionStateRef* state, ValueRef* key);
|
||||
bool has(ExecutionStateRef* state, ValueRef* key);
|
||||
size_t size(ExecutionStateRef* state);
|
||||
IteratorObjectRef* values(ExecutionStateRef* state);
|
||||
IteratorObjectRef* keys(ExecutionStateRef* state);
|
||||
IteratorObjectRef* entries(ExecutionStateRef* state);
|
||||
};
|
||||
|
||||
class ESCARGOT_EXPORT WeakSetObjectRef : public ObjectRef {
|
||||
|
|
@ -2000,6 +1927,9 @@ public:
|
|||
bool has(ExecutionStateRef* state, ValueRef* key);
|
||||
void set(ExecutionStateRef* state, ValueRef* key, ValueRef* value);
|
||||
size_t size(ExecutionStateRef* state);
|
||||
IteratorObjectRef* values(ExecutionStateRef* state);
|
||||
IteratorObjectRef* keys(ExecutionStateRef* state);
|
||||
IteratorObjectRef* entries(ExecutionStateRef* state);
|
||||
};
|
||||
|
||||
class ESCARGOT_EXPORT WeakMapObjectRef : public ObjectRef {
|
||||
|
|
@ -2237,7 +2167,7 @@ public:
|
|||
{
|
||||
return calloc(sizeInByte, 1);
|
||||
}
|
||||
virtual void onFreeArrayBufferObjectDataBuffer(void* buffer, size_t sizeInByte, void* deleterData)
|
||||
virtual void onFreeArrayBufferObjectDataBuffer(void* buffer, size_t sizeInByte)
|
||||
{
|
||||
return free(buffer);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,12 +23,9 @@
|
|||
#include "runtime/VMInstance.h"
|
||||
#include "runtime/ArrayObject.h"
|
||||
#include "runtime/IteratorObject.h"
|
||||
#include "runtime/AsyncFromSyncIteratorObject.h"
|
||||
#include "runtime/ToStringRecursionPreventer.h"
|
||||
#include "runtime/ErrorObject.h"
|
||||
#include "runtime/NativeFunctionObject.h"
|
||||
#include "runtime/ExtendedNativeFunctionObject.h"
|
||||
#include "runtime/PromiseObject.h"
|
||||
#include "interpreter/ByteCodeInterpreter.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
|
@ -134,7 +131,7 @@ static Object* arraySpeciesCreate(ExecutionState& state, Object* originalArray,
|
|||
}
|
||||
// If IsConstructor(C) is false, throw a TypeError exception.
|
||||
if (!C.isConstructor()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, state.context()->staticStrings().Array.string(), false, String::emptyString(), ErrorObject::Messages::GlobalObject_ThisNotConstructor);
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, state.context()->staticStrings().Array.string(), false, String::emptyString, ErrorObject::Messages::GlobalObject_ThisNotConstructor);
|
||||
}
|
||||
// Return Construct(C, <<length>>).
|
||||
Value argv[1] = { Value(length) };
|
||||
|
|
@ -239,7 +236,7 @@ static Value builtinArrayFrom(ExecutionState& state, Value thisValue, size_t arg
|
|||
if (k >= ((1LL << 53LL) - 1LL)) {
|
||||
// Let error be ThrowCompletion(a newly created TypeError object).
|
||||
// Return ? IteratorClose(iteratorRecord, error).
|
||||
Value throwCompletion = ErrorObject::createError(state, ErrorCode::TypeError, new ASCIIStringFromExternalMemory("Got invalid index"));
|
||||
Value throwCompletion = ErrorObject::createError(state, ErrorCode::TypeError, new ASCIIString("Got invalid index"));
|
||||
return IteratorObject::iteratorClose(state, iteratorRecord, throwCompletion, true);
|
||||
}
|
||||
// Let Pk be ! ToString(k).
|
||||
|
|
@ -330,403 +327,6 @@ static Value builtinArrayFrom(ExecutionState& state, Value thisValue, size_t arg
|
|||
return A;
|
||||
}
|
||||
|
||||
struct ArrayFromAsyncRecord : public PointerValue {
|
||||
bool m_mapping;
|
||||
int m_awaitResumeStage;
|
||||
EncodedValue m_mapper;
|
||||
EncodedValue m_thisArg;
|
||||
EncodedValue m_a;
|
||||
int64_t m_k;
|
||||
IteratorRecord* m_iteratorRecord;
|
||||
EncodedValue m_asyncCloseReturnValue;
|
||||
EncodedValue m_asyncCloseInnerResult;
|
||||
EncodedValue m_throwCompletion;
|
||||
EncodedValue m_awaitResult;
|
||||
EncodedValue m_nextResult;
|
||||
EncodedValue m_nextValue;
|
||||
EncodedValue m_mappedValue;
|
||||
|
||||
PromiseReaction::Capability m_promiseCapability;
|
||||
|
||||
ArrayFromAsyncRecord(bool mapping, EncodedValue mapper, EncodedValue thisArg, EncodedValue a, IteratorRecord* record, PromiseReaction::Capability promiseCapability)
|
||||
: m_mapping(mapping)
|
||||
, m_awaitResumeStage(0)
|
||||
, m_mapper(mapper)
|
||||
, m_thisArg(thisArg)
|
||||
, m_a(a)
|
||||
, m_k(0)
|
||||
, m_iteratorRecord(record)
|
||||
, m_promiseCapability(promiseCapability)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool isArrayFromAsyncRecord() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static PromiseObject* arrayFromAsyncAsyncAwaitOperation(ExecutionState& state, const Value& awaitValue, ArrayFromAsyncRecord* data, size_t stage);
|
||||
|
||||
#define ASYNCITERATOR_CLOSE(iteratorRecord, stage, completion, completionHasThrownRecord) \
|
||||
try { \
|
||||
data->m_asyncCloseReturnValue = Object::getMethod(state, iteratorRecord->m_iterator, state.context()->staticStrings().stringReturn); \
|
||||
} catch (const Value& error) { \
|
||||
if (completionHasThrownRecord) { \
|
||||
data->m_promiseCapability.m_promise->asPromiseObject()->reject(state, completion); \
|
||||
} \
|
||||
data->m_promiseCapability.m_promise->asPromiseObject()->reject(state, error); \
|
||||
return; \
|
||||
} \
|
||||
if (!data->m_asyncCloseReturnValue.isUndefined()) { \
|
||||
try { \
|
||||
data->m_asyncCloseInnerResult = Object::call(state, data->m_asyncCloseReturnValue, iteratorRecord->m_iterator, 0, nullptr); \
|
||||
} catch (const Value& error) { \
|
||||
if (completionHasThrownRecord) { \
|
||||
data->m_promiseCapability.m_promise->asPromiseObject()->reject(state, completion); \
|
||||
} \
|
||||
data->m_promiseCapability.m_promise->asPromiseObject()->reject(state, error); \
|
||||
return; \
|
||||
} \
|
||||
arrayFromAsyncAsyncAwaitOperation(state, data->m_asyncCloseInnerResult, data, stage); \
|
||||
return; \
|
||||
} \
|
||||
ArrayFromAsyncAsyncWorkerStage##stage : if (completionHasThrownRecord) { data->m_promiseCapability.m_promise->asPromiseObject()->reject(state, completion); } \
|
||||
return;
|
||||
|
||||
static void builtinArrayFromAsyncAsyncWorker(ExecutionState& state, ArrayFromAsyncRecord* data)
|
||||
{
|
||||
if (data->m_awaitResumeStage == 1) {
|
||||
data->m_awaitResumeStage = 0;
|
||||
goto ArrayFromAsyncAsyncWorkerStage1;
|
||||
} else if (data->m_awaitResumeStage == 2) {
|
||||
data->m_awaitResumeStage = 0;
|
||||
goto ArrayFromAsyncAsyncWorkerStage2;
|
||||
} else if (data->m_awaitResumeStage == 3) {
|
||||
data->m_awaitResumeStage = 0;
|
||||
goto ArrayFromAsyncAsyncWorkerStage3;
|
||||
} else if (data->m_awaitResumeStage == 4) {
|
||||
data->m_awaitResumeStage = 0;
|
||||
goto ArrayFromAsyncAsyncWorkerStage4;
|
||||
} else if (data->m_awaitResumeStage == 5) {
|
||||
data->m_awaitResumeStage = 0;
|
||||
goto ArrayFromAsyncAsyncWorkerStage5;
|
||||
}
|
||||
// Repeat,
|
||||
while (true) {
|
||||
// If k ≥ 2**53 - 1, then
|
||||
if (data->m_k >= ((1LL << 53) - 1)) {
|
||||
// Let error be ThrowCompletion(a newly created TypeError object).
|
||||
data->m_throwCompletion = ErrorObject::createError(state, ErrorCode::TypeError, new ASCIIStringFromExternalMemory("Got invalid index"));
|
||||
// Return ? AsyncIteratorClose(iteratorRecord, error).
|
||||
ASYNCITERATOR_CLOSE(data->m_iteratorRecord, 1, data->m_throwCompletion, true);
|
||||
}
|
||||
// Let Pk be ! ToString(𝔽(k)).
|
||||
// Let nextResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]).
|
||||
data->m_nextResult = Object::call(state, data->m_iteratorRecord->m_nextMethod, data->m_iteratorRecord->m_iterator, 0, nullptr);
|
||||
// Set nextResult to ? Await(nextResult).
|
||||
arrayFromAsyncAsyncAwaitOperation(state, data->m_nextResult, data, 2);
|
||||
return;
|
||||
ArrayFromAsyncAsyncWorkerStage2:
|
||||
data->m_nextResult = data->m_awaitResult;
|
||||
// If nextResult is not an Object, throw a TypeError excetion.
|
||||
if (!data->m_nextResult.toValue().isObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "iterator result is not Object");
|
||||
}
|
||||
// Let done be ? IteratorComplete(nextResult).
|
||||
// If done is true, then
|
||||
if (IteratorObject::iteratorComplete(state, data->m_nextResult.toValue().asObject())) {
|
||||
// Perform ? Set(A, "length", 𝔽(k), true).
|
||||
try {
|
||||
data->m_a.toValue().toObject(state)->setThrowsException(state, state.context()->staticStrings().length, Value(data->m_k), data->m_a.toValue());
|
||||
} catch (const Value& error) {
|
||||
data->m_promiseCapability.m_promise->asPromiseObject()->reject(state, error);
|
||||
return;
|
||||
}
|
||||
// Return A.
|
||||
data->m_promiseCapability.m_promise->asPromiseObject()->fulfill(state, data->m_a);
|
||||
return;
|
||||
}
|
||||
// Let nextValue be ? IteratorValue(nextResult)
|
||||
data->m_nextValue = IteratorObject::iteratorValue(state, data->m_nextResult.toValue().asObject());
|
||||
|
||||
// If mapping is true, then
|
||||
if (data->m_mapping) {
|
||||
// Let mappedValue be Completion(Call(mapper, thisArg, « nextValue, 𝔽(k) »)).
|
||||
// IfAbruptCloseAsyncIterator(mappedValue, iteratorRecord).
|
||||
try {
|
||||
data->m_throwCompletion = Value(Value::EmptyValue);
|
||||
Value arg[2] = { data->m_nextValue, Value(data->m_k) };
|
||||
data->m_mappedValue = Object::call(state, data->m_mapper, data->m_thisArg, 2, arg);
|
||||
} catch (const Value& error) {
|
||||
data->m_throwCompletion = error;
|
||||
}
|
||||
if (!data->m_throwCompletion.isEmpty()) {
|
||||
ASYNCITERATOR_CLOSE(data->m_iteratorRecord, 3, data->m_throwCompletion, true);
|
||||
}
|
||||
// Set mappedValue to Completion(Await(mappedValue)).
|
||||
// IfAbruptCloseAsyncIterator(mappedValue, iteratorRecord).
|
||||
arrayFromAsyncAsyncAwaitOperation(state, data->m_mappedValue, data, 4);
|
||||
return;
|
||||
ArrayFromAsyncAsyncWorkerStage4:
|
||||
data->m_mappedValue = data->m_awaitResult;
|
||||
} else {
|
||||
data->m_mappedValue = data->m_nextValue;
|
||||
}
|
||||
|
||||
// Let defineStatus be Completion(CreateDataPropertyOrThrow(A, Pk, mappedValue)).
|
||||
// IfAbruptCloseAsyncIterator(defineStatus, iteratorRecord).
|
||||
try {
|
||||
data->m_throwCompletion = Value(Value::EmptyValue);
|
||||
data->m_a.toValue().toObject(state)->defineOwnPropertyThrowsException(state, ObjectPropertyName(state, data->m_k),
|
||||
ObjectPropertyDescriptor(data->m_mappedValue, ObjectPropertyDescriptor::AllPresent));
|
||||
} catch (const Value& error) {
|
||||
data->m_throwCompletion = error;
|
||||
}
|
||||
if (!data->m_throwCompletion.isEmpty()) {
|
||||
ASYNCITERATOR_CLOSE(data->m_iteratorRecord, 5, data->m_throwCompletion, true);
|
||||
}
|
||||
// Set k to k + 1.
|
||||
data->m_k++;
|
||||
}
|
||||
}
|
||||
|
||||
static Value arrayFromAsyncAsyncAwaitFulfilledFunction(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
auto s = state.resolveCallee()->asExtendedNativeFunctionObject();
|
||||
auto data = s->internalSlot(0).asPointerValue()->asArrayFromAsyncRecord();
|
||||
data->m_awaitResult = argv[0];
|
||||
builtinArrayFromAsyncAsyncWorker(state, data);
|
||||
return Value();
|
||||
}
|
||||
|
||||
static Value arrayFromAsyncAsyncAwaitRejectedFunction(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
auto s = state.resolveCallee()->asExtendedNativeFunctionObject();
|
||||
auto data = s->internalSlot(0).asPointerValue()->asArrayFromAsyncRecord();
|
||||
data->m_promiseCapability.m_promise->asPromiseObject()->reject(state, *argv);
|
||||
return Value();
|
||||
}
|
||||
|
||||
static PromiseObject* arrayFromAsyncAsyncAwaitOperation(ExecutionState& state, const Value& awaitValue, ArrayFromAsyncRecord* source, size_t stage)
|
||||
{
|
||||
source->m_awaitResumeStage = stage;
|
||||
PromiseObject* promise = PromiseObject::promiseResolve(state, state.context()->globalObject()->promise(), awaitValue)->asPromiseObject();
|
||||
ExtendedNativeFunctionObject* onFulfilled = new ExtendedNativeFunctionObjectImpl<1>(state, NativeFunctionInfo(AtomicString(), arrayFromAsyncAsyncAwaitFulfilledFunction, 1));
|
||||
onFulfilled->setInternalSlot(0, source);
|
||||
ExtendedNativeFunctionObject* onRejected = new ExtendedNativeFunctionObjectImpl<1>(state, NativeFunctionInfo(AtomicString(), arrayFromAsyncAsyncAwaitRejectedFunction, 1));
|
||||
onRejected->setInternalSlot(0, source);
|
||||
promise->then(state, onFulfilled, onRejected, Optional<PromiseReaction::Capability>());
|
||||
return promise;
|
||||
}
|
||||
|
||||
struct ArrayFromAsyncSyncRecord : public PointerValue {
|
||||
bool m_mapping;
|
||||
int m_awaitResumeStage;
|
||||
EncodedValue m_mapper;
|
||||
EncodedValue m_thisArg;
|
||||
EncodedValue m_a;
|
||||
int64_t m_len;
|
||||
Object* m_arrayLike;
|
||||
int64_t m_k;
|
||||
EncodedValue m_awaitResult;
|
||||
EncodedValue m_kValue;
|
||||
EncodedValue m_mappedValue;
|
||||
|
||||
PromiseReaction::Capability m_promiseCapability;
|
||||
|
||||
ArrayFromAsyncSyncRecord(bool mapping, EncodedValue mapper, EncodedValue thisArg, EncodedValue a, int64_t len, Object* arrayLike, PromiseReaction::Capability promiseCapability)
|
||||
: m_mapping(mapping)
|
||||
, m_awaitResumeStage(0)
|
||||
, m_mapper(mapper)
|
||||
, m_thisArg(thisArg)
|
||||
, m_a(a)
|
||||
, m_len(len)
|
||||
, m_arrayLike(arrayLike)
|
||||
, m_k(0)
|
||||
, m_promiseCapability(promiseCapability)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool isArrayFromAsyncSyncRecord() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
static PromiseObject* arrayFromAsyncSyncAwaitOperation(ExecutionState& state, const Value& awaitValue, ArrayFromAsyncSyncRecord* data, size_t stage);
|
||||
|
||||
static void builtinArrayFromSyncAsyncWorker(ExecutionState& state, ArrayFromAsyncSyncRecord* data)
|
||||
{
|
||||
if (data->m_awaitResumeStage == 1) {
|
||||
data->m_awaitResumeStage = 0;
|
||||
goto ArrayFromAsyncSyncWorkerStage1;
|
||||
} else if (data->m_awaitResumeStage == 2) {
|
||||
data->m_awaitResumeStage = 0;
|
||||
goto ArrayFromAsyncSyncWorkerStage2;
|
||||
}
|
||||
// Let k be 0.
|
||||
// Repeat, while k < len,
|
||||
while (data->m_k < data->m_len) {
|
||||
// Let Pk be ! ToString(𝔽(k)).
|
||||
// Let kValue be ? Get(arrayLike, Pk).
|
||||
try {
|
||||
data->m_kValue = data->m_arrayLike->get(state, ObjectPropertyName(state, data->m_k)).value(state, data->m_arrayLike);
|
||||
} catch (const Value& error) {
|
||||
data->m_promiseCapability.m_promise->asPromiseObject()->reject(state, error);
|
||||
return;
|
||||
}
|
||||
// Set kValue to ? Await(kValue).
|
||||
arrayFromAsyncSyncAwaitOperation(state, data->m_kValue, data, 1);
|
||||
return;
|
||||
ArrayFromAsyncSyncWorkerStage1:
|
||||
data->m_kValue = data->m_awaitResult;
|
||||
|
||||
// If mapping is true, then
|
||||
if (data->m_mapping) {
|
||||
// Let mappedValue be ? Call(mapper, thisArg, « kValue, 𝔽(k) »).
|
||||
try {
|
||||
Value arg[2] = { data->m_kValue, Value(data->m_k) };
|
||||
data->m_mappedValue = Object::call(state, data->m_mapper, data->m_thisArg, 2, arg);
|
||||
} catch (const Value& error) {
|
||||
data->m_promiseCapability.m_promise->asPromiseObject()->reject(state, error);
|
||||
return;
|
||||
}
|
||||
// Set mappedValue to ? Await(mappedValue).
|
||||
arrayFromAsyncSyncAwaitOperation(state, data->m_mappedValue, data, 2);
|
||||
return;
|
||||
ArrayFromAsyncSyncWorkerStage2:
|
||||
data->m_mappedValue = data->m_awaitResult;
|
||||
} else {
|
||||
// Else,
|
||||
// Let mappedValue be kValue.
|
||||
data->m_mappedValue = data->m_kValue;
|
||||
}
|
||||
// Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).
|
||||
data->m_a.toValue().toObject(state)->defineOwnPropertyThrowsException(
|
||||
state, ObjectPropertyName(state, data->m_k), ObjectPropertyDescriptor(data->m_mappedValue, ObjectPropertyDescriptor::AllPresent));
|
||||
// Set k to k + 1.
|
||||
data->m_k++;
|
||||
}
|
||||
// Perform ? Set(A, "length", 𝔽(len), true).
|
||||
// Return A.
|
||||
try {
|
||||
data->m_a.toValue().toObject(state)->setThrowsException(state, state.context()->staticStrings().length, Value(data->m_k), data->m_a.toValue());
|
||||
} catch (const Value& error) {
|
||||
data->m_promiseCapability.m_promise->asPromiseObject()->reject(state, error);
|
||||
return;
|
||||
}
|
||||
data->m_promiseCapability.m_promise->asPromiseObject()->fulfill(state, data->m_a);
|
||||
}
|
||||
|
||||
static Value arrayFromAsyncSyncAwaitFulfilledFunction(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
auto s = state.resolveCallee()->asExtendedNativeFunctionObject();
|
||||
auto data = s->internalSlot(0).asPointerValue()->asArrayFromAsyncSyncRecord();
|
||||
data->m_awaitResult = argv[0];
|
||||
builtinArrayFromSyncAsyncWorker(state, data);
|
||||
return Value();
|
||||
}
|
||||
|
||||
static Value arrayFromAsyncSyncAwaitRejectedFunction(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
auto s = state.resolveCallee()->asExtendedNativeFunctionObject();
|
||||
auto data = s->internalSlot(0).asPointerValue()->asArrayFromAsyncSyncRecord();
|
||||
data->m_promiseCapability.m_promise->asPromiseObject()->reject(state, *argv);
|
||||
return Value();
|
||||
}
|
||||
|
||||
static PromiseObject* arrayFromAsyncSyncAwaitOperation(ExecutionState& state, const Value& awaitValue, ArrayFromAsyncSyncRecord* source, size_t stage)
|
||||
{
|
||||
source->m_awaitResumeStage = stage;
|
||||
PromiseObject* promise = PromiseObject::promiseResolve(state, state.context()->globalObject()->promise(), awaitValue)->asPromiseObject();
|
||||
ExtendedNativeFunctionObject* onFulfilled = new ExtendedNativeFunctionObjectImpl<1>(state, NativeFunctionInfo(AtomicString(), arrayFromAsyncSyncAwaitFulfilledFunction, 1));
|
||||
onFulfilled->setInternalSlot(0, source);
|
||||
ExtendedNativeFunctionObject* onRejected = new ExtendedNativeFunctionObjectImpl<1>(state, NativeFunctionInfo(AtomicString(), arrayFromAsyncSyncAwaitRejectedFunction, 1));
|
||||
onRejected->setInternalSlot(0, source);
|
||||
promise->then(state, onFulfilled, onRejected, Optional<PromiseReaction::Capability>());
|
||||
return promise;
|
||||
}
|
||||
|
||||
|
||||
// Array.fromAsync ( asyncItems [ , mapper [ , thisArg ] ] )
|
||||
static Value builtinArrayFromAsync(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// Let C be the this value.
|
||||
const Value& C = thisValue;
|
||||
Value mapper = argc < 2 ? Value() : argv[1];
|
||||
Value thisArg = argc < 3 ? Value() : argv[2];
|
||||
// If mapper is undefined, then
|
||||
bool mapping = false;
|
||||
if (mapper.isUndefined()) {
|
||||
// Let mapping be false.
|
||||
} else {
|
||||
// Else,
|
||||
// If IsCallable(mapper) is false, throw a TypeError exception.
|
||||
if (!mapper.isCallable()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "argument mapper is not callable nor undefined");
|
||||
}
|
||||
// Let mapping be true.
|
||||
mapping = true;
|
||||
}
|
||||
// Let usingAsyncIterator be ? GetMethod(asyncItems, %Symbol.asyncIterator%).
|
||||
auto usingAsyncIterator = Object::getMethod(state, argv[0], state.context()->vmInstance()->globalSymbols().asyncIterator);
|
||||
// If usingAsyncIterator is undefined, then
|
||||
Value usingSyncIterator;
|
||||
if (usingAsyncIterator.isUndefined()) {
|
||||
// Let usingSyncIterator be ? GetMethod(asyncItems, %Symbol.iterator%).
|
||||
usingSyncIterator = Object::getMethod(state, argv[0], state.context()->vmInstance()->globalSymbols().iterator);
|
||||
}
|
||||
// Let iteratorRecord be undefined.
|
||||
Optional<IteratorRecord*> iteratorRecord;
|
||||
// If usingAsyncIterator is not undefined, then
|
||||
if (!usingAsyncIterator.isUndefined()) {
|
||||
// Set iteratorRecord to ? GetIteratorFromMethod(asyncItems, usingAsyncIterator).
|
||||
iteratorRecord = IteratorObject::getIteratorFromMethod(state, argv[0], usingAsyncIterator);
|
||||
} else if (!usingSyncIterator.isUndefined()) {
|
||||
// Else if usingSyncIterator is not undefined, then
|
||||
// Set iteratorRecord to CreateAsyncFromSyncIterator(? GetIteratorFromMethod(asyncItems, usingSyncIterator)).
|
||||
iteratorRecord = AsyncFromSyncIteratorObject::createAsyncFromSyncIterator(state, IteratorObject::getIteratorFromMethod(state, argv[0], usingSyncIterator));
|
||||
}
|
||||
// If iteratorRecord is not undefined, then
|
||||
if (iteratorRecord) {
|
||||
// If IsConstructor(C) is true, then
|
||||
Value A;
|
||||
if (C.isConstructor()) {
|
||||
// Let A be ? Construct(C).
|
||||
A = Object::construct(state, C, 0, nullptr);
|
||||
} else {
|
||||
// Else,
|
||||
// Let A be ! ArrayCreate(0).
|
||||
A = new ArrayObject(state);
|
||||
}
|
||||
// Let k be 0.
|
||||
auto record = new ArrayFromAsyncRecord(mapping, mapper, thisArg, A, iteratorRecord.value(), PromiseObject::newPromiseCapability(state, state.context()->globalObject()->promise()));
|
||||
arrayFromAsyncAsyncAwaitOperation(state, Value(), record, 0);
|
||||
return record->m_promiseCapability.m_promise;
|
||||
} else {
|
||||
// Let arrayLike be ! ToObject(asyncItems).
|
||||
auto arrayLike = argv[0].toObject(state);
|
||||
// Let len be ? LengthOfArrayLike(arrayLike).
|
||||
int64_t len = static_cast<int64_t>(arrayLike->length(state));
|
||||
Value A;
|
||||
// If IsConstructor(C) is true, then
|
||||
if (C.isConstructor()) {
|
||||
// Let A be ? Construct(C, « 𝔽(len) »).
|
||||
Value v(len);
|
||||
A = Object::construct(state, C, 1, &v);
|
||||
} else {
|
||||
// Else,
|
||||
// Let A be ? ArrayCreate(len).
|
||||
A = new ArrayObject(state, len);
|
||||
}
|
||||
|
||||
auto record = new ArrayFromAsyncSyncRecord(mapping, mapper, thisArg, A, len, arrayLike, PromiseObject::newPromiseCapability(state, state.context()->globalObject()->promise()));
|
||||
arrayFromAsyncSyncAwaitOperation(state, Value(), record, 0);
|
||||
return record->m_promiseCapability.m_promise;
|
||||
}
|
||||
}
|
||||
|
||||
// Array.of ( ...items )
|
||||
static Value builtinArrayOf(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
|
|
@ -767,7 +367,7 @@ static Value builtinArrayJoin(ExecutionState& state, Value thisValue, size_t arg
|
|||
}
|
||||
|
||||
if (!state.context()->toStringRecursionPreventer()->canInvokeToString(thisBinded)) {
|
||||
return String::emptyString();
|
||||
return String::emptyString;
|
||||
}
|
||||
ToStringRecursionPreventerItemAutoHolder holder(state, thisBinded);
|
||||
|
||||
|
|
@ -776,23 +376,26 @@ static Value builtinArrayJoin(ExecutionState& state, Value thisValue, size_t arg
|
|||
int64_t curIndex = 0;
|
||||
while (curIndex < len) {
|
||||
if (curIndex != 0 && sep->length() > 0) {
|
||||
if (static_cast<double>(builder.contentLength()) > static_cast<double>(STRING_MAXIMUM_LENGTH - (curIndex - prevIndex - 1) * (int64_t)sep->length())) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, ErrorObject::Messages::String_InvalidStringLength);
|
||||
}
|
||||
while (curIndex - prevIndex > 1) {
|
||||
builder.appendString(sep, &state);
|
||||
builder.appendString(sep);
|
||||
prevIndex++;
|
||||
}
|
||||
builder.appendString(sep, &state);
|
||||
builder.appendString(sep);
|
||||
}
|
||||
Value elem = thisBinded->getIndexedPropertyValue(state, Value(curIndex), thisBinded);
|
||||
|
||||
if (!elem.isUndefinedOrNull()) {
|
||||
builder.appendString(elem.toString(state), &state);
|
||||
builder.appendString(elem.toString(state));
|
||||
}
|
||||
prevIndex = curIndex;
|
||||
if (elem.isUndefined()) {
|
||||
struct Data {
|
||||
bool exists;
|
||||
Value::ValueIndex cur;
|
||||
Value::ValueIndex ret;
|
||||
int64_t cur;
|
||||
int64_t ret;
|
||||
} data;
|
||||
data.exists = false;
|
||||
data.cur = curIndex;
|
||||
|
|
@ -805,11 +408,12 @@ static Value builtinArrayJoin(ExecutionState& state, Value thisValue, size_t arg
|
|||
break;
|
||||
}
|
||||
ptr.asObject()->enumeration(state, [](ExecutionState& state, Object* self, const ObjectPropertyName& name, const ObjectStructurePropertyDescriptor& desc, void* data) {
|
||||
int64_t index;
|
||||
Data* e = (Data*)data;
|
||||
Value::ValueIndex* ret = &e->ret;
|
||||
int64_t* ret = &e->ret;
|
||||
Value key = name.toPlainValue();
|
||||
Value::ValueIndex index = key.tryToUseAsIndex(state);
|
||||
if (index != Value::InvalidIndexValue) {
|
||||
index = key.toNumber(state);
|
||||
if ((uint64_t)index != Value::InvalidIndexValue) {
|
||||
if (self->get(state, name).value(state, self).isUndefined()) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -817,7 +421,9 @@ static Value builtinArrayJoin(ExecutionState& state, Value thisValue, size_t arg
|
|||
e->ret = std::min(index, e->ret);
|
||||
}
|
||||
}
|
||||
return true; }, &data);
|
||||
return true;
|
||||
},
|
||||
&data);
|
||||
ptr = ptr.asObject()->getPrototype(state);
|
||||
}
|
||||
curIndex = data.ret;
|
||||
|
|
@ -830,7 +436,7 @@ static Value builtinArrayJoin(ExecutionState& state, Value thisValue, size_t arg
|
|||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, ErrorObject::Messages::String_InvalidStringLength);
|
||||
}
|
||||
while (curIndex - prevIndex > 1) {
|
||||
builder.appendString(sep, &state);
|
||||
builder.appendString(sep);
|
||||
prevIndex++;
|
||||
}
|
||||
}
|
||||
|
|
@ -1764,7 +1370,7 @@ static Value builtinArrayToLocaleString(ExecutionState& state, Value thisValue,
|
|||
Object* array = thisValue.toObject(state);
|
||||
|
||||
if (!state.context()->toStringRecursionPreventer()->canInvokeToString(array)) {
|
||||
return String::emptyString();
|
||||
return String::emptyString;
|
||||
}
|
||||
ToStringRecursionPreventerItemAutoHolder holder(state, array);
|
||||
|
||||
|
|
@ -1775,7 +1381,7 @@ static Value builtinArrayToLocaleString(ExecutionState& state, Value thisValue,
|
|||
String* separator = state.context()->staticStrings().asciiTable[(size_t)','].string();
|
||||
|
||||
// Let R be the empty String.
|
||||
String* R = String::emptyString();
|
||||
String* R = String::emptyString;
|
||||
|
||||
// Let k be 0.
|
||||
uint64_t k = 0;
|
||||
|
|
@ -1786,8 +1392,8 @@ static Value builtinArrayToLocaleString(ExecutionState& state, Value thisValue,
|
|||
if (k > 0) {
|
||||
// Set R to the string-concatenation of R and separator.
|
||||
StringBuilder builder;
|
||||
builder.appendString(R, &state);
|
||||
builder.appendString(separator, &state);
|
||||
builder.appendString(R);
|
||||
builder.appendString(separator);
|
||||
R = builder.finalize(&state);
|
||||
}
|
||||
// Let nextElement be ? Get(array, ! ToString(k)).
|
||||
|
|
@ -1796,15 +1402,11 @@ static Value builtinArrayToLocaleString(ExecutionState& state, Value thisValue,
|
|||
if (!nextElement.isUndefinedOrNull()) {
|
||||
// Let S be ? ToString(? Invoke(nextElement, "toLocaleString", « locales, options »)).
|
||||
Value func = nextElement.toObject(state)->get(state, state.context()->staticStrings().toLocaleString).value(state, nextElement);
|
||||
Value callArgv[2] = {
|
||||
argc > 0 ? argv[0] : Value(),
|
||||
argc > 1 ? argv[1] : Value()
|
||||
};
|
||||
String* S = Object::call(state, func, nextElement, 2, callArgv).toString(state);
|
||||
String* S = Object::call(state, func, nextElement, argc, argv).toString(state);
|
||||
// Set R to the string-concatenation of R and S.
|
||||
StringBuilder builder;
|
||||
builder.appendString(R, &state);
|
||||
builder.appendString(S, &state);
|
||||
builder.appendString(R);
|
||||
builder.appendString(S);
|
||||
R = builder.finalize(&state);
|
||||
}
|
||||
// Increase k by 1.
|
||||
|
|
@ -2466,9 +2068,12 @@ static Value builtinArrayFindLastIndex(ExecutionState& state, Value thisValue, s
|
|||
|
||||
void GlobalObject::initializeArray(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->array(); }, nullptr);
|
||||
return self->asGlobalObject()->array();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().Array), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
@ -2499,9 +2104,6 @@ void GlobalObject::installArray(ExecutionState& state)
|
|||
m_array->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().from),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().from, builtinArrayFrom, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_array->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().fromAsync),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().fromAsync, builtinArrayFromAsync, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_array->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().of),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().of, builtinArrayOf, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
|
|
@ -2578,8 +2180,6 @@ void GlobalObject::installArray(ExecutionState& state)
|
|||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().findLast, builtinArrayFindLast, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
m_arrayPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().findLastIndex),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().findLastIndex, builtinArrayFindLastIndex, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
m_arrayPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().fromAsync),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().fromAsync, builtinArrayFromAsync, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
Object* blackList = new Object(state, Object::PrototypeIsNull);
|
||||
blackList->markThisObjectDontNeedStructureTransitionTable();
|
||||
|
|
@ -2596,9 +2196,6 @@ void GlobalObject::installArray(ExecutionState& state)
|
|||
blackList->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().includes), ObjectPropertyDescriptor(Value(true), ObjectPropertyDescriptor::AllPresent));
|
||||
blackList->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().flat), ObjectPropertyDescriptor(Value(true), ObjectPropertyDescriptor::AllPresent));
|
||||
blackList->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().flatMap), ObjectPropertyDescriptor(Value(true), ObjectPropertyDescriptor::AllPresent));
|
||||
blackList->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().toReversed), ObjectPropertyDescriptor(Value(true), ObjectPropertyDescriptor::AllPresent));
|
||||
blackList->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().toSorted), ObjectPropertyDescriptor(Value(true), ObjectPropertyDescriptor::AllPresent));
|
||||
blackList->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().toSpliced), ObjectPropertyDescriptor(Value(true), ObjectPropertyDescriptor::AllPresent));
|
||||
|
||||
FunctionObject* values = new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().values, builtinArrayValues, 0, NativeFunctionInfo::Strict));
|
||||
// Well-Known Intrinsic Objects : %ArrayProto_values%
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ static Value builtinArrayBufferConstructor(ExecutionState& state, Value thisValu
|
|||
|
||||
uint64_t byteLength = argv[0].toIndex(state);
|
||||
if (UNLIKELY(byteLength == Value::InvalidIndexValue)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().ArrayBuffer.string(), false, String::emptyString(), ErrorObject::Messages::GlobalObject_FirstArgumentInvalidLength);
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().ArrayBuffer.string(), false, String::emptyString, ErrorObject::Messages::GlobalObject_FirstArgumentInvalidLength);
|
||||
return Value();
|
||||
}
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ static Value builtinArrayBufferConstructor(ExecutionState& state, Value thisValu
|
|||
if (!maxLengthValue.isUndefined()) {
|
||||
maxByteLength = maxLengthValue.toIndex(state);
|
||||
if (UNLIKELY((maxByteLength.value() == Value::InvalidIndexValue) || (byteLength > maxByteLength.value()))) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().ArrayBuffer.string(), false, String::emptyString(), ErrorObject::Messages::GlobalObject_FirstArgumentInvalidLength);
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().ArrayBuffer.string(), false, String::emptyString, ErrorObject::Messages::GlobalObject_FirstArgumentInvalidLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -113,22 +113,16 @@ static Value builtinArrayBufferResizableGetter(ExecutionState& state, Value this
|
|||
return Value(obj->isResizableArrayBuffer());
|
||||
}
|
||||
|
||||
static Value builtinArrayBufferDetachedGetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
RESOLVE_THIS_BINDING_TO_ARRAYBUFFER(obj, ArrayBuffer, getDetached);
|
||||
|
||||
return Value(obj->isDetachedBuffer());
|
||||
}
|
||||
|
||||
static Value builtinArrayBufferResize(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
RESOLVE_THIS_BINDING_TO_ARRAYBUFFER(obj, ArrayBuffer, resize);
|
||||
obj->throwTypeErrorIfDetached(state);
|
||||
|
||||
if (!obj->isResizableArrayBuffer()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, state.context()->staticStrings().ArrayBuffer.string(), true, state.context()->staticStrings().resize.string(), ErrorObject::Messages::GlobalObject_CalledOnIncompatibleReceiver);
|
||||
}
|
||||
|
||||
double newByteLength = argv[0].toInteger(state);
|
||||
obj->throwTypeErrorIfDetached(state);
|
||||
if (newByteLength < 0 || newByteLength > obj->maxByteLength()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().ArrayBuffer.string(), true, state.context()->staticStrings().resize.string(), ErrorObject::Messages::GlobalObject_FirstArgumentInvalidLength);
|
||||
}
|
||||
|
|
@ -143,53 +137,17 @@ static Value builtinArrayBufferTransfer(ExecutionState& state, Value thisValue,
|
|||
RESOLVE_THIS_BINDING_TO_ARRAYBUFFER(obj, ArrayBuffer, transfer);
|
||||
obj->throwTypeErrorIfDetached(state);
|
||||
|
||||
uint64_t newByteLength = obj->byteLength();
|
||||
double newByteLength = obj->byteLength();
|
||||
if (argc > 0 && !argv[0].isUndefined()) {
|
||||
newByteLength = argv[0].toIndex(state);
|
||||
if (UNLIKELY(newByteLength == Value::InvalidIndexValue)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().ArrayBuffer.string(), true, state.context()->staticStrings().transfer.string(), ErrorObject::Messages::GlobalObject_FirstArgumentInvalidLength);
|
||||
}
|
||||
newByteLength = argv[0].toInteger(state);
|
||||
}
|
||||
|
||||
obj->throwTypeErrorIfDetached(state);
|
||||
Optional<uint64_t> maxLength;
|
||||
if (obj->isResizableArrayBuffer()) {
|
||||
maxLength = obj->maxByteLength();
|
||||
if (newByteLength > maxLength.value()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().ArrayBuffer.string(), true, state.context()->staticStrings().transfer.string(), ErrorObject::Messages::GlobalObject_FirstArgumentInvalidLength);
|
||||
}
|
||||
} else {
|
||||
// For non-resizable ArrayBuffer, the new buffer should also be non-resizable
|
||||
maxLength = newByteLength;
|
||||
}
|
||||
ArrayBuffer* newValue = ArrayBufferObject::allocateArrayBuffer(state, state.context()->globalObject()->arrayBuffer(), newByteLength, maxLength, obj->isResizableArrayBuffer());
|
||||
Value arguments[] = { Value(Value::DoubleToIntConvertibleTestNeeds, newByteLength) };
|
||||
ArrayBuffer* newValue = Object::construct(state, state.context()->globalObject()->arrayBuffer(), 1, arguments).asObject()->asArrayBuffer();
|
||||
|
||||
// Let copyLength be min(newByteLength, O.[[ArrayBufferByteLength]]).
|
||||
// Perform CopyDataBlockBytes(toBlock, 0, fromBlock, 0, copyLength).
|
||||
newValue->fillData(obj->data(), std::min(newByteLength, static_cast<uint64_t>(obj->byteLength())));
|
||||
|
||||
obj->asArrayBufferObject()->detachArrayBuffer();
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
static Value builtinArrayBufferTransferToFixedLength(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
RESOLVE_THIS_BINDING_TO_ARRAYBUFFER(obj, ArrayBuffer, transferToFixedLength);
|
||||
obj->throwTypeErrorIfDetached(state);
|
||||
|
||||
uint64_t newByteLength = obj->byteLength();
|
||||
if (argc > 0 && !argv[0].isUndefined()) {
|
||||
newByteLength = argv[0].toIndex(state);
|
||||
if (UNLIKELY(newByteLength == Value::InvalidIndexValue)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().ArrayBuffer.string(), true, state.context()->staticStrings().transferToFixedLength.string(), ErrorObject::Messages::GlobalObject_FirstArgumentInvalidLength);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayBuffer* newValue = ArrayBufferObject::allocateArrayBuffer(state, state.context()->globalObject()->arrayBuffer(), newByteLength, newByteLength, false);
|
||||
// Let copyLength be min(newByteLength, O.[[ArrayBufferByteLength]]).
|
||||
// Perform CopyDataBlockBytes(toBlock, 0, fromBlock, 0, copyLength).
|
||||
newValue->fillData(obj->data(), std::min(newByteLength, static_cast<uint64_t>(obj->byteLength())));
|
||||
newValue->fillData(obj->data(), std::min(newByteLength, static_cast<double>(obj->byteLength())));
|
||||
|
||||
obj->asArrayBufferObject()->detachArrayBuffer();
|
||||
|
||||
|
|
@ -235,9 +193,12 @@ static Value builtinArrayBufferSlice(ExecutionState& state, Value thisValue, siz
|
|||
|
||||
void GlobalObject::initializeArrayBuffer(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->arrayBuffer(); }, nullptr);
|
||||
return self->asGlobalObject()->arrayBuffer();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().ArrayBuffer), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
@ -283,12 +244,6 @@ void GlobalObject::installArrayBuffer(ExecutionState& state)
|
|||
Value(Value::EmptyValue));
|
||||
ObjectPropertyDescriptor resizableDesc(resizableGS, ObjectPropertyDescriptor::ConfigurablePresent);
|
||||
m_arrayBufferPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->resizable), resizableDesc);
|
||||
|
||||
JSGetterSetter detachedGS(
|
||||
new NativeFunctionObject(state, NativeFunctionInfo(strings->getDetached, builtinArrayBufferDetachedGetter, 0, NativeFunctionInfo::Strict)),
|
||||
Value(Value::EmptyValue));
|
||||
ObjectPropertyDescriptor detachedDesc(detachedGS, ObjectPropertyDescriptor::ConfigurablePresent);
|
||||
m_arrayBufferPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->detached), detachedDesc);
|
||||
}
|
||||
|
||||
m_arrayBufferPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->resize),
|
||||
|
|
@ -297,9 +252,6 @@ void GlobalObject::installArrayBuffer(ExecutionState& state)
|
|||
m_arrayBufferPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->transfer),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->transfer, builtinArrayBufferTransfer, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_arrayBufferPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->transferToFixedLength),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->transferToFixedLength, builtinArrayBufferTransferToFixedLength, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_arrayBufferPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->slice),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->slice, builtinArrayBufferSlice, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace Escargot {
|
|||
static Value builtinAsyncFunction(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
size_t argumentVectorCount = argc > 1 ? argc - 1 : 0;
|
||||
Value sourceValue = argc >= 1 ? argv[argc - 1] : Value(String::emptyString());
|
||||
Value sourceValue = argc >= 1 ? argv[argc - 1] : Value(String::emptyString);
|
||||
auto functionSource = FunctionObject::createDynamicFunctionScript(state, state.context()->staticStrings().anonymous, argumentVectorCount, argv, sourceValue, false, false, true, false);
|
||||
|
||||
// Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto).
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace Escargot {
|
|||
static Value builtinAsyncGeneratorFunction(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
size_t argumentVectorCount = argc > 1 ? argc - 1 : 0;
|
||||
Value sourceValue = argc >= 1 ? argv[argc - 1] : Value(String::emptyString());
|
||||
Value sourceValue = argc >= 1 ? argv[argc - 1] : Value(String::emptyString);
|
||||
auto functionSource = FunctionObject::createDynamicFunctionScript(state, state.context()->staticStrings().anonymous, argumentVectorCount, argv, sourceValue, false, true, true, false);
|
||||
|
||||
// Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto).
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@
|
|||
#include "runtime/Platform.h"
|
||||
#include "runtime/PromiseObject.h"
|
||||
|
||||
#include "util/Yield.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
#if defined(ENABLE_THREADING)
|
||||
|
|
@ -56,7 +54,7 @@ static ArrayBuffer* validateIntegerTypedArray(ExecutionState& state, Value typed
|
|||
if ((TA->typedArrayType() != TypedArrayType::Int32) && (TA->typedArrayType() != TypedArrayType::BigInt64)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::GlobalObject_IllegalFirstArgument);
|
||||
}
|
||||
} else if ((TA->typedArrayType() == TypedArrayType::Uint8Clamped) || (TA->typedArrayType() == TypedArrayType::Float16) || (TA->typedArrayType() == TypedArrayType::Float32) || (TA->typedArrayType() == TypedArrayType::Float64)) {
|
||||
} else if ((TA->typedArrayType() == TypedArrayType::Uint8Clamped) || (TA->typedArrayType() == TypedArrayType::Float32) || (TA->typedArrayType() == TypedArrayType::Float64)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::GlobalObject_IllegalFirstArgument);
|
||||
}
|
||||
|
||||
|
|
@ -65,8 +63,8 @@ static ArrayBuffer* validateIntegerTypedArray(ExecutionState& state, Value typed
|
|||
|
||||
static size_t validateAtomicAccess(ExecutionState& state, TypedArrayObject* typedArray, Value index)
|
||||
{
|
||||
size_t length = typedArray->arrayLength();
|
||||
uint64_t accessIndex = index.toIndex(state);
|
||||
size_t length = typedArray->arrayLength();
|
||||
if (UNLIKELY(accessIndex == Value::InvalidIndexValue || accessIndex >= (uint64_t)length)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, ErrorObject::Messages::GlobalObject_RangeError);
|
||||
}
|
||||
|
|
@ -579,30 +577,14 @@ static Value builtinAtomicsIsLockFree(ExecutionState& state, Value thisValue, si
|
|||
#endif
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-atomics-microwait/#Atomics.pause
|
||||
static Value builtinAtomicsPause(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// 1. If N is neither undefined nor an integral Number, throw a TypeError exception.
|
||||
Value N = argc ? argv[0] : Value();
|
||||
if (!N.isUndefined() && (!N.isNumber() || !isIntegralNumber(N.asNumber()))) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::GlobalObject_IllegalFirstArgument);
|
||||
}
|
||||
// 2. If the execution environment of the ECMAScript implementation supports signaling to the operating system or CPU that the current executing code is in a spin-wait loop,
|
||||
// such as executing a pause CPU instruction, send that signal. When N is not undefined, it determines the number of times that signal is sent.
|
||||
// The number of times the signal is sent for an integral Number N is less than or equal to the number times it is sent for N + 1 if both N and N + 1 have the same sign.
|
||||
|
||||
// TODO use number input
|
||||
YIELD_PROCESSOR;
|
||||
|
||||
// 3. Return undefined.
|
||||
return Value();
|
||||
}
|
||||
|
||||
void GlobalObject::initializeAtomics(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->atomics(); }, nullptr);
|
||||
return self->asGlobalObject()->atomics();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().Atomics), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
@ -654,9 +636,6 @@ void GlobalObject::installAtomics(ExecutionState& state)
|
|||
m_atomics->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().notify),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().notify, builtinAtomicsNotify, 3, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_atomics->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().pause),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().pause, builtinAtomicsPause, 0, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
redefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().Atomics),
|
||||
ObjectPropertyDescriptor(m_atomics, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ static Value builtinBigIntConstructor(ExecutionState& state, Value thisValue, si
|
|||
return new BigInt((int64_t)numValue);
|
||||
} else {
|
||||
// Otherwise, return ? ToBigInt(value).
|
||||
return prim.toBigInt(state);
|
||||
return argv[0].toBigInt(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -188,9 +188,12 @@ static Value builtinBigIntToLocaleString(ExecutionState& state, Value thisValue,
|
|||
|
||||
void GlobalObject::initializeBigInt(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->bigInt(); }, nullptr);
|
||||
return self->asGlobalObject()->bigInt();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().BigInt), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,9 +63,12 @@ static Value builtinBooleanToString(ExecutionState& state, Value thisValue, size
|
|||
|
||||
void GlobalObject::initializeBoolean(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->boolean(); }, nullptr);
|
||||
return self->asGlobalObject()->boolean();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().Boolean), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@
|
|||
namespace Escargot {
|
||||
|
||||
#define FOR_EACH_DATAVIEW_TYPES(F) \
|
||||
F(Float16) \
|
||||
F(Float32) \
|
||||
F(Float64) \
|
||||
F(Int8) \
|
||||
|
|
@ -41,90 +40,54 @@ namespace Escargot {
|
|||
|
||||
static Value builtinDataViewConstructor(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// If NewTarget is undefined, throw a TypeError exception.
|
||||
if (!newTarget.hasValue()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::GlobalObject_ConstructorRequiresNew);
|
||||
return Value();
|
||||
}
|
||||
// Perform ? RequireInternalSlot(buffer, [[ArrayBufferData]]).
|
||||
if (!(argv[0].isObject() && argv[0].asPointerValue()->isArrayBuffer())) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, state.context()->staticStrings().DataView.string(), false, String::emptyString(), ErrorObject::Messages::GlobalObject_ThisNotArrayBufferObject);
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, state.context()->staticStrings().DataView.string(), false, String::emptyString, ErrorObject::Messages::GlobalObject_ThisNotArrayBufferObject);
|
||||
}
|
||||
|
||||
ArrayBuffer* buffer = argv[0].asObject()->asArrayBuffer();
|
||||
// Let offset be ? ToIndex(byteOffset).
|
||||
double offset = 0;
|
||||
double byteOffset = 0;
|
||||
if (argc >= 2) {
|
||||
offset = argv[1].toIndex(state);
|
||||
if (offset == Value::InvalidIndexValue) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().DataView.string(), false, String::emptyString(), ErrorObject::Messages::GlobalObject_InvalidArrayBufferOffset);
|
||||
Value& val = argv[1];
|
||||
byteOffset = val.toIndex(state);
|
||||
if (byteOffset == Value::InvalidIndexValue) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().DataView.string(), false, String::emptyString, ErrorObject::Messages::GlobalObject_InvalidArrayBufferOffset);
|
||||
}
|
||||
}
|
||||
// If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
||||
if (buffer->isDetachedBuffer()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, state.context()->staticStrings().DataView.string(), false, String::emptyString(), "%s: ArrayBuffer is detached buffer");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, state.context()->staticStrings().DataView.string(), false, String::emptyString, "%s: ArrayBuffer is detached buffer");
|
||||
}
|
||||
// Let bufferByteLength be ArrayBufferByteLength(buffer, seq-cst).
|
||||
auto bufferByteLegnth = buffer->byteLength();
|
||||
// If offset > bufferByteLength, throw a RangeError exception.
|
||||
if (offset > bufferByteLegnth) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().DataView.string(), false, String::emptyString(), ErrorObject::Messages::GlobalObject_InvalidArrayBufferOffset);
|
||||
double bufferByteLength = buffer->byteLength();
|
||||
|
||||
if (byteOffset > bufferByteLength) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().DataView.string(), false, String::emptyString, ErrorObject::Messages::GlobalObject_InvalidArrayBufferOffset);
|
||||
}
|
||||
// Let bufferIsFixedLength be IsFixedLengthArrayBuffer(buffer).
|
||||
bool bufferIsFixedLength = buffer->isFixedLengthArrayBuffer();
|
||||
// If byteLength is undefined, then
|
||||
Optional<Value::ValueIndex> viewByteLength;
|
||||
if (argc < 3 || argv[2].isUndefined()) {
|
||||
// If bufferIsFixedLength is true, then
|
||||
if (bufferIsFixedLength) {
|
||||
// Let viewByteLength be bufferByteLength - offset.
|
||||
viewByteLength = bufferByteLegnth - offset;
|
||||
} else {
|
||||
// Else
|
||||
// Let viewByteLength be auto.
|
||||
}
|
||||
} else {
|
||||
// Else,
|
||||
// Let viewByteLength be ? ToIndex(byteLength).
|
||||
viewByteLength = argv[2].toIndex(state);
|
||||
if (viewByteLength.value() == Value::InvalidIndexValue) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().DataView.string(), false, String::emptyString(), ErrorObject::Messages::GlobalObject_InvalidArrayBufferSize);
|
||||
}
|
||||
// If offset + viewByteLength > bufferByteLength, throw a RangeError exception.
|
||||
if (offset + viewByteLength.value() > bufferByteLegnth) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().DataView.string(), false, String::emptyString(), ErrorObject::Messages::GlobalObject_InvalidArrayBufferSize);
|
||||
double byteLength = bufferByteLength - byteOffset;
|
||||
|
||||
if (argc >= 3) {
|
||||
Value& val = argv[2];
|
||||
if (!val.isUndefined()) {
|
||||
byteLength = val.toIndex(state);
|
||||
if (byteOffset + byteLength > bufferByteLength || byteLength == Value::InvalidIndexValue) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().DataView.string(), false, String::emptyString, ErrorObject::Messages::GlobalObject_InvalidArrayBufferOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%DataView.prototype%", « [[DataView]], [[ViewedArrayBuffer]], [[ByteLength]], [[ByteOffset]] »).
|
||||
Object* proto = Object::getPrototypeFromConstructor(state, newTarget.value(), [](ExecutionState& state, Context* constructorRealm) -> Object* {
|
||||
return constructorRealm->globalObject()->dataViewPrototype();
|
||||
});
|
||||
ArrayBufferView* O = new DataViewObject(state, proto);
|
||||
ArrayBufferView* obj = new DataViewObject(state, proto);
|
||||
obj->setBuffer(buffer, byteOffset, byteLength);
|
||||
|
||||
// If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
||||
if (buffer->isDetachedBuffer()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, state.context()->staticStrings().DataView.string(), false, String::emptyString(), "%s: ArrayBuffer is detached buffer");
|
||||
if (obj->buffer()->isDetachedBuffer()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, state.context()->staticStrings().DataView.string(), false, String::emptyString, ErrorObject::Messages::GlobalObject_DetachedBuffer);
|
||||
}
|
||||
|
||||
// Set bufferByteLength to ArrayBufferByteLength(buffer, seq-cst).
|
||||
bufferByteLegnth = buffer->byteLength();
|
||||
// If offset > bufferByteLength, throw a RangeError exception.
|
||||
if (offset > bufferByteLegnth) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().DataView.string(), false, String::emptyString(), ErrorObject::Messages::GlobalObject_InvalidArrayBufferSize);
|
||||
}
|
||||
// If byteLength is not undefined, then
|
||||
if (argc >= 3 && !argv[2].isUndefined()) {
|
||||
// If offset + viewByteLength > bufferByteLength, throw a RangeError exception.
|
||||
if (offset + viewByteLength.value() > bufferByteLegnth) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().DataView.string(), false, String::emptyString(), ErrorObject::Messages::GlobalObject_InvalidArrayBufferSize);
|
||||
}
|
||||
}
|
||||
// Set O.[[ViewedArrayBuffer]] to buffer.
|
||||
// Set O.[[ByteLength]] to viewByteLength.
|
||||
// Set O.[[ByteOffset]] to offset.
|
||||
// Return O.
|
||||
O->setBuffer(buffer, offset, viewByteLength ? viewByteLength.value() : bufferByteLegnth - offset, 0, argc < 3);
|
||||
return O;
|
||||
return obj;
|
||||
}
|
||||
|
||||
#define DECLARE_DATAVIEW_GETTER(Name) \
|
||||
|
|
@ -153,11 +116,10 @@ static Value builtinDataViewConstructor(ExecutionState& state, Value thisValue,
|
|||
ErrorObject::Messages::GlobalObject_ThisNotDataViewObject); \
|
||||
} \
|
||||
if (argc < 3) { \
|
||||
thisObject->asDataViewObject()->setViewValue(state, argv[0], Value(false), TypedArrayType::Name, argv[1]); \
|
||||
return thisObject->asDataViewObject()->setViewValue(state, argv[0], Value(false), TypedArrayType::Name, argv[1]); \
|
||||
} else { \
|
||||
thisObject->asDataViewObject()->setViewValue(state, argv[0], argv[2], TypedArrayType::Name, argv[1]); \
|
||||
return thisObject->asDataViewObject()->setViewValue(state, argv[0], argv[2], TypedArrayType::Name, argv[1]); \
|
||||
} \
|
||||
return Value(); \
|
||||
}
|
||||
|
||||
FOR_EACH_DATAVIEW_TYPES(DECLARE_DATAVIEW_GETTER);
|
||||
|
|
@ -177,8 +139,7 @@ static Value builtinDataViewBufferGetter(ExecutionState& state, Value thisValue,
|
|||
|
||||
static Value builtinDataViewByteLengthGetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
if (LIKELY(thisValue.isPointerValue() && thisValue.asPointerValue()->isDataViewObject() && thisValue.asObject()->asDataViewObject()->buffer())) {
|
||||
thisValue.asObject()->asDataViewObject()->throwTypeErrorIfDetached(state);
|
||||
if (LIKELY(thisValue.isPointerValue() && thisValue.asPointerValue()->isDataViewObject() && thisValue.asObject()->asDataViewObject()->buffer() && !thisValue.asObject()->asDataViewObject()->buffer()->isDetachedBuffer())) {
|
||||
return Value(thisValue.asObject()->asArrayBufferView()->byteLength());
|
||||
}
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "get DataView.prototype.byteLength called on incompatible receiver");
|
||||
|
|
@ -187,8 +148,7 @@ static Value builtinDataViewByteLengthGetter(ExecutionState& state, Value thisVa
|
|||
|
||||
static Value builtinDataViewByteOffsetGetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
if (LIKELY(thisValue.isPointerValue() && thisValue.asPointerValue()->isDataViewObject() && thisValue.asObject()->asDataViewObject()->buffer())) {
|
||||
thisValue.asObject()->asDataViewObject()->throwTypeErrorIfDetached(state);
|
||||
if (LIKELY(thisValue.isPointerValue() && thisValue.asPointerValue()->isDataViewObject() && thisValue.asObject()->asDataViewObject()->buffer() && !thisValue.asObject()->asDataViewObject()->buffer()->isDetachedBuffer())) {
|
||||
return Value(thisValue.asObject()->asArrayBufferView()->byteOffset());
|
||||
}
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "get DataView.prototype.byteOffset called on incompatible receiver");
|
||||
|
|
@ -197,9 +157,12 @@ static Value builtinDataViewByteOffsetGetter(ExecutionState& state, Value thisVa
|
|||
|
||||
void GlobalObject::initializeDataView(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->dataView(); }, nullptr);
|
||||
return self->asGlobalObject()->dataView();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().DataView), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,10 +29,6 @@
|
|||
#include "intl/IntlDateTimeFormat.h"
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_TEMPORAL)
|
||||
#include "runtime/TemporalInstantObject.h"
|
||||
#endif
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
#define FOR_EACH_DATE_VALUES(F) \
|
||||
|
|
@ -102,7 +98,7 @@ static Value builtinDateConstructor(ExecutionState& state, Value thisValue, size
|
|||
} else {
|
||||
// Let tv be ToNumber(v).
|
||||
double V = v.toNumber(state);
|
||||
thisObject->setTimeValue(DateObject::timeClipToTime64(state, V));
|
||||
thisObject->setTimeValue(DateObject::timeClip(state, V));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -220,23 +216,21 @@ static Value builtinDateToTimeString(ExecutionState& state, Value thisValue, siz
|
|||
}
|
||||
|
||||
#if defined(ENABLE_ICU) && defined(ENABLE_INTL)
|
||||
#define INTL_DATE_TIME_FORMAT_FORMAT(REQUIRED, DEFUALT) \
|
||||
double x = thisObject->primitiveValue(); \
|
||||
if (std::isnan(x)) { \
|
||||
return new ASCIIStringFromExternalMemory("Invalid Date"); \
|
||||
} \
|
||||
Value locales, options; \
|
||||
if (argc >= 1) { \
|
||||
locales = argv[0]; \
|
||||
} \
|
||||
if (argc >= 2) { \
|
||||
options = argv[1]; \
|
||||
} \
|
||||
auto dateTimeOption = IntlDateTimeFormatObject:: \
|
||||
toDateTimeOptions(state, options, String::fromASCII(REQUIRED), String::fromASCII(DEFUALT)) \
|
||||
.first; \
|
||||
IntlDateTimeFormatObject* dateFormat = new IntlDateTimeFormatObject(state, locales, dateTimeOption); \
|
||||
auto result = dateFormat->format(state, x); \
|
||||
#define INTL_DATE_TIME_FORMAT_FORMAT(REQUIRED, DEFUALT) \
|
||||
double x = thisObject->primitiveValue(); \
|
||||
if (std::isnan(x)) { \
|
||||
return new ASCIIString("Invalid Date"); \
|
||||
} \
|
||||
Value locales, options; \
|
||||
if (argc >= 1) { \
|
||||
locales = argv[0]; \
|
||||
} \
|
||||
if (argc >= 2) { \
|
||||
options = argv[1]; \
|
||||
} \
|
||||
auto dateTimeOption = IntlDateTimeFormatObject::toDateTimeOptions(state, options, String::fromASCII(REQUIRED), String::fromASCII(DEFUALT)); \
|
||||
IntlDateTimeFormatObject* dateFormat = new IntlDateTimeFormatObject(state, locales, dateTimeOption); \
|
||||
auto result = dateFormat->format(state, x); \
|
||||
return new UTF16String(result.data(), result.length());
|
||||
#endif
|
||||
|
||||
|
|
@ -318,19 +312,13 @@ static Value builtinDateSetHelper(ExecutionState& state, DateSetterType setterTy
|
|||
RESOLVE_THIS_BINDING_TO_DATE(thisObject, Date, name);
|
||||
DateObject* d = thisObject;
|
||||
|
||||
// Read the current [[DateValue]] first (before any ToNumber conversions)
|
||||
// To keep a record of the original state
|
||||
double originalDateValue = d->primitiveValue();
|
||||
bool isOriginalDateValid = d->isValid();
|
||||
|
||||
if (setterType == DateSetterType::Day && length == 3) {
|
||||
// setFullYear, setUTCFullYear case
|
||||
if (!isOriginalDateValid) {
|
||||
d->setTimeValue(DateObject::timeClipToTime64(state, 0));
|
||||
d->setTimeValue(d->getTimezoneOffset(state) * TimeConstant::MsPerMinute);
|
||||
originalDateValue = d->primitiveValue();
|
||||
isOriginalDateValid = true;
|
||||
if (!(d->isValid())) {
|
||||
d->setTimeValue(DateObject::timeClip(state, 0));
|
||||
d->setTimeValue(d->getTimezoneOffset(state) * const_Date_msPerMinute);
|
||||
}
|
||||
ASSERT(d->isValid());
|
||||
}
|
||||
|
||||
if (argc < 1) {
|
||||
|
|
@ -338,10 +326,9 @@ static Value builtinDateSetHelper(ExecutionState& state, DateSetterType setterTy
|
|||
return Value(Value::NanInit);
|
||||
}
|
||||
|
||||
// Read date components from original state (before ToNumber calls)
|
||||
double year = 0, month = 0, date = 0, hour = 0, minute = 0, second = 0, millisecond = 0;
|
||||
|
||||
if (isOriginalDateValid) {
|
||||
if (d->isValid()) {
|
||||
if (!utc) {
|
||||
year = d->getFullYear(state);
|
||||
month = d->getMonth(state);
|
||||
|
|
@ -363,7 +350,8 @@ static Value builtinDateSetHelper(ExecutionState& state, DateSetterType setterTy
|
|||
}
|
||||
}
|
||||
|
||||
// Convert arguments to numbers (this may cause side effects)
|
||||
bool convertToUTC = !utc;
|
||||
|
||||
switch (setterType) {
|
||||
case DateSetterType::Day:
|
||||
if ((length >= 3) && (argc > length - 3))
|
||||
|
|
@ -387,16 +375,9 @@ static Value builtinDateSetHelper(ExecutionState& state, DateSetterType setterTy
|
|||
RELEASE_ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
// Check if original date value was NaN
|
||||
if (std::isnan(originalDateValue)) {
|
||||
return Value(Value::NanInit);
|
||||
}
|
||||
|
||||
bool convertToUTC = !utc;
|
||||
|
||||
if (UNLIKELY(!isInValidRange(year, month, date, hour, minute, second, millisecond))) {
|
||||
d->setTimeValueAsNaN();
|
||||
} else {
|
||||
} else if (d->isValid()) {
|
||||
d->setTimeValue(state, year, month, date, hour, minute, second, millisecond, convertToUTC);
|
||||
}
|
||||
|
||||
|
|
@ -415,7 +396,7 @@ static Value builtinDateSetTime(ExecutionState& state, Value thisValue, size_t a
|
|||
{
|
||||
RESOLVE_THIS_BINDING_TO_DATE(thisObject, Date, setTime);
|
||||
if (argc > 0) {
|
||||
thisObject->setTimeValue(DateObject::timeClipToTime64(state, argv[0].toNumber(state)));
|
||||
thisObject->setTimeValue(DateObject::timeClip(state, argv[0].toNumber(state)));
|
||||
return Value(Value::DoubleToIntConvertibleTestNeeds, thisObject->primitiveValue());
|
||||
} else {
|
||||
thisObject->setTimeValueAsNaN();
|
||||
|
|
@ -439,8 +420,8 @@ static Value builtinDateSetYear(ExecutionState& state, Value thisValue, size_t a
|
|||
DateObject* d = thisObject;
|
||||
|
||||
if (!(d->isValid())) {
|
||||
d->setTimeValue(DateObject::timeClipToTime64(state, 0));
|
||||
d->setTimeValue(d->getTimezoneOffset(state) * TimeConstant::MsPerMinute);
|
||||
d->setTimeValue(DateObject::timeClip(state, 0));
|
||||
d->setTimeValue(d->getTimezoneOffset(state) * const_Date_msPerMinute);
|
||||
}
|
||||
ASSERT(d->isValid());
|
||||
|
||||
|
|
@ -451,12 +432,6 @@ static Value builtinDateSetYear(ExecutionState& state, Value thisValue, size_t a
|
|||
|
||||
double y;
|
||||
int month, date, hour, minute, second, millisecond;
|
||||
month = d->getMonth(state);
|
||||
date = d->getDate(state);
|
||||
hour = d->getHours(state);
|
||||
minute = d->getMinutes(state);
|
||||
second = d->getSeconds(state);
|
||||
millisecond = d->getMilliseconds(state);
|
||||
|
||||
// Let y be ToNumber(year).
|
||||
y = argv[0].toNumber(state);
|
||||
|
|
@ -466,6 +441,13 @@ static Value builtinDateSetYear(ExecutionState& state, Value thisValue, size_t a
|
|||
return Value(Value::NanInit);
|
||||
}
|
||||
|
||||
month = d->getMonth(state);
|
||||
date = d->getDate(state);
|
||||
hour = d->getHours(state);
|
||||
minute = d->getMinutes(state);
|
||||
second = d->getSeconds(state);
|
||||
millisecond = d->getMilliseconds(state);
|
||||
|
||||
double yyyy;
|
||||
double yAsInteger = Value(Value::DoubleToIntConvertibleTestNeeds, y).toInteger(state);
|
||||
// If y is not NaN and 0 ≤ ToInteger(y) ≤ 99, let yyyy be ToInteger(y) + 1900.
|
||||
|
|
@ -476,7 +458,9 @@ static Value builtinDateSetYear(ExecutionState& state, Value thisValue, size_t a
|
|||
yyyy = y;
|
||||
}
|
||||
|
||||
d->setTimeValue(state, yyyy, month, date, hour, minute, second, millisecond);
|
||||
if (d->isValid()) {
|
||||
d->setTimeValue(state, yyyy, month, date, hour, minute, second, millisecond);
|
||||
}
|
||||
|
||||
return Value(Value::DoubleToIntConvertibleTestNeeds, d->primitiveValue());
|
||||
}
|
||||
|
|
@ -520,19 +504,14 @@ static Value builtinDateToPrimitive(ExecutionState& state, Value thisValue, size
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(ENABLE_TEMPORAL)
|
||||
static Value builtinDateToTemporalInstant(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
RESOLVE_THIS_BINDING_TO_DATE(thisObject, Date, lazyToTemporalInstant());
|
||||
return thisObject->toTemporalInstant(state);
|
||||
}
|
||||
#endif
|
||||
|
||||
void GlobalObject::initializeDate(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->date(); }, nullptr);
|
||||
return self->asGlobalObject()->date();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().Date), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
@ -623,12 +602,6 @@ void GlobalObject::installDate(ExecutionState& state)
|
|||
|
||||
FOR_EACH_DATE_VALUES(DATE_DEFINE_SETTER);
|
||||
|
||||
#if defined(ENABLE_TEMPORAL)
|
||||
m_datePrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().lazyToTemporalInstant()),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().lazyToTemporalInstant(), builtinDateToTemporalInstant, 0, NativeFunctionInfo::Strict)),
|
||||
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
#endif
|
||||
|
||||
m_date->setFunctionPrototype(state, m_datePrototype);
|
||||
|
||||
redefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().Date),
|
||||
|
|
|
|||
|
|
@ -54,9 +54,9 @@ static Value builtinErrorConstructor(ExecutionState& state, Value thisValue, siz
|
|||
});
|
||||
|
||||
#if defined(ENABLE_EXTENDED_API)
|
||||
ErrorObject* obj = new ErrorObject(state, proto, String::emptyString(), true, state.context()->vmInstance()->isErrorCreationCallbackRegistered());
|
||||
ErrorObject* obj = new ErrorObject(state, proto, String::emptyString, true, state.context()->vmInstance()->isErrorCreationCallbackRegistered());
|
||||
#else
|
||||
ErrorObject* obj = new ErrorObject(state, proto, String::emptyString());
|
||||
ErrorObject* obj = new ErrorObject(state, proto, String::emptyString);
|
||||
#endif
|
||||
|
||||
Value message = argv[0];
|
||||
|
|
@ -81,7 +81,7 @@ static Value builtinErrorConstructor(ExecutionState& state, Value thisValue, siz
|
|||
Object* proto = Object::getPrototypeFromConstructor(state, newTarget.value(), [](ExecutionState& state, Context* constructorRealm) -> Object* { \
|
||||
return constructorRealm->globalObject()->lowerCaseErrorName##ErrorPrototype(); \
|
||||
}); \
|
||||
ErrorObject* obj = new errorName##ErrorObject(state, proto, String::emptyString(), true, state.context()->vmInstance()->isErrorCreationCallbackRegistered()); \
|
||||
ErrorObject* obj = new errorName##ErrorObject(state, proto, String::emptyString, true, state.context()->vmInstance()->isErrorCreationCallbackRegistered()); \
|
||||
Value message = argv[0]; \
|
||||
if (!message.isUndefined()) { \
|
||||
obj->defineOwnPropertyThrowsException(state, state.context()->staticStrings().message, \
|
||||
|
|
@ -101,7 +101,7 @@ static Value builtinErrorConstructor(ExecutionState& state, Value thisValue, siz
|
|||
Object* proto = Object::getPrototypeFromConstructor(state, newTarget.value(), [](ExecutionState& state, Context* constructorRealm) -> Object* { \
|
||||
return constructorRealm->globalObject()->lowerCaseErrorName##ErrorPrototype(); \
|
||||
}); \
|
||||
ErrorObject* obj = new errorName##ErrorObject(state, proto, String::emptyString()); \
|
||||
ErrorObject* obj = new errorName##ErrorObject(state, proto, String::emptyString); \
|
||||
Value message = argv[0]; \
|
||||
if (!message.isUndefined()) { \
|
||||
obj->defineOwnPropertyThrowsException(state, state.context()->staticStrings().message, \
|
||||
|
|
@ -132,9 +132,9 @@ static Value builtinAggregateErrorConstructor(ExecutionState& state, Value thisV
|
|||
});
|
||||
|
||||
#if defined(ENABLE_EXTENDED_API)
|
||||
ErrorObject* O = new AggregateErrorObject(state, proto, String::emptyString(), true, state.context()->vmInstance()->isErrorCreationCallbackRegistered());
|
||||
ErrorObject* O = new AggregateErrorObject(state, proto, String::emptyString, true, state.context()->vmInstance()->isErrorCreationCallbackRegistered());
|
||||
#else
|
||||
ErrorObject* O = new AggregateErrorObject(state, proto, String::emptyString());
|
||||
ErrorObject* O = new AggregateErrorObject(state, proto, String::emptyString);
|
||||
#endif
|
||||
|
||||
Value message = argv[1];
|
||||
|
|
@ -161,34 +161,6 @@ static Value builtinAggregateErrorConstructor(ExecutionState& state, Value thisV
|
|||
return O;
|
||||
}
|
||||
|
||||
static Value builtinSuppressedErrorConstructor(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
if (!newTarget.hasValue()) {
|
||||
newTarget = state.resolveCallee();
|
||||
}
|
||||
Object* proto = Object::getPrototypeFromConstructor(state, newTarget.value(), [](ExecutionState& state, Context* constructorRealm) -> Object* {
|
||||
return constructorRealm->globalObject()->suppressedErrorPrototype();
|
||||
});
|
||||
|
||||
String* message = String::emptyString();
|
||||
if (!argv[2].isUndefined()) {
|
||||
message = argv[2].toString(state);
|
||||
}
|
||||
|
||||
#if defined(ENABLE_EXTENDED_API)
|
||||
ErrorObject* O = new SuppressedErrorObject(state, proto, message, true, state.context()->vmInstance()->isErrorCreationCallbackRegistered(), argv[0], argv[1]);
|
||||
#else
|
||||
ErrorObject* O = new SuppressedErrorObject(state, proto, message, true, false, argv[0], argv[1]);
|
||||
#endif
|
||||
|
||||
// test/built-ins/NativeErrors/SuppressedError/message-undefined-no-prop.js
|
||||
if (argv[2].isUndefined()) {
|
||||
O->deleteOwnProperty(state, state.context()->staticStrings().message);
|
||||
}
|
||||
|
||||
return O;
|
||||
}
|
||||
|
||||
static Value builtinErrorThrowTypeError(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "");
|
||||
|
|
@ -203,7 +175,7 @@ static Value builtinErrorToString(ExecutionState& state, Value thisValue, size_t
|
|||
Object* o = thisValue.toObject(state);
|
||||
|
||||
if (!state.context()->toStringRecursionPreventer()->canInvokeToString(o)) {
|
||||
return String::emptyString();
|
||||
return String::emptyString;
|
||||
}
|
||||
ToStringRecursionPreventerItemAutoHolder holder(state, o);
|
||||
|
||||
|
|
@ -217,7 +189,7 @@ static Value builtinErrorToString(ExecutionState& state, Value thisValue, size_t
|
|||
Value message = o->get(state, state.context()->staticStrings().message).value(state, o);
|
||||
String* messageStr;
|
||||
if (message.isUndefined()) {
|
||||
messageStr = String::emptyString();
|
||||
messageStr = String::emptyString;
|
||||
} else {
|
||||
messageStr = message.toString(state);
|
||||
}
|
||||
|
|
@ -231,32 +203,23 @@ static Value builtinErrorToString(ExecutionState& state, Value thisValue, size_t
|
|||
}
|
||||
|
||||
StringBuilder builder;
|
||||
builder.appendString(nameStr, &state);
|
||||
builder.appendString(": ", &state);
|
||||
builder.appendString(messageStr, &state);
|
||||
builder.appendString(nameStr);
|
||||
builder.appendString(": ");
|
||||
builder.appendString(messageStr);
|
||||
return builder.finalize(&state);
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-error.iserror
|
||||
static Value builtinErrorIsError(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// 1. If arg is not an Object, return false.
|
||||
// 2. If arg does not have an [[ErrorData]] internal slot, return false.
|
||||
if (!argv[0].isObject() || !argv[0].asObject()->isErrorObject()) {
|
||||
return Value(false);
|
||||
}
|
||||
// 3. Return true.
|
||||
return Value(true);
|
||||
}
|
||||
|
||||
void GlobalObject::initializeError(ExecutionState& state)
|
||||
{
|
||||
#define DEFINE_ERROR_INIT(errorname, bname) \
|
||||
{ \
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value { \
|
||||
#define DEFINE_ERROR_INIT(errorname, bname) \
|
||||
{ \
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, \
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value { \
|
||||
ASSERT(self->isGlobalObject()); \
|
||||
return self->asGlobalObject()->errorname##Error(); }, nullptr); \
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().bname##Error), nativeData, Value(Value::EmptyValue)); \
|
||||
return self->asGlobalObject()->errorname##Error(); \
|
||||
}, \
|
||||
nullptr); \
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().bname##Error), nativeData, Value(Value::EmptyValue)); \
|
||||
}
|
||||
|
||||
DEFINE_ERROR_INIT(reference, Reference);
|
||||
|
|
@ -266,12 +229,14 @@ void GlobalObject::initializeError(ExecutionState& state)
|
|||
DEFINE_ERROR_INIT(uri, URI);
|
||||
DEFINE_ERROR_INIT(eval, Eval);
|
||||
DEFINE_ERROR_INIT(aggregate, Aggregate);
|
||||
DEFINE_ERROR_INIT(suppressed, Suppressed);
|
||||
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->error(); }, nullptr);
|
||||
return self->asGlobalObject()->error();
|
||||
},
|
||||
nullptr);
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().Error), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
}
|
||||
|
|
@ -281,10 +246,6 @@ void GlobalObject::installError(ExecutionState& state)
|
|||
m_error = new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().Error, builtinErrorConstructor, 1), NativeFunctionObject::__ForBuiltinConstructor__);
|
||||
m_error->setGlobalIntrinsicObject(state);
|
||||
|
||||
m_error->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().isError),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().isError, builtinErrorIsError, 1, NativeFunctionInfo::Strict)),
|
||||
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_errorPrototype = new PrototypeObject(state);
|
||||
m_errorPrototype->setGlobalIntrinsicObject(state, true);
|
||||
|
||||
|
|
@ -292,7 +253,7 @@ void GlobalObject::installError(ExecutionState& state)
|
|||
|
||||
m_errorPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().constructor), ObjectPropertyDescriptor(m_error, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_errorPrototype->directDefineOwnProperty(state, state.context()->staticStrings().message, ObjectPropertyDescriptor(String::emptyString(), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
m_errorPrototype->directDefineOwnProperty(state, state.context()->staticStrings().message, ObjectPropertyDescriptor(String::emptyString, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
m_errorPrototype->directDefineOwnProperty(state, state.context()->staticStrings().name, ObjectPropertyDescriptor(state.context()->staticStrings().Error.string(), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
auto errorToStringFn = new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().toString, builtinErrorToString, 0, NativeFunctionInfo::Strict));
|
||||
m_errorPrototype->directDefineOwnProperty(state, state.context()->staticStrings().toString, ObjectPropertyDescriptor(errorToStringFn, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
|
@ -305,7 +266,7 @@ void GlobalObject::installError(ExecutionState& state)
|
|||
m_throwTypeError->defineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().length),
|
||||
ObjectPropertyDescriptor(Value(0), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::NonWritablePresent | ObjectPropertyDescriptor::NonEnumerablePresent | ObjectPropertyDescriptor::NonConfigurablePresent)));
|
||||
m_throwTypeError->defineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().name),
|
||||
ObjectPropertyDescriptor(String::emptyString(), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::NonWritablePresent | ObjectPropertyDescriptor::NonEnumerablePresent | ObjectPropertyDescriptor::NonConfigurablePresent)));
|
||||
ObjectPropertyDescriptor(String::emptyString, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::NonWritablePresent | ObjectPropertyDescriptor::NonEnumerablePresent | ObjectPropertyDescriptor::NonConfigurablePresent)));
|
||||
m_throwTypeError->preventExtensions(state);
|
||||
|
||||
m_throwerGetterSetterData = new JSGetterSetter(m_throwTypeError, m_throwTypeError);
|
||||
|
|
@ -316,7 +277,7 @@ void GlobalObject::installError(ExecutionState& state)
|
|||
m_##errorname##ErrorPrototype = new PrototypeObject(state, m_errorPrototype); \
|
||||
m_##errorname##ErrorPrototype->setGlobalIntrinsicObject(state, true); \
|
||||
m_##errorname##ErrorPrototype->directDefineOwnProperty(state, state.context()->staticStrings().constructor, ObjectPropertyDescriptor(m_##errorname##Error, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectStructurePropertyDescriptor::ConfigurablePresent))); \
|
||||
m_##errorname##ErrorPrototype->directDefineOwnProperty(state, state.context()->staticStrings().message, ObjectPropertyDescriptor(String::emptyString(), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectStructurePropertyDescriptor::ConfigurablePresent))); \
|
||||
m_##errorname##ErrorPrototype->directDefineOwnProperty(state, state.context()->staticStrings().message, ObjectPropertyDescriptor(String::emptyString, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectStructurePropertyDescriptor::ConfigurablePresent))); \
|
||||
m_##errorname##ErrorPrototype->directDefineOwnProperty(state, state.context()->staticStrings().name, ObjectPropertyDescriptor(state.context()->staticStrings().bname##Error.string(), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectStructurePropertyDescriptor::ConfigurablePresent))); \
|
||||
m_##errorname##Error->setFunctionPrototype(state, m_##errorname##ErrorPrototype); \
|
||||
redefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().bname##Error), \
|
||||
|
|
@ -329,7 +290,6 @@ void GlobalObject::installError(ExecutionState& state)
|
|||
DEFINE_ERROR(uri, URI, 1);
|
||||
DEFINE_ERROR(eval, Eval, 1);
|
||||
DEFINE_ERROR(aggregate, Aggregate, 2);
|
||||
DEFINE_ERROR(suppressed, Suppressed, 3);
|
||||
|
||||
redefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().Error),
|
||||
ObjectPropertyDescriptor(m_error, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
|
|
|||
|
|
@ -102,9 +102,12 @@ static Value builtinfinalizationRegistryCleanupSome(ExecutionState& state, Value
|
|||
|
||||
void GlobalObject::initializeFinalizationRegistry(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->finalizationRegistry(); }, nullptr);
|
||||
return self->asGlobalObject()->finalizationRegistry();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().FinalizationRegistry), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ static Value builtinFunctionConstructor(ExecutionState& state, Value thisValue,
|
|||
}
|
||||
|
||||
size_t argumentVectorCount = argc > 1 ? argc - 1 : 0;
|
||||
Value sourceValue = argc >= 1 ? argv[argc - 1] : Value(String::emptyString());
|
||||
Value sourceValue = argc >= 1 ? argv[argc - 1] : Value(String::emptyString);
|
||||
auto functionSource = FunctionObject::createDynamicFunctionScript(state, state.context()->staticStrings().anonymous, argumentVectorCount, argv, sourceValue, false, false, false, false);
|
||||
|
||||
// Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto).
|
||||
|
|
@ -81,7 +81,7 @@ static Value builtinFunctionToString(ExecutionState& state, Value thisValue, siz
|
|||
while (length > 0 && EscargotLexer::isWhiteSpaceOrLineTerminator(src[length - 1])) {
|
||||
length--;
|
||||
}
|
||||
builder.appendString(new StringView(src, 0, length), &state);
|
||||
builder.appendString(new StringView(src, 0, length));
|
||||
} else {
|
||||
ASSERT(fn->isNativeFunctionObject());
|
||||
builder.appendString("function ");
|
||||
|
|
@ -155,7 +155,7 @@ static Value builtinFunctionBind(ExecutionState& state, Value thisValue, size_t
|
|||
Value boundThis = argv[0];
|
||||
size_t boundArgc = (argc > 0) ? argc - 1 : 0;
|
||||
Value* boundArgv = (boundArgc > 0) ? argv + 1 : nullptr;
|
||||
// BoundFunctionObject* F = new BoundFunctionObject(state, thisValue, boundThis, boundArgc, boundArgv);
|
||||
//BoundFunctionObject* F = new BoundFunctionObject(state, thisValue, boundThis, boundArgc, boundArgv);
|
||||
|
||||
// Let targetHasLength be HasOwnProperty(Target, "length").
|
||||
bool targetHasLength = target->hasOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().length));
|
||||
|
|
@ -179,14 +179,14 @@ static Value builtinFunctionBind(ExecutionState& state, Value thisValue, size_t
|
|||
Value targetName = target->get(state, ObjectPropertyName(state.context()->staticStrings().name)).value(state, target);
|
||||
// If Type(targetName) is not String, let targetName be the empty string.
|
||||
if (!targetName.isString()) {
|
||||
targetName = String::emptyString();
|
||||
targetName = String::emptyString;
|
||||
}
|
||||
|
||||
StringBuilder builder;
|
||||
builder.appendString("bound ");
|
||||
builder.appendString(targetName.asString());
|
||||
// F->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().name),
|
||||
// ObjectPropertyDescriptor(Value(builder.finalize(&state)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
//F->defineOwnPropertyThrowsException(state, ObjectPropertyName(state.context()->staticStrings().name),
|
||||
// ObjectPropertyDescriptor(Value(builder.finalize(&state)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
// Let F be BoundFunctionCreate(Target, thisArg, args).
|
||||
// Let status be DefinePropertyOrThrow(F, "length", PropertyDescriptor {[[Value]]: L, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}).
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace Escargot {
|
|||
static Value builtinGeneratorFunction(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
size_t argumentVectorCount = argc > 1 ? argc - 1 : 0;
|
||||
Value sourceValue = argc >= 1 ? argv[argc - 1] : Value(String::emptyString());
|
||||
Value sourceValue = argc >= 1 ? argv[argc - 1] : Value(String::emptyString);
|
||||
auto functionSource = FunctionObject::createDynamicFunctionScript(state, state.context()->staticStrings().anonymous, argumentVectorCount, argv, sourceValue, false, true, false, false);
|
||||
|
||||
// Let proto be ? GetPrototypeFromConstructor(newTarget, fallbackProto).
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -21,9 +21,9 @@
|
|||
#include "runtime/Context.h"
|
||||
#include "runtime/GlobalObject.h"
|
||||
#include "runtime/JSON.h"
|
||||
#include "runtime/RawJSONObject.h"
|
||||
#include "runtime/NativeFunctionObject.h"
|
||||
#include "runtime/VMInstance.h"
|
||||
#include "runtime/Value.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
|
|
@ -37,63 +37,14 @@ static Value builtinJSONStringify(ExecutionState& state, Value thisValue, size_t
|
|||
return JSON::stringify(state, argv[0], argv[1], argv[2]);
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-json-parse-with-source/#sec-json.rawjson
|
||||
static Value builtinJSONRawJSON(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// 1. Let jsonString be ? ToString(text).
|
||||
String* jsonString = argv[0].toString(state);
|
||||
// 2. Throw a SyntaxError exception if jsonString is the empty String, or
|
||||
// if either the first or last code unit of jsonString is any of 0x0009 (CHARACTER TABULATION), 0x000A (LINE FEED), 0x000D (CARRIAGE RETURN), or 0x0020 (SPACE).
|
||||
// 3. Parse StringToCodePoints(jsonString) as a JSON text as specified in ECMA-404.
|
||||
// Throw a SyntaxError exception if it is not a valid JSON text as defined in that specification,
|
||||
// or if its outermost value is an object or array as defined in that specification.
|
||||
auto msg = "input value is not valid JSON text";
|
||||
if (jsonString->length() == 0) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::SyntaxError, msg);
|
||||
}
|
||||
auto isWrongCodePoint = [](char32_t cp) -> bool {
|
||||
if (cp == 0x9 || cp == 0xa || cp == 0xd || cp == 0x20) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (isWrongCodePoint(jsonString->codePointAt(0).codePoint) || isWrongCodePoint(jsonString->codePointAt(jsonString->length() - 1).codePoint)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::SyntaxError, msg);
|
||||
}
|
||||
|
||||
Value jsonParseResult;
|
||||
try {
|
||||
jsonParseResult = JSON::parse(state, jsonString, Value());
|
||||
} catch (const Value& e) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::SyntaxError, msg);
|
||||
}
|
||||
if (jsonParseResult.isObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::SyntaxError, msg);
|
||||
}
|
||||
// 4. Let internalSlotsList be « [[IsRawJSON]] ».
|
||||
// 5. Let obj be OrdinaryObjectCreate(null, internalSlotsList).
|
||||
// 6. Perform ! CreateDataPropertyOrThrow(obj, "rawJSON", jsonString).
|
||||
// 7. Perform ! SetIntegrityLevel(obj, frozen).
|
||||
// 8. Return obj.
|
||||
return new RawJSONObject(state, jsonString);
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-json-parse-with-source/#sec-json.israwjson
|
||||
static Value builtinJSONIsRawJSON(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// 1. If Type(O) is Object and O has an [[IsRawJSON]] internal slot, return true.
|
||||
if (argv[0].isObject() && argv[0].asObject()->isRawJSONObject()) {
|
||||
return Value(true);
|
||||
}
|
||||
// 2. Return false.
|
||||
return Value(false);
|
||||
}
|
||||
|
||||
void GlobalObject::initializeJSON(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->json(); }, nullptr);
|
||||
return self->asGlobalObject()->json();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().JSON), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
@ -118,15 +69,5 @@ void GlobalObject::installJSON(ExecutionState& state)
|
|||
m_json->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().stringify),
|
||||
ObjectPropertyDescriptor(m_jsonStringify,
|
||||
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
auto rawJSON = new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().rawJSON, builtinJSONRawJSON, 1, NativeFunctionInfo::Strict));
|
||||
m_json->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().rawJSON),
|
||||
ObjectPropertyDescriptor(rawJSON,
|
||||
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
auto isRawJSON = new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().isRawJSON, builtinJSONIsRawJSON, 1, NativeFunctionInfo::Strict));
|
||||
m_json->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().isRawJSON),
|
||||
ObjectPropertyDescriptor(isRawJSON,
|
||||
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
}
|
||||
} // namespace Escargot
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
#include "runtime/Context.h"
|
||||
#include "runtime/VMInstance.h"
|
||||
#include "runtime/MapObject.h"
|
||||
#include "runtime/ArrayObject.h"
|
||||
#include "runtime/IteratorObject.h"
|
||||
#include "runtime/NativeFunctionObject.h"
|
||||
|
||||
|
|
@ -66,8 +65,7 @@ static Value builtinMapConstructor(ExecutionState& state, Value thisValue, size_
|
|||
|
||||
Value nextItem = IteratorObject::iteratorValue(state, next.value());
|
||||
if (!nextItem.isObject()) {
|
||||
ErrorObject* errorobj = ErrorObject::createError(state, ErrorCode::TypeError,
|
||||
new ASCIIStringFromExternalMemory("Invalid iterator value"));
|
||||
ErrorObject* errorobj = ErrorObject::createError(state, ErrorCode::TypeError, new ASCIIString("TypeError"));
|
||||
return IteratorObject::iteratorClose(state, iteratorRecord, errorobj, true);
|
||||
}
|
||||
|
||||
|
|
@ -118,18 +116,6 @@ static Value builtinMapGet(ExecutionState& state, Value thisValue, size_t argc,
|
|||
return M->get(state, argv[0]);
|
||||
}
|
||||
|
||||
static Value builtinMapGetOrInsert(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
RESOLVE_THIS_BINDING_TO_MAP(M, Map, getOrInsert);
|
||||
return M->getOrInsert(state, argv[0], argv[1]);
|
||||
}
|
||||
|
||||
static Value builtinMapGetOrInsertComputed(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
RESOLVE_THIS_BINDING_TO_MAP(M, Map, getOrInsertComputed);
|
||||
return M->getOrInsertComputed(state, argv[0], argv[1]);
|
||||
}
|
||||
|
||||
static Value builtinMapHas(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
RESOLVE_THIS_BINDING_TO_MAP(M, Map, has);
|
||||
|
|
@ -208,29 +194,14 @@ static Value builtinMapIteratorNext(ExecutionState& state, Value thisValue, size
|
|||
return iter->next(state);
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/multipage/keyed-collections.html#sec-map.groupby
|
||||
static Value builtinMapGroupBy(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// Let groups be ? GroupBy(items, callbackfn, COLLECTION).
|
||||
auto groups = IteratorObject::groupBy(state, argv[0], argv[1], IteratorObject::GroupByKeyCoercion::Collection);
|
||||
// Let map be ! Construct(%Map%).
|
||||
auto map = new MapObject(state);
|
||||
// For each Record { [[Key]], [[Elements]] } g of groups, do
|
||||
for (size_t i = 0; i < groups.size(); i++) {
|
||||
// Let elements be CreateArrayFromList(g.[[Elements]]).
|
||||
// Let entry be the Record { [[Key]]: g.[[Key]], [[Value]]: elements }.
|
||||
// Append entry to map.[[MapData]].
|
||||
map->set(state, groups[i]->key, Value(Object::createArrayFromList(state, groups[i]->elements)));
|
||||
}
|
||||
// Return map.
|
||||
return map;
|
||||
}
|
||||
|
||||
void GlobalObject::initializeMap(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->map(); }, nullptr);
|
||||
return self->asGlobalObject()->map();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().Map), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
@ -249,10 +220,6 @@ void GlobalObject::installMap(ExecutionState& state)
|
|||
m_map->directDefineOwnProperty(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().species), desc);
|
||||
}
|
||||
|
||||
// Map.groupBy
|
||||
m_map->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().groupBy),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().groupBy, builtinMapGroupBy, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_mapPrototype = new MapObject(state, m_objectPrototype);
|
||||
m_mapPrototype->setGlobalIntrinsicObject(state, true);
|
||||
m_mapPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().constructor), ObjectPropertyDescriptor(m_map, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
|
@ -266,12 +233,6 @@ void GlobalObject::installMap(ExecutionState& state)
|
|||
m_mapPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().get),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().get, builtinMapGet, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_mapPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().getOrInsert),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().getOrInsert, builtinMapGetOrInsert, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_mapPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().getOrInsertComputed),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().getOrInsertComputed, builtinMapGetOrInsertComputed, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_mapPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().has),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().has, builtinMapHas, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
|
|
|
|||
|
|
@ -24,13 +24,10 @@
|
|||
#include "runtime/ThreadLocal.h"
|
||||
#include "runtime/StringObject.h"
|
||||
#include "runtime/NativeFunctionObject.h"
|
||||
#include "runtime/IteratorObject.h"
|
||||
|
||||
#include "runtime/IEEE754.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "xsum.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
static Value builtinMathAbs(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
|
|
@ -323,142 +320,6 @@ static Value builtinMathFround(ExecutionState& state, Value thisValue, size_t ar
|
|||
return Value(Value::DoubleToIntConvertibleTestNeeds, static_cast<double>(static_cast<float>(x)));
|
||||
}
|
||||
|
||||
// This is extracted from Version 2.2.0 of the half library by Christian Rau.
|
||||
// See https://sourceforge.net/projects/half/.
|
||||
// The original copyright and MIT license are reproduced below:
|
||||
|
||||
// half - IEEE 754-based half-precision floating-point library.
|
||||
//
|
||||
// Copyright (c) 2012-2021 Christian Rau <rauy@users.sourceforge.net>
|
||||
//
|
||||
// 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.
|
||||
|
||||
/// Type traits for floating-point bits.
|
||||
template <typename T>
|
||||
struct bits {
|
||||
using type = unsigned char;
|
||||
};
|
||||
template <typename T>
|
||||
struct bits<const T> : bits<T> {};
|
||||
template <typename T>
|
||||
struct bits<volatile T> : bits<T> {};
|
||||
template <typename T>
|
||||
struct bits<const volatile T> : bits<T> {};
|
||||
|
||||
/// Unsigned integer of (at least) 64 bits width.
|
||||
template <>
|
||||
struct bits<double> {
|
||||
using type = std::uint_least64_t;
|
||||
};
|
||||
|
||||
|
||||
/// Fastest unsigned integer of (at least) 32 bits width.
|
||||
using uint32 = std::uint_fast32_t;
|
||||
|
||||
/// Half-precision overflow.
|
||||
/// \param sign half-precision value with sign bit only
|
||||
/// \return rounded overflowing half-precision value
|
||||
constexpr unsigned int overflow(unsigned int sign = 0) { return sign | 0x7C00; }
|
||||
|
||||
/// Half-precision underflow.
|
||||
/// \param sign half-precision value with sign bit only
|
||||
/// \return rounded underflowing half-precision value
|
||||
constexpr unsigned int underflow(unsigned int sign = 0) { return sign; }
|
||||
|
||||
/// Round half-precision number.
|
||||
/// \param value finite half-precision number to round
|
||||
/// \param g guard bit (most significant discarded bit)
|
||||
/// \param s sticky bit (or of all but the most significant discarded bits)
|
||||
/// \return rounded half-precision value
|
||||
constexpr unsigned int rounded(unsigned int value, int g, int s)
|
||||
{
|
||||
return value + (g & (s | value));
|
||||
}
|
||||
|
||||
/// Convert IEEE double-precision to half-precision.
|
||||
/// \param value double-precision value to convert
|
||||
/// \return rounded half-precision value
|
||||
inline unsigned int float2half_impl(double value)
|
||||
{
|
||||
bits<double>::type dbits;
|
||||
std::memcpy(&dbits, &value, sizeof(double));
|
||||
uint32 hi = dbits >> 32, lo = dbits & 0xFFFFFFFF;
|
||||
unsigned int sign = (hi >> 16) & 0x8000;
|
||||
hi &= 0x7FFFFFFF;
|
||||
if (hi >= 0x7FF00000)
|
||||
return sign | 0x7C00 | ((dbits & 0xFFFFFFFFFFFFF) ? (0x200 | ((hi >> 10) & 0x3FF)) : 0);
|
||||
if (hi >= 0x40F00000)
|
||||
return overflow(sign);
|
||||
if (hi >= 0x3F100000)
|
||||
return rounded(sign | (((hi >> 20) - 1008) << 10) | ((hi >> 10) & 0x3FF),
|
||||
(hi >> 9) & 1, ((hi & 0x1FF) | lo) != 0);
|
||||
if (hi >= 0x3E600000) {
|
||||
int i = 1018 - (hi >> 20);
|
||||
hi = (hi & 0xFFFFF) | 0x100000;
|
||||
return rounded(sign | (hi >> (i + 1)), (hi >> i) & 1,
|
||||
((hi & ((static_cast<uint32>(1) << i) - 1)) | lo) != 0);
|
||||
}
|
||||
if ((hi | lo) != 0)
|
||||
return underflow(sign);
|
||||
return sign;
|
||||
}
|
||||
|
||||
/// Convert half-precision to IEEE double-precision.
|
||||
/// \param value half-precision value to convert
|
||||
/// \return double-precision value
|
||||
inline double half2float_impl(unsigned int value)
|
||||
{
|
||||
uint32 hi = static_cast<uint32>(value & 0x8000) << 16;
|
||||
unsigned int abs = value & 0x7FFF;
|
||||
if (abs) {
|
||||
hi |= 0x3F000000 << static_cast<unsigned>(abs >= 0x7C00);
|
||||
for (; abs < 0x400; abs <<= 1, hi -= 0x100000)
|
||||
;
|
||||
hi += static_cast<uint32>(abs) << 10;
|
||||
}
|
||||
bits<double>::type dbits = static_cast<bits<double>::type>(hi) << 32;
|
||||
double out;
|
||||
std::memcpy(&out, &dbits, sizeof(double));
|
||||
return out;
|
||||
}
|
||||
|
||||
// End of the half library extraction.
|
||||
|
||||
// \brief Conversion from float/double to half round number.
|
||||
// \details For the specification, see https://tc39.es/proposal-float16array/#sec-function-properties-of-the-math-object.
|
||||
// \author Anwar Fuadi
|
||||
// \date 2025-present
|
||||
static auto builtinMathF16round(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget) -> Value
|
||||
{
|
||||
// 1. Let n be ? ToNumber(x).
|
||||
auto x = double{ argv[0].toNumber(state) };
|
||||
|
||||
// 2. If n is NaN, return NaN.
|
||||
// 3. If n is one of +0𝔽, -0𝔽, +∞𝔽, or -∞𝔽, return n.
|
||||
// 4. Let n16 be the result of converting n to IEEE 754-2019 binary16 format using roundTiesToEven mode.
|
||||
auto f16 = float2half_impl(x);
|
||||
x = half2float_impl(f16);
|
||||
// 5. Let n64 be the result of converting n16 to IEEE 754-2019 binary64 format.
|
||||
// 6. Return the ECMAScript Number value corresponding to n64.
|
||||
return Value(Value::DoubleToIntConvertibleTestNeeds, x);
|
||||
}
|
||||
|
||||
static Value builtinMathHypot(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
double maxValue = 0;
|
||||
|
|
@ -548,133 +409,14 @@ static Value builtinMathExpm1(ExecutionState& state, Value thisValue, size_t arg
|
|||
return Value(Value::DoubleToIntConvertibleTestNeeds, ieee754::expm1(x));
|
||||
}
|
||||
|
||||
enum class SumPreciseState {
|
||||
MinusZero,
|
||||
NotANumber,
|
||||
MinusInfinity,
|
||||
PlusInfinity,
|
||||
Finite
|
||||
};
|
||||
|
||||
// https://tc39.es/proposal-math-sum/#sec-math.sumprecise
|
||||
static Value builtinMathSumPrecise(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
const Value& items = argv[0];
|
||||
|
||||
// 1. Perform ? RequireObjectCoercible(items).
|
||||
if (items.isUndefinedOrNull()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "sumPrecise called on undefined or null");
|
||||
}
|
||||
|
||||
xsum_small_accumulator sacc;
|
||||
xsum_small_init(&sacc);
|
||||
|
||||
// 2. Let iteratorRecord be ? GetIterator(items, sync).
|
||||
auto iteratorRecord = IteratorObject::getIterator(state, items, true);
|
||||
// 3. Let state be minus-zero.
|
||||
SumPreciseState sumPreciseState = SumPreciseState::MinusZero;
|
||||
// 4. Let sum be 0.
|
||||
// 5. Let count be 0.
|
||||
// 6. Let next be not-started.
|
||||
uint64_t count = 0;
|
||||
// 7. Repeat, while next is not done,
|
||||
while (true) {
|
||||
// a. Set next to ? IteratorStepValue(iteratorRecord).
|
||||
auto next = IteratorObject::iteratorStepValue(state, iteratorRecord);
|
||||
// b. If next is not done, then
|
||||
if (next) {
|
||||
// i. Set count to count + 1.
|
||||
count++;
|
||||
// ii. If count ≥ 2**53, then
|
||||
if (count >= (1LL << 53LL)) {
|
||||
// 1. Let error be ThrowCompletion(a newly created RangeError object).
|
||||
Value error = ErrorObject::createError(state, ErrorCode::TypeError, new ASCIIStringFromExternalMemory("Too many result on sumPrecise function"));
|
||||
// 2. Return ? IteratorClose(iteratorRecord, error).
|
||||
IteratorObject::iteratorClose(state, iteratorRecord, error, true);
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
// iii. NOTE: The above case is not expected to be reached in practice and is included only so that implementations may rely on inputs being "reasonably sized" without violating this specification.
|
||||
// iv. If next is not a Number, then
|
||||
if (!next.value().isNumber()) {
|
||||
// 1. Let error be ThrowCompletion(a newly created TypeError object).
|
||||
Value error = ErrorObject::createError(state, ErrorCode::TypeError, new ASCIIStringFromExternalMemory("sumPrecise function needs number value"));
|
||||
// 2. Return ? IteratorClose(iteratorRecord, error).
|
||||
IteratorObject::iteratorClose(state, iteratorRecord, error, true);
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
// v. Let n be next.
|
||||
double n = next.value().asNumber();
|
||||
// vi. If state is not not-a-number, then
|
||||
if (sumPreciseState != SumPreciseState::NotANumber) {
|
||||
// 1. If n is NaN, then
|
||||
if (std::isnan(n)) {
|
||||
// a. Set state to not-a-number.
|
||||
sumPreciseState = SumPreciseState::NotANumber;
|
||||
} else if (std::isinf(n) && std::signbit(n) == 0) {
|
||||
// 2. Else if n is +∞𝔽, then
|
||||
// a. If state is minus-infinity, set state to not-a-number.
|
||||
if (sumPreciseState == SumPreciseState::MinusInfinity) {
|
||||
sumPreciseState = SumPreciseState::NotANumber;
|
||||
} else {
|
||||
// b. Else, set state to plus-infinity.
|
||||
sumPreciseState = SumPreciseState::PlusInfinity;
|
||||
}
|
||||
} else if (std::isinf(n) && std::signbit(n) == 1) {
|
||||
// 3. Else if n is -∞𝔽, then
|
||||
// a. If state is plus-infinity, set state to not-a-number.
|
||||
if (sumPreciseState == SumPreciseState::PlusInfinity) {
|
||||
sumPreciseState = SumPreciseState::NotANumber;
|
||||
} else {
|
||||
// b. Else, set state to minus-infinity.
|
||||
sumPreciseState = SumPreciseState::MinusInfinity;
|
||||
}
|
||||
} else if (!(n == 0 && std::signbit(n)) && (sumPreciseState == SumPreciseState::MinusZero || sumPreciseState == SumPreciseState::Finite)) {
|
||||
// 4. Else if n is not -0𝔽 and state is either minus-zero or finite, then
|
||||
// a. Set state to finite.
|
||||
sumPreciseState = SumPreciseState::Finite;
|
||||
// b. Set sum to sum + ℝ(n).
|
||||
xsum_small_add1(&sacc, n);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 8. If state is not-a-number, return NaN.
|
||||
// 9. If state is plus-infinity, return +∞𝔽.
|
||||
// 10. If state is minus-infinity, return -∞𝔽.
|
||||
// 11. If state is minus-zero, return -0𝔽.
|
||||
// 12. Return 𝔽(sum).
|
||||
double result = 0;
|
||||
switch (sumPreciseState) {
|
||||
case SumPreciseState::NotANumber:
|
||||
result = std::numeric_limits<double>::quiet_NaN();
|
||||
break;
|
||||
case SumPreciseState::PlusInfinity:
|
||||
result = std::numeric_limits<double>::infinity();
|
||||
break;
|
||||
case SumPreciseState::MinusInfinity:
|
||||
result = -std::numeric_limits<double>::infinity();
|
||||
break;
|
||||
case SumPreciseState::MinusZero:
|
||||
result = -0.0;
|
||||
break;
|
||||
case SumPreciseState::Finite:
|
||||
result = xsum_small_round(&sacc);
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
return Value(Value::DoubleToIntConvertibleTestNeeds, result);
|
||||
}
|
||||
|
||||
void GlobalObject::initializeMath(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->math(); }, nullptr);
|
||||
return self->asGlobalObject()->math();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().Math), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
@ -811,13 +553,6 @@ void GlobalObject::installMath(ExecutionState& state)
|
|||
m_math->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().trunc),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().trunc, builtinMathTrunc, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_math->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().sumPrecise),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().sumPrecise, builtinMathSumPrecise, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_math->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().f16round),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().f16round, builtinMathF16round, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
|
||||
redefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().Math),
|
||||
ObjectPropertyDescriptor(m_math, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -251,20 +251,18 @@ static Value builtinNumberToString(ExecutionState& state, Value thisValue, size_
|
|||
bool minusFlag = (number < 0) ? 1 : 0;
|
||||
number = (number < 0) ? (-1 * number) : number;
|
||||
char buffer[256];
|
||||
size_t len;
|
||||
if (minusFlag) {
|
||||
buffer[0] = '-';
|
||||
len = itoa(static_cast<int64_t>(number), &buffer[1], radix);
|
||||
len += 1;
|
||||
itoa(static_cast<int64_t>(number), &buffer[1], radix);
|
||||
} else {
|
||||
len = itoa(static_cast<int64_t>(number), buffer, radix);
|
||||
itoa(static_cast<int64_t>(number), buffer, radix);
|
||||
}
|
||||
return String::fromASCII(buffer, len);
|
||||
return String::fromASCII(buffer, strlen(buffer));
|
||||
} else {
|
||||
ASSERT(Value(Value::DoubleToIntConvertibleTestNeeds, number).isDouble());
|
||||
NumberObject::RadixBuffer s;
|
||||
const char* str = NumberObject::toStringWithRadix(state, s, number, radix);
|
||||
return String::fromASCII(str, strnlen(str, sizeof(NumberObject::RadixBuffer)));
|
||||
return String::fromASCII(str, strlen(str));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -386,9 +384,12 @@ static Value builtinNumberIsSafeInteger(ExecutionState& state, Value thisValue,
|
|||
|
||||
void GlobalObject::initializeNumber(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->number(); }, nullptr);
|
||||
return self->asGlobalObject()->number();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().Number), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -393,8 +393,7 @@ static Value builtinObjectFromEntries(ExecutionState& state, Value thisValue, si
|
|||
|
||||
Value nextItem = IteratorObject::iteratorValue(state, next.value());
|
||||
if (!nextItem.isObject()) {
|
||||
ErrorObject* errorobj = ErrorObject::createError(state, ErrorCode::TypeError,
|
||||
new ASCIIStringFromExternalMemory("Invalid iterator value"));
|
||||
ErrorObject* errorobj = ErrorObject::createError(state, ErrorCode::TypeError, new ASCIIString("TypeError"));
|
||||
return IteratorObject::iteratorClose(state, iteratorRecord, errorobj, true);
|
||||
}
|
||||
|
||||
|
|
@ -641,7 +640,7 @@ static Value builtinDefineGetter(ExecutionState& state, Value thisValue, size_t
|
|||
Object* O = thisValue.toObject(state);
|
||||
// If IsCallable(getter) is false, throw a TypeError exception.
|
||||
if (!argv[1].isCallable()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, String::emptyString(), true, state.context()->staticStrings().__defineGetter__.string(), ErrorObject::Messages::GlobalObject_CallbackNotCallable);
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, String::emptyString, true, state.context()->staticStrings().__defineGetter__.string(), ErrorObject::Messages::GlobalObject_CallbackNotCallable);
|
||||
}
|
||||
// Let desc be PropertyDescriptor{[[Get]]: getter, [[Enumerable]]: true, [[Configurable]]: true}.
|
||||
ObjectPropertyDescriptor desc(JSGetterSetter(argv[1].asObject(), Value(Value::EmptyValue)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::EnumerablePresent | ObjectPropertyDescriptor::ConfigurablePresent));
|
||||
|
|
@ -667,7 +666,7 @@ static Value builtinDefineSetter(ExecutionState& state, Value thisValue, size_t
|
|||
Object* O = thisValue.toObject(state);
|
||||
// If IsCallable(getter) is false, throw a TypeError exception.
|
||||
if (!argv[1].isCallable()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, String::emptyString(), true, state.context()->staticStrings().__defineSetter__.string(), ErrorObject::Messages::GlobalObject_CallbackNotCallable);
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, String::emptyString, true, state.context()->staticStrings().__defineSetter__.string(), ErrorObject::Messages::GlobalObject_CallbackNotCallable);
|
||||
}
|
||||
// Let desc be PropertyDescriptor{[[Get]]: getter, [[Enumerable]]: true, [[Configurable]]: true}.
|
||||
ObjectPropertyDescriptor desc(JSGetterSetter(Value(Value::EmptyValue), argv[1].asObject()), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::EnumerablePresent | ObjectPropertyDescriptor::ConfigurablePresent));
|
||||
|
|
@ -744,24 +743,6 @@ static Value builtinLookupSetter(ExecutionState& state, Value thisValue, size_t
|
|||
return Value();
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.groupby
|
||||
static Value builtinObjectGroupBy(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// Let groups be ? GroupBy(items, callbackfn, PROPERTY).
|
||||
auto groups = IteratorObject::groupBy(state, argv[0], argv[1], IteratorObject::GroupByKeyCoercion::Property);
|
||||
// Let obj be OrdinaryObjectCreate(null).
|
||||
auto obj = new Object(state, Object::PrototypeIsNull);
|
||||
// For each Record { [[Key]], [[Elements]] } g of groups, do
|
||||
for (size_t i = 0; i < groups.size(); i++) {
|
||||
// Let elements be CreateArrayFromList(g.[[Elements]]).
|
||||
// Perform ! CreateDataPropertyOrThrow(obj, g.[[Key]], elements).
|
||||
obj->defineOwnPropertyThrowsException(state, ObjectPropertyName(state, groups[i]->key),
|
||||
ObjectPropertyDescriptor(Object::createArrayFromList(state, groups[i]->elements), ObjectPropertyDescriptor::AllPresent));
|
||||
}
|
||||
// Return obj.
|
||||
return obj;
|
||||
}
|
||||
|
||||
void GlobalObject::initializeObject(ExecutionState& state)
|
||||
{
|
||||
// Object should be installed at the start time
|
||||
|
|
@ -896,10 +877,6 @@ void GlobalObject::installObject(ExecutionState& state)
|
|||
m_object->directDefineOwnProperty(state, ObjectPropertyName(strings.hasOwn),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings.hasOwn, builtinObjectHasOwn, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
// Object.groupBy
|
||||
m_object->directDefineOwnProperty(state, ObjectPropertyName(strings.groupBy),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings.groupBy, builtinObjectGroupBy, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
// $19.1.3.2 Object.prototype.hasOwnProperty(V)
|
||||
m_objectPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings.hasOwnProperty),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings.hasOwnProperty, builtinObjectHasOwnProperty, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
|
|
|||
|
|
@ -36,13 +36,13 @@ static Value builtinPromiseConstructor(ExecutionState& state, Value thisValue, s
|
|||
{
|
||||
auto strings = &state.context()->staticStrings();
|
||||
if (!newTarget.hasValue()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Promise.string(), false, String::emptyString(), "%s: Promise constructor should be called with new Promise()");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Promise.string(), false, String::emptyString, "%s: Promise constructor should be called with new Promise()");
|
||||
return Value();
|
||||
}
|
||||
|
||||
Value executor = argv[0];
|
||||
if (!executor.isCallable()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Promise.string(), false, String::emptyString(), "%s: Promise executor is not a function object");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Promise.string(), false, String::emptyString, "%s: Promise executor is not a function object");
|
||||
}
|
||||
|
||||
// Let promise be ? OrdinaryCreateFromConstructor(NewTarget, "%PromisePrototype%", « [[PromiseState]], [[PromiseResult]], [[PromiseFulfillReactions]], [[PromiseRejectReactions]], [[PromiseIsHandled]] »).
|
||||
|
|
@ -216,7 +216,7 @@ static Value builtinPromiseAll(ExecutionState& state, Value thisValue, size_t ar
|
|||
result = IteratorObject::iteratorClose(state, iteratorRecord, exceptionValue, true);
|
||||
}
|
||||
} catch (const Value& v) {
|
||||
// ignore error value
|
||||
exceptionValue = v;
|
||||
}
|
||||
|
||||
// If value is an abrupt completion,
|
||||
|
|
@ -323,7 +323,7 @@ static Value builtinPromiseRace(ExecutionState& state, Value thisValue, size_t a
|
|||
result = IteratorObject::iteratorClose(state, record, exceptionValue, true);
|
||||
}
|
||||
} catch (const Value& v) {
|
||||
// ignore error here
|
||||
exceptionValue = v;
|
||||
}
|
||||
// If value is an abrupt completion,
|
||||
// Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »).
|
||||
|
|
@ -565,7 +565,7 @@ static Value builtinPromiseAllSettled(ExecutionState& state, Value thisValue, si
|
|||
try {
|
||||
result = IteratorObject::iteratorClose(state, iteratorRecord, exceptionValue, true);
|
||||
} catch (const Value& v) {
|
||||
// ignore error value
|
||||
exceptionValue = v;
|
||||
// IfAbruptRejectPromise(result, promiseCapability).
|
||||
// If value is an abrupt completion,
|
||||
// Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »).
|
||||
|
|
@ -731,7 +731,7 @@ static Value builtinPromiseAny(ExecutionState& state, Value thisValue, size_t ar
|
|||
IteratorObject::iteratorClose(state, iteratorRecord, thrownValue, true);
|
||||
}
|
||||
} catch (const Value& v) {
|
||||
// ignore error here
|
||||
thrownValue = v;
|
||||
}
|
||||
// IfAbruptRejectPromise(result, promiseCapability).
|
||||
Object::call(state, promiseCapability.m_rejectFunction, Value(), 1, &thrownValue);
|
||||
|
|
@ -741,74 +741,14 @@ static Value builtinPromiseAny(ExecutionState& state, Value thisValue, size_t ar
|
|||
return result;
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-promise.withResolvers
|
||||
static Value builtinPromiseWithResolvers(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// Let C be the this value.
|
||||
const Value& C = thisValue;
|
||||
// Let promiseCapability be ? NewPromiseCapability(C).
|
||||
if (!C.isObject()) {
|
||||
// NOTE: isConstructor will be checked on PromiseObject::newPromiseCapability function
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::Not_Constructor);
|
||||
}
|
||||
auto promiseCapability = PromiseObject::newPromiseCapability(state, C.asObject());
|
||||
// Let obj be OrdinaryObjectCreate(%Object.prototype%).
|
||||
Object* obj = new Object(state);
|
||||
|
||||
StaticStrings* strings = &state.context()->staticStrings();
|
||||
// Perform ! CreateDataPropertyOrThrow(obj, "promise", promiseCapability.[[Promise]]).
|
||||
obj->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->lazyPromise()),
|
||||
ObjectPropertyDescriptor(promiseCapability.m_promise, ObjectPropertyDescriptor::AllPresent));
|
||||
// Perform ! CreateDataPropertyOrThrow(obj, "resolve", promiseCapability.[[Resolve]]).
|
||||
obj->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->resolve),
|
||||
ObjectPropertyDescriptor(promiseCapability.m_resolveFunction, ObjectPropertyDescriptor::AllPresent));
|
||||
// Perform ! CreateDataPropertyOrThrow(obj, "reject", promiseCapability.[[Reject]]).
|
||||
obj->defineOwnPropertyThrowsException(state, ObjectPropertyName(strings->reject),
|
||||
ObjectPropertyDescriptor(promiseCapability.m_rejectFunction, ObjectPropertyDescriptor::AllPresent));
|
||||
// Return obj.
|
||||
return obj;
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-promise.try
|
||||
static Value builtinPromiseTry(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// 1. Let C be the this value.
|
||||
const Value& C = thisValue;
|
||||
// 2. If C is not an Object, throw a TypeError exception.
|
||||
if (!C.isObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::Not_Constructor);
|
||||
}
|
||||
// 3. Let promiseCapability be ? NewPromiseCapability(C).
|
||||
auto promiseCapability = PromiseObject::newPromiseCapability(state, C.asObject());
|
||||
|
||||
// 4. Let status be Completion(Call(callback, undefined, args)).
|
||||
Value exception = Value(Value::EmptyValue);
|
||||
Value result;
|
||||
try {
|
||||
result = Object::call(state, argv[0], Value(), argc - 1, argv + 1);
|
||||
} catch (const Value& error) {
|
||||
exception = error;
|
||||
}
|
||||
|
||||
// 5. If status is an abrupt completion, then
|
||||
if (!exception.isEmpty()) {
|
||||
// a. Perform ? Call(promiseCapability.[[Reject]], undefined, « status.[[Value]] »).
|
||||
Object::call(state, promiseCapability.m_rejectFunction, Value(), 1, &exception);
|
||||
} else {
|
||||
// 6. Else,
|
||||
// a. Perform ? Call(promiseCapability.[[Resolve]], undefined, « status.[[Value]] »).
|
||||
Object::call(state, promiseCapability.m_resolveFunction, Value(), 1, &result);
|
||||
}
|
||||
|
||||
// 7. Return promiseCapability.[[Promise]].
|
||||
return promiseCapability.m_promise;
|
||||
}
|
||||
|
||||
void GlobalObject::initializePromise(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->promise(); }, nullptr);
|
||||
return self->asGlobalObject()->promise();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().Promise), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
@ -879,16 +819,6 @@ void GlobalObject::installPromise(ExecutionState& state)
|
|||
ObjectPropertyDescriptor(m_promiseAny,
|
||||
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
// Promise.withResolvers()
|
||||
m_promise->directDefineOwnProperty(state, ObjectPropertyName(strings->withResolvers),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->withResolvers, builtinPromiseWithResolvers, 0, NativeFunctionInfo::Strict)),
|
||||
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
// Promise.try()
|
||||
m_promise->directDefineOwnProperty(state, ObjectPropertyName(strings->stringTry),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->stringTry, builtinPromiseTry, 1, NativeFunctionInfo::Strict)),
|
||||
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
|
||||
redefineOwnProperty(state, ObjectPropertyName(strings->Promise),
|
||||
ObjectPropertyDescriptor(m_promise, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ static Value builtinProxyConstructor(ExecutionState& state, Value thisValue, siz
|
|||
auto strings = &state.context()->staticStrings();
|
||||
|
||||
if (!newTarget.hasValue()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString(), "%s: calling a builtin Proxy constructor without new is forbidden");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Proxy.string(), false, String::emptyString, "%s: calling a builtin Proxy constructor without new is forbidden");
|
||||
return Value();
|
||||
}
|
||||
|
||||
|
|
@ -107,9 +107,12 @@ static Value builtinProxyRevocable(ExecutionState& state, Value thisValue, size_
|
|||
|
||||
void GlobalObject::initializeProxy(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->proxy(); }, nullptr);
|
||||
return self->asGlobalObject()->proxy();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().Proxy), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ static Value builtinReflectApply(ExecutionState& state, Value thisValue, size_t
|
|||
|
||||
// 1. If IsCallable(target) is false, throw a TypeError exception.
|
||||
if (!target.isObject() || !target.asObject()->isCallable()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString(), "%s: calling a not-callable target in apply function is forbidden");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString, "%s: calling a not-callable target in apply function is forbidden");
|
||||
}
|
||||
|
||||
// 2. Let args be CreateListFromArrayLike(argumentsList).
|
||||
|
|
@ -59,13 +59,13 @@ static Value builtinReflectConstruct(ExecutionState& state, Value thisValue, siz
|
|||
|
||||
// 1. If IsConstructor(target) is false, throw a TypeError exception.
|
||||
if (!target.isConstructor()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString(), "%s: The target of Reflect.construct should has a construct method");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString, "%s: The target of Reflect.construct should has a construct method");
|
||||
}
|
||||
|
||||
// 2. If newTarget is not present, let newTarget be target.
|
||||
// 3. Else, if IsConstructor(newTarget) is false, throw a TypeError exception.
|
||||
if (!newTargetArg.isConstructor()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString(), "%s: The new target of Reflect.construct should be a constructor");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString, "%s: The new target of Reflect.construct should be a constructor");
|
||||
}
|
||||
|
||||
// 4. Let args be CreateListFromArrayLike(argumentsList).
|
||||
|
|
@ -82,7 +82,7 @@ static Value builtinReflectDefineProperty(ExecutionState& state, Value thisValue
|
|||
|
||||
// 1. If Type(target) is not Object, throw a TypeError exception.
|
||||
if (!target.isObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString(), "%s: The target of Reflect.defineProperty should be an Object");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString, "%s: The target of Reflect.defineProperty should be an Object");
|
||||
}
|
||||
|
||||
// 2. Let key be ToPropertyKey(propertyKey).
|
||||
|
|
@ -90,7 +90,7 @@ static Value builtinReflectDefineProperty(ExecutionState& state, Value thisValue
|
|||
|
||||
// 3. Let desc be ToPropertyDescriptor(attributes).
|
||||
if (!argv[2].isObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString(), "%s: The attributes of Reflect.defineProperty should be an Object");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString, "%s: The attributes of Reflect.defineProperty should be an Object");
|
||||
}
|
||||
ObjectPropertyDescriptor desc(state, argv[2].asObject());
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ static Value builtinReflectDeleteProperty(ExecutionState& state, Value thisValue
|
|||
|
||||
// 1. If Type(target) is not Object, throw a TypeError exception.
|
||||
if (!target.isObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString(), "%s: The target of Reflect.deleteProperty should be an Object");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString, "%s: The target of Reflect.deleteProperty should be an Object");
|
||||
}
|
||||
|
||||
// 2. Let key be ToPropertyKey(propertyKey).
|
||||
|
|
@ -124,7 +124,7 @@ static Value builtinReflectGet(ExecutionState& state, Value thisValue, size_t ar
|
|||
|
||||
// 1. If Type(target) is not Object, throw a TypeError exception.
|
||||
if (!target.isObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString(), "%s: The target of Reflect.get should be an Object");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString, "%s: The target of Reflect.get should be an Object");
|
||||
}
|
||||
|
||||
// 2. Let key be ToPropertyKey(propertyKey).
|
||||
|
|
@ -146,7 +146,7 @@ static Value builtinReflectGetOwnPropertyDescriptor(ExecutionState& state, Value
|
|||
|
||||
// 1. If Type(target) is not Object, throw a TypeError exception.
|
||||
if (!target.isObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString(), "%s: The target of Reflect.getOwnPropertyDescriptor should be an Object");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString, "%s: The target of Reflect.getOwnPropertyDescriptor should be an Object");
|
||||
}
|
||||
|
||||
// 2. Let key be ToPropertyKey(propertyKey).
|
||||
|
|
@ -167,7 +167,7 @@ static Value builtinReflectGetPrototypeOf(ExecutionState& state, Value thisValue
|
|||
|
||||
// 1. If Type(target) is not Object, throw a TypeError exception.
|
||||
if (!target.isObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString(), "%s: The target of Reflect.getPrototypeOf should be an Object");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString, "%s: The target of Reflect.getPrototypeOf should be an Object");
|
||||
}
|
||||
|
||||
// 2. Return target.[[GetPrototypeOf]]().
|
||||
|
|
@ -182,7 +182,7 @@ static Value builtinReflectHas(ExecutionState& state, Value thisValue, size_t ar
|
|||
|
||||
// 1. If Type(target) is not Object, throw a TypeError exception.
|
||||
if (!target.isObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString(), "%s: The target of Reflect.has should be an Object");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString, "%s: The target of Reflect.has should be an Object");
|
||||
}
|
||||
|
||||
// 2. Let key be ToPropertyKey(propertyKey).
|
||||
|
|
@ -200,7 +200,7 @@ static Value builtinReflectIsExtensible(ExecutionState& state, Value thisValue,
|
|||
|
||||
// 1. If Type(target) is not Object, throw a TypeError exception.
|
||||
if (!target.isObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString(), "%s: The target of Reflect.isExtensible should be an Object");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString, "%s: The target of Reflect.isExtensible should be an Object");
|
||||
}
|
||||
|
||||
// 2. Return target.[[IsExtensible]]().
|
||||
|
|
@ -215,7 +215,7 @@ static Value builtinReflectPreventExtensions(ExecutionState& state, Value thisVa
|
|||
|
||||
// 1. If Type(target) is not Object, throw a TypeError exception.
|
||||
if (!target.isObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString(), "%s: The target of Reflect.preventExtension should be an Object");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString, "%s: The target of Reflect.preventExtension should be an Object");
|
||||
}
|
||||
|
||||
// 2. Return target.[[PreventExtensions]]().
|
||||
|
|
@ -230,7 +230,7 @@ static Value builtinReflectOwnKeys(ExecutionState& state, Value thisValue, size_
|
|||
|
||||
// 1. If Type(target) is not Object, throw a TypeError exception.
|
||||
if (!target.isObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString(), "%s: The target of Reflect.preventExtension should be an Object");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString, "%s: The target of Reflect.preventExtension should be an Object");
|
||||
}
|
||||
// 2. Let keys be target.[[OwnPropertyKeys]]().
|
||||
// 3. ReturnIfAbrupt(keys).
|
||||
|
|
@ -248,7 +248,7 @@ static Value builtinReflectSet(ExecutionState& state, Value thisValue, size_t ar
|
|||
|
||||
// 1. If Type(target) is not Object, throw a TypeError exception.
|
||||
if (!target.isObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString(), "%s: The target of Reflect.set should be an Object");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString, "%s: The target of Reflect.set should be an Object");
|
||||
}
|
||||
|
||||
// 2. Let key be ToPropertyKey(propertyKey).
|
||||
|
|
@ -271,12 +271,12 @@ static Value builtinReflectSetPrototypeOf(ExecutionState& state, Value thisValue
|
|||
|
||||
// 1. If Type(target) is not Object, throw a TypeError exception.
|
||||
if (!target.isObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString(), "%s: The target of Reflect.setPrototypeOf should be an Object");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString, "%s: The target of Reflect.setPrototypeOf should be an Object");
|
||||
}
|
||||
|
||||
// 2. If Type(proto) is not Object and proto is not null, throw a TypeError exception
|
||||
if (!proto.isObject() && !proto.isNull()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString(), "%s: The proto of Reflect.setPrototypeOf should be an Object or Null");
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, strings->Reflect.string(), false, String::emptyString, "%s: The proto of Reflect.setPrototypeOf should be an Object or Null");
|
||||
}
|
||||
|
||||
// 3. Return target.[[SetPrototypeOf]](proto).
|
||||
|
|
@ -285,9 +285,12 @@ static Value builtinReflectSetPrototypeOf(ExecutionState& state, Value thisValue
|
|||
|
||||
void GlobalObject::initializeReflect(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->reflect(); }, nullptr);
|
||||
return self->asGlobalObject()->reflect();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().Reflect), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@
|
|||
#include "runtime/RegExpObject.h"
|
||||
#include "runtime/ArrayObject.h"
|
||||
#include "runtime/NativeFunctionObject.h"
|
||||
#include "parser/Lexer.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
|
|
@ -32,8 +31,8 @@ static Value builtinRegExpConstructor(ExecutionState& state, Value thisValue, si
|
|||
{
|
||||
Value pattern = argv[0];
|
||||
Value flags = argv[1];
|
||||
String* source = pattern.isUndefined() ? String::emptyString() : pattern.toString(state);
|
||||
String* option = flags.isUndefined() ? String::emptyString() : flags.toString(state);
|
||||
String* source = pattern.isUndefined() ? String::emptyString : pattern.toString(state);
|
||||
String* option = flags.isUndefined() ? String::emptyString : flags.toString(state);
|
||||
|
||||
// Let patternIsRegExp be IsRegExp(pattern).
|
||||
bool patternIsRegExp = argv[0].isObject() && argv[0].asObject()->isRegExp(state);
|
||||
|
|
@ -62,10 +61,10 @@ static Value builtinRegExpConstructor(ExecutionState& state, Value thisValue, si
|
|||
option = flags.isUndefined() ? RegExpObject::computeRegExpOptionString(state, patternRegExp) : flags.toString(state);
|
||||
} else if (patternIsRegExp) {
|
||||
Value P = pattern.asObject()->get(state, ObjectPropertyName(state, state.context()->staticStrings().source)).value(state, pattern);
|
||||
source = P.isUndefined() ? String::emptyString() : P.toString(state);
|
||||
source = P.isUndefined() ? String::emptyString : P.toString(state);
|
||||
if (flags.isUndefined()) {
|
||||
Value F = pattern.asObject()->get(state, ObjectPropertyName(state, state.context()->staticStrings().flags)).value(state, pattern);
|
||||
option = F.isUndefined() ? String::emptyString() : F.toString(state);
|
||||
option = F.isUndefined() ? String::emptyString : F.toString(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,11 +106,13 @@ static Value builtinRegExpExec(ExecutionState& state, Value thisValue, size_t ar
|
|||
if (regexp->matchNonGlobally(state, str, result, false, lastIndex)) {
|
||||
int e = result.m_matchResults[0][0].m_end;
|
||||
if (option & RegExpObject::Option::Unicode) {
|
||||
char16_t utfRes = (static_cast<size_t>(e) == str->length()) ? 0 : str->charAt(e);
|
||||
char16_t utfRes = str->charAt(e);
|
||||
const char* buf = reinterpret_cast<const char*>(&utfRes);
|
||||
size_t len = strnlen(buf, 2);
|
||||
size_t eUTF = len == 0 ? str->length() : (str->find(buf, len, 0));
|
||||
if ((int)eUTF > e || e == (int)str->length()) {
|
||||
size_t eUTF = str->find(buf, len, 0);
|
||||
if (eUTF >= str->length()) {
|
||||
e = str->length();
|
||||
} else if ((int)eUTF > e || e == (int)str->length()) {
|
||||
e = eUTF;
|
||||
}
|
||||
}
|
||||
|
|
@ -180,14 +181,14 @@ static Value builtinRegExpToString(ExecutionState& state, Value thisValue, size_
|
|||
String* pattern = RegExpObject::regexpSourceValue(state, thisObject);
|
||||
Value flagsValue = RegExpObject::regexpFlagsValue(state, thisObject);
|
||||
|
||||
builder.appendString("/", &state);
|
||||
builder.appendString(pattern, &state);
|
||||
builder.appendString("/", &state);
|
||||
builder.appendString("/");
|
||||
builder.appendString(pattern);
|
||||
builder.appendString("/");
|
||||
|
||||
if (!flagsValue.isUndefined()) {
|
||||
builder.appendString(flagsValue.toString(state), &state);
|
||||
builder.appendString(flagsValue.toString(state));
|
||||
} else {
|
||||
builder.appendString("\0", &state);
|
||||
builder.appendString("\0");
|
||||
}
|
||||
|
||||
return builder.finalize(&state);
|
||||
|
|
@ -199,29 +200,6 @@ static Value builtinRegExpCompile(ExecutionState& state, Value thisValue, size_t
|
|||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::GlobalObject_ThisNotRegExpObject);
|
||||
}
|
||||
|
||||
Optional<Object*> proto = thisValue.asObject()->getPrototypeObject(state);
|
||||
Context* calleeContext = state.resolveCallee()->codeBlock()->context();
|
||||
|
||||
if (!proto || !proto->isRegExpPrototypeObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::GlobalObject_ThisNotRegExpObject);
|
||||
}
|
||||
|
||||
bool match = false;
|
||||
while (proto) {
|
||||
Value c = proto->getOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().constructor)).value(state, proto.value());
|
||||
if (c.isFunction()) {
|
||||
if (c.asFunction()->codeBlock()->context() == calleeContext) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
proto = proto->getPrototypeObject(state);
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Cannot use compile function with another Realm");
|
||||
}
|
||||
|
||||
if (argv[0].isObject() && argv[0].asObject()->isRegExpObject()) {
|
||||
if (!argv[1].isUndefined()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Cannot supply flags when constructing one RegExp from another");
|
||||
|
|
@ -238,9 +216,9 @@ static Value builtinRegExpCompile(ExecutionState& state, Value thisValue, size_t
|
|||
}
|
||||
|
||||
RegExpObject* retVal = thisValue.asPointerValue()->asObject()->asRegExpObject();
|
||||
String* patternStr = argv[0].isUndefined() ? String::emptyString() : argv[0].toString(state);
|
||||
String* flagsStr = argv[1].isUndefined() ? String::emptyString() : argv[1].toString(state);
|
||||
retVal->init(state, patternStr, flagsStr);
|
||||
String* pattern_str = argv[0].isUndefined() ? String::emptyString : argv[0].toString(state);
|
||||
String* flags_str = argv[1].isUndefined() ? String::emptyString : argv[1].toString(state);
|
||||
retVal->init(state, pattern_str, flags_str);
|
||||
return retVal;
|
||||
}
|
||||
static Value builtinRegExpSearch(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
|
|
@ -421,19 +399,16 @@ static Value builtinRegExpReplace(ExecutionState& state, Value thisValue, size_t
|
|||
size_t lengthStr = str->length();
|
||||
bool functionalReplace = replaceValue.isCallable();
|
||||
bool fullUnicode = false;
|
||||
String* replacement = String::emptyString();
|
||||
String* replacement = String::emptyString;
|
||||
size_t nextSourcePosition = 0;
|
||||
StringBuilder builder;
|
||||
|
||||
if (!functionalReplace) {
|
||||
replaceValue = replaceValue.toString(state);
|
||||
}
|
||||
|
||||
String* flags = rx.asObject()->get(state, ObjectPropertyName(state.context()->staticStrings().flags)).value(state, rx).toString(state);
|
||||
|
||||
bool global = flags->contains("g");
|
||||
bool global = rx.asObject()->get(state, ObjectPropertyName(state, state.context()->staticStrings().global)).value(state, rx).toBoolean();
|
||||
if (global) {
|
||||
fullUnicode = flags->contains("u");
|
||||
fullUnicode = rx.asObject()->get(state, ObjectPropertyName(state, state.context()->staticStrings().unicode)).value(state, rx).toBoolean();
|
||||
rx.asObject()->setThrowsException(state, ObjectPropertyName(state, state.context()->staticStrings().lastIndex), Value(0), rx);
|
||||
}
|
||||
ValueVectorWithInlineStorage results;
|
||||
|
|
@ -493,7 +468,7 @@ static Value builtinRegExpReplace(ExecutionState& state, Value thisValue, size_t
|
|||
if (!capN.isUndefined()) {
|
||||
captures.push_back(capN.toString(state));
|
||||
} else {
|
||||
captures.push_back(String::emptyString());
|
||||
captures.push_back(String::emptyString);
|
||||
}
|
||||
if (functionalReplace) {
|
||||
replacerArgs[n] = capN;
|
||||
|
|
@ -533,9 +508,8 @@ static Value builtinRegExpMatch(ExecutionState& state, Value thisValue, size_t a
|
|||
String* str = argv[0].toString(state);
|
||||
ASSERT(str != nullptr);
|
||||
|
||||
// 21.2.5.6.8
|
||||
String* flags = rx.asObject()->get(state, ObjectPropertyName(state.context()->staticStrings().flags)).value(state, rx).toString(state);
|
||||
bool global = flags->contains("g");
|
||||
//21.2.5.6.8
|
||||
bool global = rx.asObject()->get(state, ObjectPropertyName(state, state.context()->staticStrings().global)).value(state, rx).toBoolean();
|
||||
|
||||
if (!global) {
|
||||
return regExpExec(state, rx.asObject(), str);
|
||||
|
|
@ -546,22 +520,22 @@ static Value builtinRegExpMatch(ExecutionState& state, Value thisValue, size_t a
|
|||
ArrayObject* A = new ArrayObject(state);
|
||||
size_t n = 0;
|
||||
|
||||
// 21.2.5.6.8.g.i
|
||||
//21.2.5.6.8.g.i
|
||||
while (true) {
|
||||
// 21.2.5.6.8.g.i
|
||||
//21.2.5.6.8.g.i
|
||||
Value result = regExpExec(state, rx.asObject(), str);
|
||||
// 21.2.5.6.8.g.iii
|
||||
//21.2.5.6.8.g.iii
|
||||
if (result.isNull()) {
|
||||
if (n == 0) {
|
||||
return Value(Value::Null);
|
||||
}
|
||||
return A;
|
||||
} else {
|
||||
// 21.2.5.6.8.g.iv
|
||||
//21.2.5.6.8.g.iv
|
||||
Value matchStr = result.asObject()->get(state, ObjectPropertyName(state, Value(0))).value(state, result).toString(state);
|
||||
A->defineOwnProperty(state, ObjectPropertyName(state, Value(n).toString(state)), ObjectPropertyDescriptor(Value(matchStr), (ObjectPropertyDescriptor::PresentAttribute::AllPresent)));
|
||||
if (matchStr.asString()->length() == 0) {
|
||||
// 21.2.5.6.8.g.iv.5
|
||||
//21.2.5.6.8.g.iv.5
|
||||
uint64_t thisIndex = rx.asObject()->get(state, ObjectPropertyName(state, state.context()->staticStrings().lastIndex)).value(state, rx).toLength(state);
|
||||
uint64_t nextIndex = str->advanceStringIndex(thisIndex, fullUnicode);
|
||||
rx.asObject()->setThrowsException(state, state.context()->staticStrings().lastIndex, Value(nextIndex), rx);
|
||||
|
|
@ -593,7 +567,7 @@ static Value builtinRegExpMatchAll(ExecutionState& state, Value thisValue, size_
|
|||
if (flags->find("g") != SIZE_MAX) {
|
||||
global = true;
|
||||
}
|
||||
if (flags->find("u") != SIZE_MAX || flags->find("v") != SIZE_MAX) {
|
||||
if (flags->find("u") != SIZE_MAX) {
|
||||
unicode = true;
|
||||
}
|
||||
return new RegExpStringIteratorObject(state, global, unicode, matcher->asRegExpObject(), s);
|
||||
|
|
@ -646,11 +620,6 @@ static Value builtinRegExpMultiLineGetter(ExecutionState& state, Value thisValue
|
|||
return builtinRegExpOptionGetterHelper(state, thisValue, RegExpObject::Option::MultiLine);
|
||||
}
|
||||
|
||||
static Value builtinRegExpHasIndicesGetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
return builtinRegExpOptionGetterHelper(state, thisValue, RegExpObject::Option::HasIndices);
|
||||
}
|
||||
|
||||
static Value builtinRegExpSourceGetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
if (!thisValue.isObject()) {
|
||||
|
|
@ -678,11 +647,6 @@ static Value builtinRegExpUnicodeGetter(ExecutionState& state, Value thisValue,
|
|||
return builtinRegExpOptionGetterHelper(state, thisValue, RegExpObject::Option::Unicode);
|
||||
}
|
||||
|
||||
static Value builtinRegExpUnicodeSetsGetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
return builtinRegExpOptionGetterHelper(state, thisValue, RegExpObject::Option::UnicodeSets);
|
||||
}
|
||||
|
||||
|
||||
// Legacy RegExp Features (non-standard)
|
||||
static Value builtinRegExpInputGetter(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
|
|
@ -747,229 +711,12 @@ static Value builtinRegExpInputSetter(ExecutionState& state, Value thisValue, si
|
|||
if (!status.isValid()) { \
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::String_InvalidStringLength); \
|
||||
} \
|
||||
return (status.dollarCount < number) ? String::emptyString() : new StringView(status.dollars[number - 1]); \
|
||||
return (status.dollarCount < number) ? String::emptyString : new StringView(status.dollars[number - 1]); \
|
||||
}
|
||||
|
||||
REGEXP_LEGACY_FEATURES(DEFINE_LEGACY_FEATURE_GETTER);
|
||||
REGEXP_LEGACY_DOLLAR_NUMBER_FEATURES(DEFINE_LEGACY_DOLLAR_NUMBER_FEATURE_GETTER);
|
||||
|
||||
// https://tc39.es/ecma262/#prod-SyntaxCharacter
|
||||
static bool isSyntaxChar(char32_t cp)
|
||||
{
|
||||
switch (cp) {
|
||||
case '^':
|
||||
case '$':
|
||||
case '\\':
|
||||
case '.':
|
||||
case '*':
|
||||
case '+':
|
||||
case '?':
|
||||
case '(':
|
||||
case ')':
|
||||
case '[':
|
||||
case ']':
|
||||
case '{':
|
||||
case '}':
|
||||
case '|':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void utf16EncodeCodePoint(ExecutionState& state, char32_t cp, LargeStringBuilder& builder)
|
||||
{
|
||||
auto r = utf16EncodeCodePoint(cp);
|
||||
if (r.second) {
|
||||
char16_t cu1 = floor((cp - 65536) / 1024) + 0xD800;
|
||||
char16_t cu2 = ((cp - 65536) % 1024) + 0xDC00;
|
||||
|
||||
builder.appendChar(cu1, &state);
|
||||
builder.appendChar(cu2, &state);
|
||||
} else {
|
||||
builder.appendChar(r.first, &state);
|
||||
}
|
||||
}
|
||||
|
||||
static bool isOtherPunctuators(char32_t cp)
|
||||
{
|
||||
// ",-=<>#&!%:;@~'`" and the code unit 0x0022 (QUOTATION MARK).
|
||||
switch (cp) {
|
||||
case ',':
|
||||
case '-':
|
||||
case '=':
|
||||
case '<':
|
||||
case '>':
|
||||
case '#':
|
||||
case '&':
|
||||
case '!':
|
||||
case '%':
|
||||
case ':':
|
||||
case ';':
|
||||
case '@':
|
||||
case '~':
|
||||
case '\'':
|
||||
case '`':
|
||||
case 0x22:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static std::pair<std::array<char, 12>, size_t> itoa(char32_t v)
|
||||
{
|
||||
std::array<char, 12> result;
|
||||
char* tp = result.data();
|
||||
while (v || tp == result.data()) {
|
||||
int i = v % 16;
|
||||
v /= 16;
|
||||
if (i < 10) {
|
||||
*tp++ = i + '0';
|
||||
} else {
|
||||
*tp++ = i + 'a' - 10;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(result, tp - result.data());
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-unicodeescape
|
||||
static void unicodeEscape(ExecutionState& state, char32_t cp, LargeStringBuilder& builder)
|
||||
{
|
||||
ASSERT(cp <= 0x10FFFF);
|
||||
builder.appendChar(static_cast<char>(0x5C), &state);
|
||||
builder.appendChar('u', &state);
|
||||
|
||||
auto a = itoa(cp);
|
||||
int pad = 4 - a.second;
|
||||
while (pad > 0) {
|
||||
builder.appendChar('0', &state);
|
||||
pad--;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < a.second; i++) {
|
||||
builder.appendChar(a.first.at(a.second - i - 1), &state);
|
||||
}
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-encodeforregexpescape
|
||||
static void encodeForRegExpEscape(ExecutionState& state, char32_t cp, LargeStringBuilder& builder)
|
||||
{
|
||||
// If cp is matched by SyntaxCharacter or cp is U+002F (SOLIDUS), then
|
||||
if (isSyntaxChar(cp) || cp == 0x02f) {
|
||||
// a. Return the string-concatenation of 0x005C (REVERSE SOLIDUS) and UTF16EncodeCodePoint(cp).
|
||||
builder.appendChar(static_cast<char>(0x5C), &state);
|
||||
utf16EncodeCodePoint(state, cp, builder);
|
||||
return;
|
||||
} else if (cp >= 0x09 && cp <= 0x0d) {
|
||||
// 2. Else if cp is a code point listed in the “Code Point” column of Table 67, then
|
||||
// a. Return the string-concatenation of 0x005C (REVERSE SOLIDUS) and the string in the “ControlEscape” column of the row whose “Code Point” column contains cp.
|
||||
builder.appendChar(static_cast<char>(0x5C), &state);
|
||||
switch (cp) {
|
||||
case 0x9:
|
||||
builder.appendChar('t', &state);
|
||||
break;
|
||||
case 0xa:
|
||||
builder.appendChar('n', &state);
|
||||
break;
|
||||
case 0xb:
|
||||
builder.appendChar('v', &state);
|
||||
break;
|
||||
case 0xc:
|
||||
builder.appendChar('f', &state);
|
||||
break;
|
||||
case 0xd:
|
||||
builder.appendChar('r', &state);
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Let otherPunctuators be the string-concatenation of ",-=<>#&!%:;@~'`" and the code unit 0x0022 (QUOTATION MARK).
|
||||
// 4. Let toEscape be StringToCodePoints(otherPunctuators).
|
||||
// 5. If toEscape contains cp, cp is matched by either WhiteSpace or LineTerminator, or cp has the same numeric value as a leading surrogate or trailing surrogate, then
|
||||
if (isOtherPunctuators(cp) || EscargotLexer::isWhiteSpaceOrLineTerminator(cp) || U16_IS_LEAD(cp) || U16_IS_TRAIL(cp)) {
|
||||
// a. Let cpNum be the numeric value of cp.
|
||||
// b. If cpNum ≤ 0xFF, then
|
||||
if (cp < 0xff) {
|
||||
// i. Let hex be Number::toString(𝔽(cpNum), 16).
|
||||
// ii. Return the string-concatenation of the code unit 0x005C (REVERSE SOLIDUS), "x", and StringPad(hex, 2, "0", start).
|
||||
auto cd = charToHexCode(static_cast<char>(cp), false);
|
||||
builder.appendChar(static_cast<char>(0x5C), &state);
|
||||
builder.appendChar('x', &state);
|
||||
if (cd.first) {
|
||||
builder.appendChar(cd.first, &state);
|
||||
} else {
|
||||
// check
|
||||
builder.appendChar('0', &state);
|
||||
}
|
||||
builder.appendChar(cd.second, &state);
|
||||
return;
|
||||
}
|
||||
// c. Let escaped be the empty String.
|
||||
// d. Let codeUnits be UTF16EncodeCodePoint(cp).
|
||||
// e. For each code unit cu of codeUnits, do
|
||||
// i. Set escaped to the string-concatenation of escaped and UnicodeEscape(cu).
|
||||
// f. Return escaped.
|
||||
|
||||
auto r = utf16EncodeCodePoint(cp);
|
||||
if (r.second) {
|
||||
unicodeEscape(state, r.first, builder);
|
||||
unicodeEscape(state, r.second, builder);
|
||||
} else {
|
||||
unicodeEscape(state, r.first, builder);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 6. Return UTF16EncodeCodePoint(cp).
|
||||
utf16EncodeCodePoint(state, cp, builder);
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-regexp.escape
|
||||
static Value builtinRegExpEscape(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// 1. If S is not a String, throw a TypeError exception.
|
||||
if (!argv[0].isString()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "First parameter must be String");
|
||||
}
|
||||
String* S = argv[0].asString();
|
||||
// 2. Let escaped be the empty String.
|
||||
// 3. Let cpList be StringToCodePoints(S).
|
||||
// 4. For each code point cp of cpList, do
|
||||
size_t index = 0;
|
||||
LargeStringBuilder builder;
|
||||
while (index < S->length()) {
|
||||
auto cp = S->codePointAt(index);
|
||||
// a. If escaped is the empty String and cp is matched by either DecimalDigit or AsciiLetter, then
|
||||
if ((builder.contentLength() == 0) && cp.codePoint < 128 && isASCIIAlphanumeric(cp.codePoint)) {
|
||||
// i. NOTE: Escaping a leading digit ensures that output corresponds with pattern text which may be used after a \0 character escape or a DecimalEscape such as \1 and still match S rather than be interpreted as an extension of the preceding escape sequence. Escaping a leading ASCII letter does the same for the context after \c.
|
||||
// ii. Let numericValue be the numeric value of cp.
|
||||
char numericValue = static_cast<char>(cp.codePoint);
|
||||
// iii. Let hex be Number::toString(𝔽(numericValue), 16).
|
||||
// iv. Assert: The length of hex is 2.
|
||||
// v. Set escaped to the string-concatenation of the code unit 0x005C (REVERSE SOLIDUS), "x", and hex.
|
||||
auto cd = charToHexCode(numericValue, false);
|
||||
builder.appendChar(static_cast<char>(0x5C), &state);
|
||||
builder.appendChar('x', &state);
|
||||
builder.appendChar(cd.first, &state);
|
||||
builder.appendChar(cd.second, &state);
|
||||
} else {
|
||||
// b. Else,
|
||||
// i. Set escaped to the string-concatenation of escaped and EncodeForRegExpEscape(cp).
|
||||
encodeForRegExpEscape(state, cp.codePoint, builder);
|
||||
}
|
||||
|
||||
|
||||
index += cp.codeUnitCount;
|
||||
}
|
||||
// 5. Return escaped.
|
||||
return builder.finalize(&state);
|
||||
}
|
||||
|
||||
static Value builtinRegExpStringIteratorNext(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
if (!thisValue.isObject() || !thisValue.asObject()->isRegExpStringIteratorObject()) {
|
||||
|
|
@ -997,7 +744,7 @@ std::pair<Value, bool> RegExpStringIteratorObject::advance(ExecutionState& state
|
|||
if (global) {
|
||||
String* matchStr = match.asObject()->get(state, ObjectPropertyName(state, Value(0))).value(state, match).toString(state);
|
||||
if (matchStr->length() == 0) {
|
||||
// 21.2.5.6.8.g.iv.5
|
||||
//21.2.5.6.8.g.iv.5
|
||||
uint64_t thisIndex = r->get(state, ObjectPropertyName(state, state.context()->staticStrings().lastIndex)).value(state, r).toLength(state);
|
||||
uint64_t nextIndex = s->advanceStringIndex(thisIndex, unicode);
|
||||
r->setThrowsException(state, state.context()->staticStrings().lastIndex, Value(nextIndex), r);
|
||||
|
|
@ -1010,9 +757,12 @@ std::pair<Value, bool> RegExpStringIteratorObject::advance(ExecutionState& state
|
|||
|
||||
void GlobalObject::initializeRegExp(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->regexp(); }, nullptr);
|
||||
return self->asGlobalObject()->regexp();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().RegExp), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
@ -1079,14 +829,6 @@ void GlobalObject::installRegExp(ExecutionState& state)
|
|||
|
||||
REGEXP_LEGACY_DOLLAR_NUMBER_FEATURES(DEFINE_LEGACY_DOLLAR_NUMBER_ATTR);
|
||||
|
||||
|
||||
{
|
||||
auto fn = new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().escape, builtinRegExpEscape, 1, NativeFunctionInfo::Strict));
|
||||
m_regexp->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().escape),
|
||||
ObjectPropertyDescriptor(fn,
|
||||
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
}
|
||||
|
||||
m_regexpPrototype = new RegExpPrototypeObject(state);
|
||||
m_regexpPrototype->setGlobalIntrinsicObject(state, true);
|
||||
|
||||
|
|
@ -1126,13 +868,6 @@ void GlobalObject::installRegExp(ExecutionState& state)
|
|||
m_regexpPrototype->directDefineOwnProperty(state, ObjectPropertyName(state, strings->multiline), desc);
|
||||
}
|
||||
|
||||
{
|
||||
Value getter = new NativeFunctionObject(state, NativeFunctionInfo(strings->getHasIndices, builtinRegExpHasIndicesGetter, 0, NativeFunctionInfo::Strict));
|
||||
JSGetterSetter gs(getter, Value());
|
||||
ObjectPropertyDescriptor desc(gs, ObjectPropertyDescriptor::ConfigurablePresent);
|
||||
m_regexpPrototype->directDefineOwnProperty(state, ObjectPropertyName(state, strings->hasIndices), desc);
|
||||
}
|
||||
|
||||
{
|
||||
Value getter = new NativeFunctionObject(state, NativeFunctionInfo(strings->getSource, builtinRegExpSourceGetter, 0, NativeFunctionInfo::Strict));
|
||||
JSGetterSetter gs(getter, Value());
|
||||
|
|
@ -1154,13 +889,6 @@ void GlobalObject::installRegExp(ExecutionState& state)
|
|||
m_regexpPrototype->directDefineOwnProperty(state, ObjectPropertyName(state, strings->unicode), desc);
|
||||
}
|
||||
|
||||
{
|
||||
Value getter = new NativeFunctionObject(state, NativeFunctionInfo(strings->getUnicodeSets, builtinRegExpUnicodeSetsGetter, 0, NativeFunctionInfo::Strict));
|
||||
JSGetterSetter gs(getter, Value());
|
||||
ObjectPropertyDescriptor desc(gs, ObjectPropertyDescriptor::ConfigurablePresent);
|
||||
m_regexpPrototype->directDefineOwnProperty(state, ObjectPropertyName(state, strings->unicodeSets), desc);
|
||||
}
|
||||
|
||||
m_regexpPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->constructor), ObjectPropertyDescriptor(m_regexp, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_regexp->setFunctionPrototype(state, m_regexpPrototype);
|
||||
|
|
|
|||
|
|
@ -154,467 +154,6 @@ static Value builtinSetForEach(ExecutionState& state, Value thisValue, size_t ar
|
|||
return Value();
|
||||
}
|
||||
|
||||
// https://tc39.es/proposal-set-methods/#sec-getsetrecord
|
||||
struct SetRecord {
|
||||
Object* set;
|
||||
size_t size;
|
||||
Value has;
|
||||
Value keys;
|
||||
};
|
||||
|
||||
static SetRecord getSetRecord(ExecutionState& state, const Value& objInput)
|
||||
{
|
||||
// 1. If obj is not an Object, throw a TypeError exception.
|
||||
if (!objInput.isObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::GlobalObject_FirstArgumentNotObject);
|
||||
}
|
||||
Object* obj = objInput.asObject();
|
||||
// 2. Let rawSize be ? Get(obj, "size").
|
||||
auto rawSize = obj->get(state, ObjectPropertyName(state.context()->staticStrings().size)).value(state, obj);
|
||||
// 3. Let numSize be ? ToNumber(rawSize).
|
||||
// 4. NOTE: If rawSize is undefined, then numSize will be NaN.
|
||||
auto numSize = rawSize.toNumber(state);
|
||||
// 5. If numSize is NaN, throw a TypeError exception.
|
||||
if (std::isnan(numSize)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Invalid obj.size");
|
||||
}
|
||||
// 6. Let intSize be ! ToIntegerOrInfinity(numSize).
|
||||
auto intSize = Value(Value::DoubleToIntConvertibleTestNeeds, numSize).toInteger(state);
|
||||
// 7. If intSize < 0, throw a RangeError exception.
|
||||
if (intSize < 0) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Invalid obj.size");
|
||||
}
|
||||
// 8. Let has be ? Get(obj, "has").
|
||||
auto has = obj->get(state, ObjectPropertyName(state.context()->staticStrings().has)).value(state, obj);
|
||||
// 9. If IsCallable(has) is false, throw a TypeError exception.
|
||||
if (!has.isCallable()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "obj.has is not callable");
|
||||
}
|
||||
// 10. Let keys be ? Get(obj, "keys").
|
||||
auto keys = obj->get(state, ObjectPropertyName(state.context()->staticStrings().keys)).value(state, obj);
|
||||
// 11. If IsCallable(keys) is false, throw a TypeError exception.
|
||||
if (!keys.isCallable()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "obj.keys is not callable");
|
||||
}
|
||||
// 12. Return a new Set Record { [[Set]]: obj, [[Size]]: intSize, [[Has]]: has, [[Keys]]: keys }.
|
||||
return { obj, static_cast<size_t>(intSize), has, keys };
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-setdataindex
|
||||
static size_t setDataIndex(ExecutionState& state, const SetObject::SetObjectData& setData, Value value)
|
||||
{
|
||||
// 1. Set value to CanonicalizeKeyedCollectionKey(value).
|
||||
value = value.toCanonicalizeKeyedCollectionKey(state);
|
||||
// 2. Let size be the number of elements in setData.
|
||||
size_t size = setData.size();
|
||||
// 3. Let index be 0.
|
||||
size_t index = 0;
|
||||
// 4. Repeat, while index < size,
|
||||
while (index < size) {
|
||||
// a. Let e be setData[index].
|
||||
auto e = setData[index];
|
||||
// b. If e is not empty and e is SameValueZero(e, value), then
|
||||
if (!e.isEmpty() && value.equalsToByTheSameValueZeroAlgorithm(state, Value(e))) {
|
||||
// i. Return index.
|
||||
return index;
|
||||
}
|
||||
// c. Set index to index + 1.
|
||||
index++;
|
||||
}
|
||||
// 5. Return not-found.
|
||||
return SIZE_MAX;
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-setdatahas
|
||||
static bool setDataHas(ExecutionState& state, const SetObject::SetObjectData& setData, const Value& value)
|
||||
{
|
||||
return setDataIndex(state, setData, value) != SIZE_MAX;
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-setdatasize
|
||||
static size_t setDataSize(ExecutionState& state, const SetObject::SetObjectData& setData)
|
||||
{
|
||||
// 1. Let count be 0.
|
||||
// 2. For each element e of setData, do
|
||||
// a. If e is not empty, set count to count + 1.
|
||||
// 3. Return count.
|
||||
size_t count = 0;
|
||||
for (const auto& d : setData) {
|
||||
if (!d.isEmpty()) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-set.prototype.union
|
||||
static Value builtinSetUnion(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
RESOLVE_THIS_BINDING_TO_SET(O, Set, stringUnion);
|
||||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
SetRecord otherRec = getSetRecord(state, argv[0]);
|
||||
// 4. Let keysIter be ? GetIteratorFromMethod(otherRec.[[Set]], otherRec.[[Keys]]).
|
||||
auto keysIter = IteratorObject::getIteratorFromMethod(state, otherRec.set, otherRec.keys);
|
||||
// 5. Let resultSetData be a copy of O.[[SetData]].
|
||||
SetObject::SetObjectData resultSetData = O->asSetObject()->storage();
|
||||
// 6. Let next be true.
|
||||
// 7. Repeat, while next is not false,
|
||||
while (true) {
|
||||
// a. Set next to ? IteratorStep(keysIter).
|
||||
Optional<Object*> next = IteratorObject::iteratorStep(state, keysIter);
|
||||
// b. If next is not false, then
|
||||
if (next) {
|
||||
// i. Let nextValue be ? IteratorValue(next).
|
||||
auto nextValue = IteratorObject::iteratorValue(state, next.value());
|
||||
// Set nextValue to CanonicalizeKeyedCollectionKey(nextValue).
|
||||
nextValue = nextValue.toCanonicalizeKeyedCollectionKey(state);
|
||||
// ii. If SetDataHas(resultSetData, nextValue) is false, then
|
||||
if (!setDataHas(state, resultSetData, nextValue)) {
|
||||
// 1. Append nextValue to resultSetData.
|
||||
resultSetData.pushBack(nextValue);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 8. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).
|
||||
// 9. Set result.[[SetData]] to resultSetData.
|
||||
// 10. Return result.
|
||||
return new SetObject(state, state.context()->globalObject()->setPrototypeObject(), std::move(resultSetData));
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-set.prototype.intersection
|
||||
static Value builtinSetIntersection(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
RESOLVE_THIS_BINDING_TO_SET(O, Set, intersection);
|
||||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
SetRecord otherRec = getSetRecord(state, argv[0]);
|
||||
// 4. Let resultSetData be a new empty List.
|
||||
SetObject::SetObjectData resultSetData;
|
||||
|
||||
// 5. If SetDataSize(O.[[SetData]]) ≤ otherRec.[[Size]], then
|
||||
if (setDataSize(state, O->storage()) <= otherRec.size) {
|
||||
// a. Let thisSize be the number of elements in O.[[SetData]].
|
||||
auto thisSize = O->storage().size();
|
||||
// b. Let index be 0.
|
||||
size_t index = 0;
|
||||
// c. Repeat, while index < thisSize
|
||||
while (index < thisSize) {
|
||||
// i. Let e be O.[[SetData]][index].
|
||||
Value e = O->storage()[index];
|
||||
// ii. Set index to index + 1.
|
||||
index++;
|
||||
// iii. If e is not empty, then
|
||||
if (!e.isEmpty()) {
|
||||
// 1. Let inOther be ToBoolean(? Call(otherRec.[[Has]], otherRec.[[SetObject]], « e »)).
|
||||
Value argv = e;
|
||||
bool inOther = Object::call(state, otherRec.has, otherRec.set, 1, &argv).toBoolean();
|
||||
// 2. If inOther is true, then
|
||||
if (inOther) {
|
||||
// a. NOTE: It is possible for earlier calls to otherRec.[[Has]] to remove and re-add an element of O.[[SetData]], which can cause the same element to be visited twice during this iteration.
|
||||
// b. If SetDataHas(resultSetData, e) is false, then
|
||||
if (!setDataHas(state, resultSetData, e)) {
|
||||
// i. Append e to resultSetData.
|
||||
resultSetData.pushBack(e);
|
||||
}
|
||||
// 3. NOTE: The number of elements in O.[[SetData]] may have increased during execution of otherRec.[[Has]].
|
||||
// 4. Set thisSize to the number of elements in O.[[SetData]].
|
||||
thisSize = O->storage().size();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Else,
|
||||
// a. Let keysIter be ? GetIteratorFromMethod(otherRec.[[SetObject]], otherRec.[[Keys]]).
|
||||
auto keysIter = IteratorObject::getIteratorFromMethod(state, otherRec.set, otherRec.keys);
|
||||
// b. Let next be not-started.
|
||||
// c. Repeat, while next is not done,
|
||||
while (true) {
|
||||
// i. Set next to ? IteratorStepValue(keysIter).
|
||||
auto next = IteratorObject::iteratorStepValue(state, keysIter);
|
||||
// ii. If next is not done, then
|
||||
if (next) {
|
||||
// 1. Set next to CanonicalizeKeyedCollectionKey(next).
|
||||
next = next.value().toCanonicalizeKeyedCollectionKey(state);
|
||||
// 2. Let inThis be SetDataHas(O.[[SetData]], next).
|
||||
bool inThis = setDataHas(state, O->storage(), next.value());
|
||||
// 3. If inThis is true, then
|
||||
if (inThis) {
|
||||
// a. NOTE: Because other is an arbitrary object, it is possible for its "keys" iterator to produce the same value more than once.
|
||||
// b. If SetDataHas(resultSetData, next) is false, then
|
||||
if (!setDataHas(state, resultSetData, next.value())) {
|
||||
// i. Append next to resultSetData.
|
||||
resultSetData.pushBack(next.value());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).
|
||||
// 8. Set result.[[SetData]] to resultSetData.
|
||||
// 9. Return result.
|
||||
return new SetObject(state, state.context()->globalObject()->setPrototypeObject(), std::move(resultSetData));
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-set.prototype.isdisjointfrom
|
||||
static Value builtinSetIsDisjointFrom(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
RESOLVE_THIS_BINDING_TO_SET(O, Set, isDisjointFrom);
|
||||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
SetRecord otherRec = getSetRecord(state, argv[0]);
|
||||
// 4. If SetDataSize(O.[[SetData]]) ≤ otherRec.[[Size]], then
|
||||
if (setDataSize(state, O->storage()) <= otherRec.size) {
|
||||
// a. Let thisSize be the number of elements in O.[[SetData]].
|
||||
auto thisSize = O->storage().size();
|
||||
// b. Let index be 0.
|
||||
size_t index = 0;
|
||||
// c. Repeat, while index < thisSize,
|
||||
while (index < thisSize) {
|
||||
// i. Let e be O.[[SetData]][index].
|
||||
Value e = O->storage()[index];
|
||||
// ii. Set index to index + 1.
|
||||
index++;
|
||||
// iii. If e is not empty, then
|
||||
if (!e.isEmpty()) {
|
||||
// 1. Let inOther be ToBoolean(? Call(otherRec.[[Has]], otherRec.[[SetObject]], « e »)).
|
||||
Value argv = e;
|
||||
bool inOther = Object::call(state, otherRec.has, otherRec.set, 1, &argv).toBoolean();
|
||||
// 2. If inOther is true, return false.
|
||||
if (inOther) {
|
||||
return Value(false);
|
||||
}
|
||||
// 3. NOTE: The number of elements in O.[[SetData]] may have increased during execution of otherRec.[[Has]].
|
||||
// 4. Set thisSize to the number of elements in O.[[SetData]].
|
||||
thisSize = O->storage().size();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 5. Else,
|
||||
// a. Let keysIter be ? GetIteratorFromMethod(otherRec.[[SetObject]], otherRec.[[Keys]]).
|
||||
auto keysIter = IteratorObject::getIteratorFromMethod(state, otherRec.set, otherRec.keys);
|
||||
// b. Let next be not-started.
|
||||
// c. Repeat, while next is not done,
|
||||
while (true) {
|
||||
// i. Set next to ? IteratorStepValue(keysIter).
|
||||
auto next = IteratorObject::iteratorStepValue(state, keysIter);
|
||||
// ii. If next is not done, then
|
||||
if (next) {
|
||||
// 1. If SetDataHas(O.[[SetData]], next) is true, then
|
||||
if (setDataHas(state, O->storage(), next.value())) {
|
||||
// a. Perform ? IteratorClose(keysIter, NormalCompletion(unused)).
|
||||
IteratorObject::iteratorClose(state, keysIter, Value(), false);
|
||||
// b. Return false.
|
||||
return Value(false);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 6. Return true.
|
||||
return Value(true);
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-set.prototype.difference
|
||||
static Value builtinSetDifference(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
RESOLVE_THIS_BINDING_TO_SET(O, Set, difference);
|
||||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
SetRecord otherRec = getSetRecord(state, argv[0]);
|
||||
// 4. Let resultSetData be a copy of O.[[SetData]].
|
||||
SetObject::SetObjectData resultSetData = O->storage();
|
||||
// 5. If SetDataSize(O.[[SetData]]) ≤ otherRec.[[Size]], then
|
||||
if (setDataSize(state, O->storage()) <= otherRec.size) {
|
||||
// a. Let thisSize be the number of elements in O.[[SetData]].
|
||||
auto thisSize = O->storage().size();
|
||||
// b. Let index be 0.
|
||||
size_t index = 0;
|
||||
// c. Repeat, while index < thisSize,
|
||||
while (index < thisSize) {
|
||||
// i. Let e be resultSetData[index].
|
||||
Value e = resultSetData[index];
|
||||
// ii. If e is not empty, then
|
||||
if (!e.isEmpty()) {
|
||||
// 1. Let inOther be ToBoolean(? Call(otherRec.[[Has]], otherRec.[[SetObject]], « e »)).
|
||||
Value argv = e;
|
||||
bool inOther = Object::call(state, otherRec.has, otherRec.set, 1, &argv).toBoolean();
|
||||
// 2. If inOther is true, then
|
||||
if (inOther) {
|
||||
// a. Set resultSetData[index] to empty.
|
||||
resultSetData[index] = Value(Value::EmptyValue);
|
||||
}
|
||||
}
|
||||
// iii. Set index to index + 1.
|
||||
index++;
|
||||
}
|
||||
} else {
|
||||
// 6. Else,
|
||||
// a. Let keysIter be ? GetIteratorFromMethod(otherRec.[[SetObject]], otherRec.[[Keys]]).
|
||||
auto keysIter = IteratorObject::getIteratorFromMethod(state, otherRec.set, otherRec.keys);
|
||||
// b. Let next be not-started.
|
||||
// c. Repeat, while next is not done,
|
||||
while (true) {
|
||||
// i. Set next to ? IteratorStepValue(keysIter).
|
||||
auto next = IteratorObject::iteratorStepValue(state, keysIter);
|
||||
// ii. If next is not done, then
|
||||
if (next) {
|
||||
// 1. Set next to CanonicalizeKeyedCollectionKey(next).
|
||||
next = next.value().toCanonicalizeKeyedCollectionKey(state);
|
||||
// 2. Let valueIndex be SetDataIndex(resultSetData, next).
|
||||
auto valueIndex = setDataIndex(state, resultSetData, next.value());
|
||||
// 3. If valueIndex is not not-found, then
|
||||
if (valueIndex != SIZE_MAX) {
|
||||
// a. Set resultSetData[valueIndex] to empty.
|
||||
resultSetData[valueIndex] = Value(Value::EmptyValue);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 7. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).
|
||||
// 8. Set result.[[SetData]] to resultSetData.
|
||||
// 9. Return result.
|
||||
return new SetObject(state, state.context()->globalObject()->setPrototypeObject(), std::move(resultSetData));
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-set.prototype.issubsetof
|
||||
static Value builtinSetIsSubsetOf(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
RESOLVE_THIS_BINDING_TO_SET(O, Set, isSubsetOf);
|
||||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
SetRecord otherRec = getSetRecord(state, argv[0]);
|
||||
// 4. If SetDataSize(O.[[SetData]]) > otherRec.[[Size]], return false.
|
||||
if (setDataSize(state, O->storage()) > otherRec.size) {
|
||||
return Value(false);
|
||||
}
|
||||
|
||||
// 5. Let thisSize be the number of elements in O.[[SetData]].
|
||||
size_t thisSize = O->storage().size();
|
||||
// 6. Let index be 0.
|
||||
size_t index = 0;
|
||||
// 7. Repeat, while index < thisSize,
|
||||
while (index < thisSize) {
|
||||
// a. Let e be O.[[SetData]][index].
|
||||
Value e = O->storage()[index];
|
||||
// b. Set index to index + 1.
|
||||
index++;
|
||||
// c. If e is not empty, then
|
||||
if (!e.isEmpty()) {
|
||||
// i. Let inOther be ToBoolean(? Call(otherRec.[[Has]], otherRec.[[SetObject]], « e »)).
|
||||
Value argv = e;
|
||||
bool inOther = Object::call(state, otherRec.has, otherRec.set, 1, &argv).toBoolean();
|
||||
// ii. If inOther is false, return false.
|
||||
if (!inOther) {
|
||||
return Value(false);
|
||||
}
|
||||
// iii. NOTE: The number of elements in O.[[SetData]] may have increased during execution of otherRec.[[Has]].
|
||||
// iv. Set thisSize to the number of elements in O.[[SetData]].
|
||||
thisSize = O->storage().size();
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Return true.
|
||||
return Value(true);
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-set.prototype.issupersetof
|
||||
static Value builtinSetIsSupersetOf(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
RESOLVE_THIS_BINDING_TO_SET(O, Set, isSupersetOf);
|
||||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
SetRecord otherRec = getSetRecord(state, argv[0]);
|
||||
// 4. If SetDataSize(O.[[SetData]]) < otherRec.[[Size]], return false.
|
||||
if (setDataSize(state, O->storage()) < otherRec.size) {
|
||||
return Value(false);
|
||||
}
|
||||
// 5. Let keysIter be ? GetIteratorFromMethod(otherRec.[[SetObject]], otherRec.[[Keys]]).
|
||||
auto keysIter = IteratorObject::getIteratorFromMethod(state, otherRec.set, otherRec.keys);
|
||||
// 6. Let next be not-started.
|
||||
// 7. Repeat, while next is not done,
|
||||
while (true) {
|
||||
// a. Set next to ? IteratorStepValue(keysIter).
|
||||
auto next = IteratorObject::iteratorStepValue(state, keysIter);
|
||||
// b. If next is not done, then
|
||||
if (next) {
|
||||
// i. If SetDataHas(O.[[SetData]], next) is false, then
|
||||
if (!setDataHas(state, O->storage(), next.value())) {
|
||||
// 1. Perform ? IteratorClose(keysIter, NormalCompletion(unused)).
|
||||
IteratorObject::iteratorClose(state, keysIter, Value(), false);
|
||||
// 2. Return false.
|
||||
return Value(false);
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 8. Return true.
|
||||
return Value(true);
|
||||
}
|
||||
|
||||
static Value builtinSetSymmetricDifference(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// 1. Let O be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(O, [[SetData]]).
|
||||
RESOLVE_THIS_BINDING_TO_SET(O, Set, symmetricDifference);
|
||||
// 3. Let otherRec be ? GetSetRecord(other).
|
||||
SetRecord otherRec = getSetRecord(state, argv[0]);
|
||||
// 4. Let keysIter be ? GetIteratorFromMethod(otherRec.[[SetObject]], otherRec.[[Keys]]).
|
||||
auto keysIter = IteratorObject::getIteratorFromMethod(state, otherRec.set, otherRec.keys);
|
||||
// 5. Let resultSetData be a copy of O.[[SetData]].
|
||||
SetObject::SetObjectData resultSetData = O->asSetObject()->storage();
|
||||
// 6. Let next be not-started.
|
||||
// 7. Repeat, while next is not done,
|
||||
while (true) {
|
||||
// a. Set next to ? IteratorStepValue(keysIter).
|
||||
auto next = IteratorObject::iteratorStepValue(state, keysIter);
|
||||
// b. If next is not done, then
|
||||
if (next) {
|
||||
// i. Set next to CanonicalizeKeyedCollectionKey(next).
|
||||
next = next.value().toCanonicalizeKeyedCollectionKey(state);
|
||||
// ii. Let resultIndex be SetDataIndex(resultSetData, next).
|
||||
auto resultIndex = setDataIndex(state, resultSetData, next.value());
|
||||
// iii. If resultIndex is not-found, let alreadyInResult be false. Otherwise let alreadyInResult be true.
|
||||
bool alreadyInResult = resultIndex != SIZE_MAX;
|
||||
// iv. If SetDataHas(O.[[SetData]], next) is true, then
|
||||
if (setDataHas(state, O->storage(), next.value())) {
|
||||
// 1. If alreadyInResult is true, set resultSetData[resultIndex] to empty.
|
||||
if (alreadyInResult) {
|
||||
resultSetData[resultIndex] = Value(Value::EmptyValue);
|
||||
}
|
||||
} else {
|
||||
// v. Else,
|
||||
// 1. If alreadyInResult is false, append next to resultSetData.
|
||||
if (!alreadyInResult) {
|
||||
resultSetData.pushBack(next.value());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 8. Let result be OrdinaryObjectCreate(%Set.prototype%, « [[SetData]] »).
|
||||
// 9. Set result.[[SetData]] to resultSetData.
|
||||
// 10. Return result.
|
||||
return new SetObject(state, state.context()->globalObject()->setPrototypeObject(), std::move(resultSetData));
|
||||
}
|
||||
|
||||
static Value builtinSetValues(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
RESOLVE_THIS_BINDING_TO_SET(S, Set, values);
|
||||
|
|
@ -644,9 +183,12 @@ static Value builtinSetIteratorNext(ExecutionState& state, Value thisValue, size
|
|||
|
||||
void GlobalObject::initializeSet(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->set(); }, nullptr);
|
||||
return self->asGlobalObject()->set();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().Set), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
@ -684,27 +226,6 @@ void GlobalObject::installSet(ExecutionState& state)
|
|||
m_setPrototypeObject->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().forEach),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().forEach, builtinSetForEach, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_setPrototypeObject->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().stringUnion),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().stringUnion, builtinSetUnion, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_setPrototypeObject->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().intersection),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().intersection, builtinSetIntersection, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_setPrototypeObject->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().isDisjointFrom),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().isDisjointFrom, builtinSetIsDisjointFrom, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_setPrototypeObject->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().difference),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().difference, builtinSetDifference, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_setPrototypeObject->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().isSubsetOf),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().isSubsetOf, builtinSetIsSubsetOf, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_setPrototypeObject->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().isSupersetOf),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().isSupersetOf, builtinSetIsSupersetOf, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_setPrototypeObject->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().symmetricDifference),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().symmetricDifference, builtinSetSymmetricDifference, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
auto valuesFn = new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().values, builtinSetValues, 0, NativeFunctionInfo::Strict));
|
||||
auto values = ObjectPropertyDescriptor(valuesFn, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent));
|
||||
m_setPrototypeObject->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().values), values);
|
||||
|
|
|
|||
|
|
@ -1,173 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2025-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "Escargot.h"
|
||||
#include "interpreter/ByteCode.h"
|
||||
#include "interpreter/ByteCodeGenerator.h"
|
||||
#include "interpreter/ByteCodeInterpreter.h"
|
||||
#include "runtime/Global.h"
|
||||
#include "runtime/GlobalObject.h"
|
||||
#include "runtime/Context.h"
|
||||
#include "runtime/Environment.h"
|
||||
#include "runtime/EnvironmentRecord.h"
|
||||
#include "runtime/ExtendedNativeFunctionObject.h"
|
||||
#include "runtime/VMInstance.h"
|
||||
#include "runtime/ShadowRealmObject.h"
|
||||
#include "runtime/NativeFunctionObject.h"
|
||||
#include "runtime/ScriptFunctionObject.h"
|
||||
#include "parser/Script.h"
|
||||
#include "parser/ScriptParser.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
#if defined(ENABLE_SHADOWREALM)
|
||||
|
||||
// https://tc39.es/proposal-shadowrealm/#sec-shadowrealm
|
||||
static Value builtinShadowRealmConstructor(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// If NewTarget is undefined, throw a TypeError exception.
|
||||
if (!newTarget) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, ErrorObject::Messages::GlobalObject_ConstructorRequiresNew);
|
||||
}
|
||||
|
||||
// Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%ShadowRealm.prototype%", « [[ShadowRealm]] »).
|
||||
Object* proto = Object::getPrototypeFromConstructor(state, newTarget.value(), [](ExecutionState& state, Context* constructorRealm) -> Object* {
|
||||
return constructorRealm->globalObject()->shadowRealmPrototype();
|
||||
});
|
||||
// Let callerContext be the running execution context.
|
||||
// Perform ? InitializeHostDefinedRealm().
|
||||
// Let innerContext be the running execution context.
|
||||
Context* innerContext = new Context(state.context()->vmInstance());
|
||||
// Remove innerContext from the execution context stack and restore callerContext as the running execution context.
|
||||
// Let realmRec be the Realm of innerContext.
|
||||
// Set O.[[ShadowRealm]] to realmRec.
|
||||
auto targetState = &state;
|
||||
Optional<Script*> referrer;
|
||||
while (targetState) {
|
||||
auto callee = targetState->resolveCallee();
|
||||
if (callee && !callee->isNativeFunctionObject()) {
|
||||
auto fn = targetState->mostNearestFunctionLexicalEnvironment()->record()->asDeclarativeEnvironmentRecord()->asFunctionEnvironmentRecord()->functionObject();
|
||||
referrer = fn->codeBlock()->asInterpretedCodeBlock()->script();
|
||||
break;
|
||||
}
|
||||
auto outerScript = targetState->resolveOuterScript();
|
||||
if (outerScript && outerScript->topCodeBlock() && !outerScript->topCodeBlock()->isEvalCode()) {
|
||||
referrer = outerScript;
|
||||
break;
|
||||
}
|
||||
targetState = targetState->parent();
|
||||
}
|
||||
if (!referrer) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Shadow Realm needs Javascript code as importValue referrer");
|
||||
}
|
||||
ShadowRealmObject* O = new ShadowRealmObject(state, proto, innerContext, referrer.value());
|
||||
// Perform ? HostInitializeShadowRealm(realmRec, innerContext, O).
|
||||
// Assert: realmRec.[[GlobalObject]] is an ordinary object.
|
||||
ASSERT(innerContext->globalObject()->isOrdinary());
|
||||
// Return O.
|
||||
return O;
|
||||
}
|
||||
|
||||
static Value builtinShadowRealmEvaluate(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// Let O be the this value.
|
||||
const Value& O = thisValue;
|
||||
// Perform ? ValidateShadowRealmObject(O).
|
||||
if (!O.isObject() || !O.asObject()->isShadowRealmObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "this value must be a ShadowRealm object");
|
||||
}
|
||||
// If sourceText is not a String, throw a TypeError exception.
|
||||
if (!argv[0].isString()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "sourceText must be a String");
|
||||
}
|
||||
// Let callerRealm be the current Realm Record.
|
||||
Context* callerRealm = state.context();
|
||||
// Let evalRealm be O.[[ShadowRealm]].
|
||||
Context* evalRealm = O.asObject()->asShadowRealmObject()->realmContext();
|
||||
// Return ? PerformShadowRealmEval(sourceText, callerRealm, evalRealm).
|
||||
return O.asObject()->asShadowRealmObject()->eval(state, argv[0].asString(), callerRealm);
|
||||
}
|
||||
|
||||
static Value builtinShadowRealmImportValue(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// Let O be the this value.
|
||||
const Value& O = thisValue;
|
||||
// Perform ? ValidateShadowRealmObject(O).
|
||||
if (!O.isObject() || !O.asObject()->isShadowRealmObject()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "this value must be a ShadowRealm object");
|
||||
}
|
||||
|
||||
// Let specifierString be ? ToString(specifier).
|
||||
auto specifierString = argv[0].toString(state);
|
||||
// If exportName is not a String, throw a TypeError exception.
|
||||
if (!argv[1].isString()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "exportName must be a String");
|
||||
}
|
||||
// Let callerRealm be the current Realm Record.
|
||||
Context* callerRealm = state.context();
|
||||
// Let evalRealm be O.[[ShadowRealm]].
|
||||
Context* evalRealm = O.asObject()->asShadowRealmObject()->realmContext();
|
||||
// Return ShadowRealmImportValue(specifierString, exportName, callerRealm, evalRealm).
|
||||
return O.asObject()->asShadowRealmObject()->importValue(state, specifierString, argv[1].asString(), callerRealm);
|
||||
}
|
||||
|
||||
void GlobalObject::initializeShadowRealm(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->shadowRealm(); }, nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().ShadowRealm), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
||||
void GlobalObject::installShadowRealm(ExecutionState& state)
|
||||
{
|
||||
const StaticStrings* strings = &state.context()->staticStrings();
|
||||
|
||||
m_shadowRealm = new NativeFunctionObject(state, NativeFunctionInfo(strings->ShadowRealm, builtinShadowRealmConstructor, 0), NativeFunctionObject::__ForBuiltinConstructor__);
|
||||
m_shadowRealm->setGlobalIntrinsicObject(state);
|
||||
|
||||
m_shadowRealmPrototype = new PrototypeObject(state);
|
||||
m_shadowRealmPrototype->setGlobalIntrinsicObject(state, true);
|
||||
|
||||
m_shadowRealm->setFunctionPrototype(state, m_shadowRealmPrototype);
|
||||
|
||||
m_shadowRealmPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().toStringTag),
|
||||
ObjectPropertyDescriptor(state.context()->staticStrings().ShadowRealm.string(), ObjectPropertyDescriptor::ConfigurablePresent));
|
||||
|
||||
m_shadowRealmPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->evaluate),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->evaluate, builtinShadowRealmEvaluate, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_shadowRealmPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->importValue),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->importValue, builtinShadowRealmImportValue, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
redefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().ShadowRealm),
|
||||
ObjectPropertyDescriptor(m_shadowRealm, (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void GlobalObject::initializeShadowRealm(ExecutionState& state)
|
||||
{
|
||||
// dummy initialize function
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Escargot
|
||||
|
|
@ -37,7 +37,7 @@ static Value builtinSharedArrayBufferConstructor(ExecutionState& state, Value th
|
|||
|
||||
uint64_t byteLength = argv[0].toIndex(state);
|
||||
if (byteLength == Value::InvalidIndexValue) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().SharedArrayBuffer.string(), false, String::emptyString(), ErrorObject::Messages::GlobalObject_FirstArgumentInvalidLength);
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().SharedArrayBuffer.string(), false, String::emptyString, ErrorObject::Messages::GlobalObject_FirstArgumentInvalidLength);
|
||||
}
|
||||
|
||||
Optional<uint64_t> maxByteLength;
|
||||
|
|
@ -48,7 +48,7 @@ static Value builtinSharedArrayBufferConstructor(ExecutionState& state, Value th
|
|||
if (!maxLengthValue.isUndefined()) {
|
||||
maxByteLength = maxLengthValue.toIndex(state);
|
||||
if (UNLIKELY((maxByteLength.value() == Value::InvalidIndexValue) || (byteLength > maxByteLength.value()))) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().SharedArrayBuffer.string(), false, String::emptyString(), ErrorObject::Messages::GlobalObject_InvalidArrayLength);
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, state.context()->staticStrings().SharedArrayBuffer.string(), false, String::emptyString, ErrorObject::Messages::GlobalObject_InvalidArrayLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -136,9 +136,12 @@ static Value builtinSharedArrayBufferSlice(ExecutionState& state, Value thisValu
|
|||
|
||||
void GlobalObject::initializeSharedArrayBuffer(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->sharedArrayBuffer(); }, nullptr);
|
||||
return self->asGlobalObject()->sharedArrayBuffer();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().SharedArrayBuffer), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ namespace Escargot {
|
|||
|
||||
static Value builtinStringConstructor(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
String* s = String::emptyString();
|
||||
String* s = String::emptyString;
|
||||
if (argc > 0) {
|
||||
Value value = argv[0];
|
||||
if (!newTarget.hasValue() && value.isSymbol()) {
|
||||
|
|
@ -194,7 +194,7 @@ static Value builtinStringMatch(ExecutionState& state, Value thisValue, size_t a
|
|||
}
|
||||
|
||||
Value regexp = argv[0];
|
||||
if (regexp.isObject()) {
|
||||
if (!regexp.isUndefinedOrNull()) {
|
||||
Value matcher = Object::getMethod(state, regexp, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().match));
|
||||
if (!matcher.isUndefined()) {
|
||||
Value args[1] = { thisValue };
|
||||
|
|
@ -203,7 +203,7 @@ static Value builtinStringMatch(ExecutionState& state, Value thisValue, size_t a
|
|||
}
|
||||
|
||||
String* S = thisValue.toString(state);
|
||||
RegExpObject* rx = new RegExpObject(state, regexp.isUndefined() ? String::emptyString() : regexp.toString(state), String::emptyString());
|
||||
RegExpObject* rx = new RegExpObject(state, regexp.isUndefined() ? String::emptyString : regexp.toString(state), String::emptyString);
|
||||
Value func = rx->get(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().match)).value(state, rx);
|
||||
Value args[1] = { Value(S) };
|
||||
return Object::call(state, func, rx, 1, args);
|
||||
|
|
@ -216,8 +216,8 @@ static Value builtinStringMatchAll(ExecutionState& state, Value thisValue, size_
|
|||
}
|
||||
|
||||
Value regexp = argv[0];
|
||||
if (regexp.isObject()) {
|
||||
if (regexp.asObject()->isRegExpObject()) {
|
||||
if (!regexp.isUndefinedOrNull()) {
|
||||
if (regexp.isObject() && regexp.asObject()->isRegExpObject()) {
|
||||
String* flags = regexp.asObject()->get(state, ObjectPropertyName(state, state.context()->staticStrings().flags)).value(state, regexp).toString(state);
|
||||
if (flags->find("g") == SIZE_MAX) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, state.context()->staticStrings().String.string(), true, state.context()->staticStrings().match.string(), ErrorObject::Messages::GlobalObject_ThisUndefinedOrNull);
|
||||
|
|
@ -232,7 +232,7 @@ static Value builtinStringMatchAll(ExecutionState& state, Value thisValue, size_
|
|||
String* S = thisValue.toString(state);
|
||||
StringBuilder builder;
|
||||
builder.appendChar('g');
|
||||
RegExpObject* rx = new RegExpObject(state, regexp.isUndefined() ? String::emptyString() : regexp.toString(state), builder.finalize());
|
||||
RegExpObject* rx = new RegExpObject(state, regexp.isUndefined() ? String::emptyString : regexp.toString(state), builder.finalize());
|
||||
Value func = rx->get(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().matchAll)).value(state, rx);
|
||||
Value args[1] = { Value(S) };
|
||||
return Object::call(state, func, rx, 1, args);
|
||||
|
|
@ -328,7 +328,7 @@ static Value builtinStringRepeat(ExecutionState& state, Value thisValue, size_t
|
|||
}
|
||||
|
||||
if (newStringLength == 0) {
|
||||
return String::emptyString();
|
||||
return String::emptyString;
|
||||
}
|
||||
|
||||
repeatCount = static_cast<int32_t>(count);
|
||||
|
|
@ -344,10 +344,9 @@ static Value stringReplaceFastPathHelper(ExecutionState& state, String* string,
|
|||
{
|
||||
ASSERT(string && replaceString);
|
||||
|
||||
auto replaceStringBad = replaceString->bufferAccessData();
|
||||
bool hasDollar = false;
|
||||
for (size_t i = 0; i < replaceStringBad.length; i++) {
|
||||
if (replaceStringBad.charAt(i) == '$') {
|
||||
for (size_t i = 0; i < replaceString->length(); i++) {
|
||||
if (replaceString->charAt(i) == '$') {
|
||||
hasDollar = true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -357,35 +356,36 @@ static Value stringReplaceFastPathHelper(ExecutionState& state, String* string,
|
|||
if (!hasDollar) {
|
||||
// flat replace
|
||||
int32_t matchCount = result.m_matchResults.size();
|
||||
builder.appendSubString(string, 0, result.m_matchResults[0][0].m_start, &state);
|
||||
builder.appendSubString(string, 0, result.m_matchResults[0][0].m_start);
|
||||
for (int32_t i = 0; i < matchCount; i++) {
|
||||
builder.appendString(replaceString, &state);
|
||||
String* res = replaceString;
|
||||
builder.appendString(res);
|
||||
if (i < matchCount - 1) {
|
||||
builder.appendSubString(string, result.m_matchResults[i][0].m_end, result.m_matchResults[i + 1][0].m_start, &state);
|
||||
builder.appendSubString(string, result.m_matchResults[i][0].m_end, result.m_matchResults[i + 1][0].m_start);
|
||||
}
|
||||
}
|
||||
builder.appendSubString(string, result.m_matchResults[matchCount - 1][0].m_end, string->length(), &state);
|
||||
builder.appendSubString(string, result.m_matchResults[matchCount - 1][0].m_end, string->length());
|
||||
} else {
|
||||
// dollar replace
|
||||
int32_t matchCount = result.m_matchResults.size();
|
||||
builder.appendSubString(string, 0, result.m_matchResults[0][0].m_start, &state);
|
||||
builder.appendSubString(string, 0, result.m_matchResults[0][0].m_start);
|
||||
for (int32_t i = 0; i < matchCount; i++) {
|
||||
for (unsigned j = 0; j < replaceStringBad.length; j++) {
|
||||
if (replaceStringBad.charAt(j) == '$' && (j + 1) < replaceStringBad.length) {
|
||||
char16_t c = replaceStringBad.charAt(j + 1);
|
||||
for (unsigned j = 0; j < replaceString->length(); j++) {
|
||||
if (replaceString->charAt(j) == '$' && (j + 1) < replaceString->length()) {
|
||||
char16_t c = replaceString->charAt(j + 1);
|
||||
if (c == '$') {
|
||||
builder.appendChar(replaceStringBad.charAt(j), &state);
|
||||
builder.appendChar(replaceString->charAt(j));
|
||||
} else if (c == '&') {
|
||||
builder.appendSubString(string, result.m_matchResults[i][0].m_start, result.m_matchResults[i][0].m_end, &state);
|
||||
builder.appendSubString(string, result.m_matchResults[i][0].m_start, result.m_matchResults[i][0].m_end);
|
||||
} else if (c == '\'') {
|
||||
builder.appendSubString(string, result.m_matchResults[i][0].m_end, string->length(), &state);
|
||||
builder.appendSubString(string, result.m_matchResults[i][0].m_end, string->length());
|
||||
} else if (c == '`') {
|
||||
builder.appendSubString(string, 0, result.m_matchResults[i][0].m_start, &state);
|
||||
builder.appendSubString(string, 0, result.m_matchResults[i][0].m_start);
|
||||
} else if ('0' <= c && c <= '9') {
|
||||
size_t idx = c - '0';
|
||||
bool usePeek = false;
|
||||
if (j + 2 < replaceStringBad.length) {
|
||||
int peek = replaceStringBad.charAt(j + 2) - '0';
|
||||
if (j + 2 < replaceString->length()) {
|
||||
int peek = replaceString->charAt(j + 2) - '0';
|
||||
if (0 <= peek && peek <= 9) {
|
||||
idx *= 10;
|
||||
idx += peek;
|
||||
|
|
@ -394,33 +394,33 @@ static Value stringReplaceFastPathHelper(ExecutionState& state, String* string,
|
|||
}
|
||||
|
||||
if (idx < result.m_matchResults[i].size() && idx != 0) {
|
||||
builder.appendSubString(string, result.m_matchResults[i][idx].m_start, result.m_matchResults[i][idx].m_end, &state);
|
||||
builder.appendSubString(string, result.m_matchResults[i][idx].m_start, result.m_matchResults[i][idx].m_end);
|
||||
if (usePeek)
|
||||
j++;
|
||||
} else {
|
||||
idx = c - '0';
|
||||
if (idx < result.m_matchResults[i].size() && idx != 0) {
|
||||
builder.appendSubString(string, result.m_matchResults[i][idx].m_start, result.m_matchResults[i][idx].m_end, &state);
|
||||
builder.appendSubString(string, result.m_matchResults[i][idx].m_start, result.m_matchResults[i][idx].m_end);
|
||||
} else {
|
||||
builder.appendChar('$', &state);
|
||||
builder.appendChar(c, &state);
|
||||
builder.appendChar('$');
|
||||
builder.appendChar(c);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
builder.appendChar('$', &state);
|
||||
builder.appendChar(c, &state);
|
||||
builder.appendChar('$');
|
||||
builder.appendChar(c);
|
||||
}
|
||||
j++;
|
||||
} else {
|
||||
builder.appendChar(replaceStringBad.charAt(j), &state);
|
||||
builder.appendChar(replaceString->charAt(j));
|
||||
}
|
||||
}
|
||||
if (i < matchCount - 1) {
|
||||
builder.appendSubString(string, result.m_matchResults[i][0].m_end, result.m_matchResults[i + 1][0].m_start, &state);
|
||||
builder.appendSubString(string, result.m_matchResults[i][0].m_end, result.m_matchResults[i + 1][0].m_start);
|
||||
}
|
||||
}
|
||||
|
||||
builder.appendSubString(string, result.m_matchResults[matchCount - 1][0].m_end, string->length(), &state);
|
||||
builder.appendSubString(string, result.m_matchResults[matchCount - 1][0].m_end, string->length());
|
||||
}
|
||||
|
||||
return builder.finalize(&state);
|
||||
|
|
@ -438,7 +438,7 @@ static Value builtinStringReplace(ExecutionState& state, Value thisValue, size_t
|
|||
bool isSearchValueRegExp = searchValue.isPointerValue() && searchValue.asPointerValue()->isRegExpObject();
|
||||
// we should keep fast-path while performace issue is unresolved
|
||||
bool canUseFastPath = searchValue.isString() || (isSearchValueRegExp && searchValue.asPointerValue()->asRegExpObject()->yarrPatern()->m_captureGroupNames.size() == 0);
|
||||
if (searchValue.isObject()) {
|
||||
if (!searchValue.isUndefinedOrNull()) {
|
||||
Value replacer = Object::getMethod(state, searchValue, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().replace));
|
||||
if (canUseFastPath && isSearchValueRegExp && replacer.isPointerValue() && replacer.asPointerValue() == state.context()->globalObject()->regexpReplaceMethod()) {
|
||||
auto exec = searchValue.asObject()->get(state, ObjectPropertyName(state.context()->staticStrings().exec));
|
||||
|
|
@ -545,7 +545,7 @@ static Value builtinStringReplace(ExecutionState& state, Value thisValue, size_t
|
|||
if (pos == SIZE_MAX) {
|
||||
return Value(string);
|
||||
}
|
||||
String* replStr = String::emptyString();
|
||||
String* replStr = String::emptyString;
|
||||
if (functionalReplace) {
|
||||
Value parameters[3] = { Value(matched), Value(pos), Value(string) };
|
||||
Value replValue = Object::call(state, replaceValue, Value(), 3, parameters);
|
||||
|
|
@ -571,7 +571,8 @@ static Value builtinStringReplaceAll(ExecutionState& state, Value thisValue, siz
|
|||
}
|
||||
Value searchValue = argv[0];
|
||||
Value replaceValue = argv[1];
|
||||
if (searchValue.isObject()) {
|
||||
// If searchValue is neither undefined nor null, then
|
||||
if (!searchValue.isUndefinedOrNull()) {
|
||||
// If isRegExp is true, then
|
||||
if (searchValue.isObject() && searchValue.asObject()->isRegExp(state)) {
|
||||
Value flags = searchValue.asObject()->get(state, ObjectPropertyName(state, state.context()->staticStrings().flags)).value(state, searchValue);
|
||||
|
|
@ -611,7 +612,7 @@ static Value builtinStringReplaceAll(ExecutionState& state, Value thisValue, siz
|
|||
size_t endOfLastMatch = 0;
|
||||
|
||||
StringBuilder builder;
|
||||
String* replacement = String::emptyString();
|
||||
String* replacement = String::emptyString;
|
||||
// For each element p of matchPositions, do
|
||||
for (size_t i = 0; i < matchPositions.size(); i++) {
|
||||
size_t p = matchPositions[i];
|
||||
|
|
@ -641,7 +642,7 @@ static Value builtinStringSearch(ExecutionState& state, Value thisValue, size_t
|
|||
}
|
||||
|
||||
Value regexp = argv[0];
|
||||
if (regexp.isObject()) {
|
||||
if (!regexp.isUndefinedOrNull()) {
|
||||
Value searcher = Object::getMethod(state, regexp, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().search));
|
||||
if (!searcher.isUndefined()) {
|
||||
Value args[1] = { thisValue };
|
||||
|
|
@ -650,7 +651,7 @@ static Value builtinStringSearch(ExecutionState& state, Value thisValue, size_t
|
|||
}
|
||||
|
||||
String* string = thisValue.toString(state);
|
||||
RegExpObject* rx = new RegExpObject(state, regexp.isUndefined() ? String::emptyString() : regexp.toString(state), String::emptyString());
|
||||
RegExpObject* rx = new RegExpObject(state, regexp.isUndefined() ? String::emptyString : regexp.toString(state), String::emptyString);
|
||||
Value func = rx->get(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().search)).value(state, rx);
|
||||
Value args[1] = { Value(string) };
|
||||
return Object::call(state, func, rx, 1, args);
|
||||
|
|
@ -666,7 +667,8 @@ static Value builtinStringSplit(ExecutionState& state, Value thisValue, size_t a
|
|||
Value limit = argv[1];
|
||||
bool isSeparatorRegExp = separator.isPointerValue() && separator.asPointerValue()->isRegExpObject();
|
||||
|
||||
if (separator.isObject()) {
|
||||
// If separator is neither undefined nor null, then
|
||||
if (!separator.isUndefinedOrNull()) {
|
||||
// Let splitter be GetMethod(separator, @@split).
|
||||
Value splitter = Object::getMethod(state, separator, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().split));
|
||||
|
||||
|
|
@ -715,18 +717,11 @@ static Value builtinStringSplit(ExecutionState& state, Value thisValue, size_t a
|
|||
splitMatchUsingStr = [](String* S, int q, String* R) -> Value {
|
||||
int s = S->length();
|
||||
int r = R->length();
|
||||
if (q + r > s) {
|
||||
if (q + r > s)
|
||||
return Value(false);
|
||||
}
|
||||
|
||||
auto sData = S->bufferAccessData();
|
||||
auto rData = R->bufferAccessData();
|
||||
|
||||
for (int i = 0; i < r; i++) {
|
||||
if (sData.charAt(q + i) != rData.charAt(i)) {
|
||||
for (int i = 0; i < r; i++)
|
||||
if (S->charAt(q + i) != R->charAt(i))
|
||||
return Value(false);
|
||||
}
|
||||
}
|
||||
return Value(q + r);
|
||||
};
|
||||
if (s == 0) {
|
||||
|
|
@ -857,7 +852,7 @@ static Value builtinStringCharAt(ExecutionState& state, Value thisValue, size_t
|
|||
char16_t c = str->charAt(position);
|
||||
return state.context()->staticStrings().charCodeToString(c);
|
||||
} else {
|
||||
return String::emptyString();
|
||||
return String::emptyString;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -987,7 +982,7 @@ static Value builtinStringToLowerCase(ExecutionState& state, Value thisValue, si
|
|||
}
|
||||
|
||||
#if defined(ENABLE_ICU)
|
||||
return stringToLocaleConvertCase(state, str, String::emptyString(), false);
|
||||
return stringToLocaleConvertCase(state, str, String::emptyString, false);
|
||||
#else
|
||||
size_t len = str->length();
|
||||
UTF16StringData newStr;
|
||||
|
|
@ -1072,7 +1067,7 @@ static Value builtinStringToUpperCase(ExecutionState& state, Value thisValue, si
|
|||
}
|
||||
|
||||
#if defined(ENABLE_ICU)
|
||||
return stringToLocaleConvertCase(state, str, String::emptyString(), true);
|
||||
return stringToLocaleConvertCase(state, str, String::emptyString, true);
|
||||
#else
|
||||
size_t len = str->length();
|
||||
UTF16StringData newStr;
|
||||
|
|
@ -1281,7 +1276,7 @@ static Value builtinStringRaw(ExecutionState& state, Value thisValue, size_t arg
|
|||
double literalSegments = raw->length(state);
|
||||
// If literalSegments ≤ 0, return the empty string.
|
||||
if (literalSegments <= 0) {
|
||||
return String::emptyString();
|
||||
return String::emptyString;
|
||||
}
|
||||
// Let stringElements be a new empty List.
|
||||
StringBuilder stringElements;
|
||||
|
|
@ -1294,7 +1289,7 @@ static Value builtinStringRaw(ExecutionState& state, Value thisValue, size_t arg
|
|||
String* nextSeg = raw->get(state, ObjectPropertyName(state, Value(nextIndex))).value(state, raw).toString(state);
|
||||
// Append in order the code unit elements of nextSeg to the end of stringElements.
|
||||
for (size_t i = 0; i < nextSeg->length(); i++) {
|
||||
stringElements.appendChar(nextSeg->charAt(i), &state);
|
||||
stringElements.appendChar(nextSeg->charAt(i));
|
||||
}
|
||||
// If nextIndex + 1 = literalSegments, then
|
||||
if (nextIndex + 1 == literalSegments) {
|
||||
|
|
@ -1307,7 +1302,7 @@ static Value builtinStringRaw(ExecutionState& state, Value thisValue, size_t arg
|
|||
next = argv[nextIndex + 1];
|
||||
} else {
|
||||
// Else, let next be the empty String.
|
||||
next = String::emptyString();
|
||||
next = String::emptyString;
|
||||
}
|
||||
// Let nextSub be ? ToString(next).
|
||||
String* nextSub = next.toString(state);
|
||||
|
|
@ -1415,13 +1410,12 @@ static String* createHTML(ExecutionState& state, Value string, String* tag, Stri
|
|||
// ReturnIfAbrupt(V).
|
||||
// Let escapedV be the String value that is the same as V except that each occurrence of the code unit 0x0022 (QUOTATION MARK) in V has been replaced with the six code unit sequence """.
|
||||
StringBuilder sb;
|
||||
auto vData = V->bufferAccessData();
|
||||
for (size_t i = 0; i < vData.length; i++) {
|
||||
char16_t ch = vData.charAt(i);
|
||||
for (size_t i = 0; i < V->length(); i++) {
|
||||
char16_t ch = V->charAt(i);
|
||||
if (ch == 0x22) {
|
||||
sb.appendString(""");
|
||||
} else {
|
||||
sb.appendChar(ch, &state);
|
||||
sb.appendChar(ch);
|
||||
}
|
||||
}
|
||||
String* escapedV = sb.finalize(&state);
|
||||
|
|
@ -1479,7 +1473,7 @@ static Value builtinStringSubstr(ExecutionState& state, Value thisValue, size_t
|
|||
intStart = std::max(size + intStart, 0.0);
|
||||
double resultLength = std::min(std::max(end, 0.0), size - intStart);
|
||||
if (resultLength <= 0)
|
||||
return String::emptyString();
|
||||
return String::emptyString;
|
||||
|
||||
return str->substring(intStart, intStart + resultLength);
|
||||
}
|
||||
|
|
@ -1509,16 +1503,16 @@ static Value builtinStringAt(ExecutionState& state, Value thisValue, size_t argc
|
|||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(anchor, state.context()->staticStrings().asciiTable[(size_t)'a'].string(), state.context()->staticStrings().name.string(), argv[0])
|
||||
// String.prototype.big ()
|
||||
// Return CreateHTML(S, "big", "", "").
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(big, state.context()->staticStrings().big.string(), String::emptyString(), String::emptyString())
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(big, state.context()->staticStrings().big.string(), String::emptyString, String::emptyString)
|
||||
// String.prototype.blink ()
|
||||
// Return CreateHTML(S, "blink", "", "").
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(blink, state.context()->staticStrings().blink.string(), String::emptyString(), String::emptyString())
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(blink, state.context()->staticStrings().blink.string(), String::emptyString, String::emptyString)
|
||||
// String.prototype.bold ()
|
||||
// Return CreateHTML(S, "b", "", "").
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(bold, state.context()->staticStrings().asciiTable[(size_t)'b'].string(), String::emptyString(), String::emptyString())
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(bold, state.context()->staticStrings().asciiTable[(size_t)'b'].string(), String::emptyString, String::emptyString)
|
||||
// String.prototype.fixed ()
|
||||
// Return CreateHTML(S, "tt", "", "").
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(fixed, String::fromASCII("tt"), String::emptyString(), String::emptyString())
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(fixed, String::fromASCII("tt"), String::emptyString, String::emptyString)
|
||||
// String.prototype.fontcolor (color)
|
||||
// Return CreateHTML(S, "font", "color", color).
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(fontcolor, String::fromASCII("font"), String::fromASCII("color"), argv[0])
|
||||
|
|
@ -1527,22 +1521,22 @@ DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(fontcolor, String::fromASCII("font"), Str
|
|||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(fontsize, String::fromASCII("font"), state.context()->staticStrings().size.string(), argv[0])
|
||||
// String.prototype.italics ()
|
||||
// Return CreateHTML(S, "i", "", "").
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(italics, state.context()->staticStrings().asciiTable[(size_t)'i'].string(), String::emptyString(), String::emptyString())
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(italics, state.context()->staticStrings().asciiTable[(size_t)'i'].string(), String::emptyString, String::emptyString)
|
||||
// String.prototype.link (url)
|
||||
// Return CreateHTML(S, "a", "href", url).
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(link, state.context()->staticStrings().asciiTable[(size_t)'a'].string(), String::fromASCII("href"), argv[0])
|
||||
// String.prototype.small ()
|
||||
// Return CreateHTML(S, "small", "", "").
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(small, state.context()->staticStrings().small.string(), String::emptyString(), String::emptyString())
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(small, state.context()->staticStrings().small.string(), String::emptyString, String::emptyString)
|
||||
// String.prototype.strike ()
|
||||
// Return CreateHTML(S, "strike", "", "").
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(strike, state.context()->staticStrings().strike.string(), String::emptyString(), String::emptyString())
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(strike, state.context()->staticStrings().strike.string(), String::emptyString, String::emptyString)
|
||||
// String.prototype.sub ()
|
||||
// Return CreateHTML(S, "sub", "", "").
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(sub, state.context()->staticStrings().sub.string(), String::emptyString(), String::emptyString())
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(sub, state.context()->staticStrings().sub.string(), String::emptyString, String::emptyString)
|
||||
// String.prototype.sup ()
|
||||
// Return CreateHTML(S, "sup", "", "").
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(sup, state.context()->staticStrings().sup.string(), String::emptyString(), String::emptyString())
|
||||
DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(sup, state.context()->staticStrings().sup.string(), String::emptyString, String::emptyString)
|
||||
|
||||
#undef DEFINE_STRING_ADDITIONAL_HTML_FUNCTION
|
||||
|
||||
|
|
@ -1581,36 +1575,6 @@ static Value builtinStringIncludes(ExecutionState& state, Value thisValue, size_
|
|||
return Value(true);
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/multipage/text-processing.html#sec-string.prototype.iswellformed
|
||||
static Value builtinStringIsWellFormed(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// Let O be ? RequireObjectCoercible(this value).
|
||||
// Let S be ? ToString(O).
|
||||
RESOLVE_THIS_BINDING_TO_STRING(S, String, isWellFormed);
|
||||
// Return IsStringWellFormedUnicode(S)
|
||||
return Value(S->isWellFormed());
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/multipage/text-processing.html#sec-string.prototype.towellformed
|
||||
static Value builtinStringToWellFormed(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
// Let O be ? RequireObjectCoercible(this value).
|
||||
// Let S be ? ToString(O).
|
||||
RESOLVE_THIS_BINDING_TO_STRING(S, String, toWellFormed);
|
||||
// Let strLen be the length of S.
|
||||
// Let k be 0.
|
||||
// Let result be the empty String.
|
||||
// Repeat, while k < strLen,
|
||||
// Let cp be CodePointAt(S, k).
|
||||
// If cp.[[IsUnpairedSurrogate]] is true, then
|
||||
// Set result to the string-concatenation of result and 0xFFFD (REPLACEMENT CHARACTER).
|
||||
// Else,
|
||||
// Set result to the string-concatenation of result and UTF16EncodeCodePoint(cp.[[CodePoint]]).
|
||||
// Set k to k + cp.[[CodeUnitCount]].
|
||||
// Return result.
|
||||
return S->toWellFormed();
|
||||
}
|
||||
|
||||
static Value builtinStringIteratorNext(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
if (!thisValue.isObject() || !thisValue.asObject()->isStringIteratorObject()) {
|
||||
|
|
@ -1631,9 +1595,12 @@ static Value builtinStringIterator(ExecutionState& state, Value thisValue, size_
|
|||
|
||||
void GlobalObject::initializeString(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->string(); }, nullptr);
|
||||
return self->asGlobalObject()->string();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().String), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
@ -1646,7 +1613,7 @@ void GlobalObject::installString(ExecutionState& state)
|
|||
m_string = new NativeFunctionObject(state, NativeFunctionInfo(strings->String, builtinStringConstructor, 1), NativeFunctionObject::__ForBuiltinConstructor__);
|
||||
m_string->setGlobalIntrinsicObject(state);
|
||||
|
||||
m_stringPrototype = new StringObject(state, m_objectPrototype, String::emptyString());
|
||||
m_stringPrototype = new StringObject(state, m_objectPrototype, String::emptyString);
|
||||
m_stringPrototype->setGlobalIntrinsicObject(state, true);
|
||||
m_string->setFunctionPrototype(state, m_stringPrototype);
|
||||
|
||||
|
|
@ -1771,21 +1738,13 @@ void GlobalObject::installString(ExecutionState& state)
|
|||
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->vmInstance()->globalSymbols().iterator),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->symbolIterator, builtinStringIterator, 0, NativeFunctionInfo::Strict)),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(AtomicString(state, String::fromASCII("[Symbol.iterator]")), builtinStringIterator, 0, NativeFunctionInfo::Strict)),
|
||||
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->at),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->at, builtinStringAt, 1, NativeFunctionInfo::Strict)),
|
||||
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->isWellFormed),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->isWellFormed, builtinStringIsWellFormed, 0, NativeFunctionInfo::Strict)),
|
||||
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->toWellFormed),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->toWellFormed, builtinStringToWellFormed, 0, NativeFunctionInfo::Strict)),
|
||||
(ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
#define DEFINE_STRING_ADDITIONAL_HTML_FUNCTION(fnName, argLength) \
|
||||
m_stringPrototype->directDefineOwnProperty(state, ObjectPropertyName(strings->fnName), \
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(strings->fnName, builtinString##fnName, argLength, NativeFunctionInfo::Strict)), \
|
||||
|
|
|
|||
|
|
@ -107,9 +107,12 @@ static Value builtinSymbolDescriptionGetter(ExecutionState& state, Value thisVal
|
|||
|
||||
void GlobalObject::initializeSymbol(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->symbol(); }, nullptr);
|
||||
return self->asGlobalObject()->symbol();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().Symbol), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -59,8 +59,7 @@ static Value builtinWeakMapConstructor(ExecutionState& state, Value thisValue, s
|
|||
|
||||
Value nextItem = IteratorObject::iteratorValue(state, next.value());
|
||||
if (!nextItem.isObject()) {
|
||||
ErrorObject* error = ErrorObject::createError(state, ErrorCode::TypeError,
|
||||
new ASCIIStringFromExternalMemory("Invalid iterator value"));
|
||||
ErrorObject* error = ErrorObject::createError(state, ErrorCode::TypeError, new ASCIIString("TypeError"));
|
||||
return IteratorObject::iteratorClose(state, iteratorRecord, error, true);
|
||||
}
|
||||
|
||||
|
|
@ -103,26 +102,6 @@ static Value builtinWeakMapGet(ExecutionState& state, Value thisValue, size_t ar
|
|||
return M->get(state, argv[0].asPointerValue());
|
||||
}
|
||||
|
||||
static Value builtinWeakMapGetOrInsert(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
RESOLVE_THIS_BINDING_TO_WEAKMAP(M, WeakMap, getOrInsert);
|
||||
if (!argv[0].canBeHeldWeakly(state.context()->vmInstance())) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Invalid value used as weak map key");
|
||||
}
|
||||
|
||||
return M->getOrInsert(state, argv[0].asPointerValue(), argv[1]);
|
||||
}
|
||||
|
||||
static Value builtinWeakMapGetOrInsertComputed(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
RESOLVE_THIS_BINDING_TO_WEAKMAP(M, WeakMap, getOrInsert);
|
||||
if (!argv[0].canBeHeldWeakly(state.context()->vmInstance())) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Invalid value used as weak map key");
|
||||
}
|
||||
|
||||
return M->getOrInsertComputed(state, argv[0].asPointerValue(), argv[1]);
|
||||
}
|
||||
|
||||
static Value builtinWeakMapHas(ExecutionState& state, Value thisValue, size_t argc, Value* argv, Optional<Object*> newTarget)
|
||||
{
|
||||
RESOLVE_THIS_BINDING_TO_WEAKMAP(M, WeakMap, has);
|
||||
|
|
@ -146,9 +125,12 @@ static Value builtinWeakMapSet(ExecutionState& state, Value thisValue, size_t ar
|
|||
|
||||
void GlobalObject::initializeWeakMap(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->weakMap(); }, nullptr);
|
||||
return self->asGlobalObject()->weakMap();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().WeakMap), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
@ -168,12 +150,6 @@ void GlobalObject::installWeakMap(ExecutionState& state)
|
|||
m_weakMapPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().get),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().get, builtinWeakMapGet, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_weakMapPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().getOrInsert),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().getOrInsert, builtinWeakMapGetOrInsert, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_weakMapPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().getOrInsertComputed),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().getOrInsertComputed, builtinWeakMapGetOrInsertComputed, 2, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
m_weakMapPrototype->directDefineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().has),
|
||||
ObjectPropertyDescriptor(new NativeFunctionObject(state, NativeFunctionInfo(state.context()->staticStrings().has, builtinWeakMapHas, 1, NativeFunctionInfo::Strict)), (ObjectPropertyDescriptor::PresentAttribute)(ObjectPropertyDescriptor::WritablePresent | ObjectPropertyDescriptor::ConfigurablePresent)));
|
||||
|
||||
|
|
|
|||
|
|
@ -58,9 +58,12 @@ static Value builtinWeakRefDeRef(ExecutionState& state, Value thisValue, size_t
|
|||
|
||||
void GlobalObject::initializeWeakRef(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->weakRef(); }, nullptr);
|
||||
return self->asGlobalObject()->weakRef();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().WeakRef), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,9 +114,12 @@ static Value builtinWeakSetHas(ExecutionState& state, Value thisValue, size_t ar
|
|||
|
||||
void GlobalObject::initializeWeakSet(ExecutionState& state)
|
||||
{
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true, [](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ObjectPropertyNativeGetterSetterData* nativeData = new ObjectPropertyNativeGetterSetterData(true, false, true,
|
||||
[](ExecutionState& state, Object* self, const Value& receiver, const EncodedValue& privateDataFromObjectPrivateArea) -> Value {
|
||||
ASSERT(self->isGlobalObject());
|
||||
return self->asGlobalObject()->weakSet(); }, nullptr);
|
||||
return self->asGlobalObject()->weakSet();
|
||||
},
|
||||
nullptr);
|
||||
|
||||
defineNativeDataAccessorProperty(state, ObjectPropertyName(state.context()->staticStrings().WeakSet), nativeData, Value(Value::EmptyValue));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -424,66 +424,46 @@ bool CodeCache::loadGlobalCache(Context* context, const CodeCacheIndex& cacheInd
|
|||
{
|
||||
ASSERT(m_enabled && cacheIndex.isValid());
|
||||
|
||||
if (m_status != Status::READY) {
|
||||
// load global CodeBlock and its related information
|
||||
prepareCacheLoading(context, cacheIndex, entry);
|
||||
|
||||
InterpretedCodeBlock* topCodeBlock = loadCodeBlockTree(context, script);
|
||||
ByteCodeBlock* topByteCodeBlock = loadByteCodeBlock(context, topCodeBlock);
|
||||
|
||||
if (!postCacheLoading()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// load global CodeBlock and its related information
|
||||
prepareCacheLoading(context, cacheIndex, entry);
|
||||
ASSERT(!!topCodeBlock && !!topByteCodeBlock);
|
||||
script->m_topCodeBlock = topCodeBlock;
|
||||
topCodeBlock->m_byteCodeBlock = topByteCodeBlock;
|
||||
|
||||
InterpretedCodeBlock* topCodeBlock = loadCodeBlockTree(context, script);
|
||||
ByteCodeBlock* topByteCodeBlock = loadByteCodeBlock(context, topCodeBlock);
|
||||
ESCARGOT_LOG_INFO("[CodeCache] Load CodeCache Done (%s)\n", script->srcName()->toUTF8StringData().data());
|
||||
|
||||
if (!postCacheLoading()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ASSERT(!!topCodeBlock && !!topByteCodeBlock);
|
||||
script->m_topCodeBlock = topCodeBlock;
|
||||
topCodeBlock->m_byteCodeBlock = topByteCodeBlock;
|
||||
|
||||
ESCARGOT_LOG_INFO("[CodeCache] Load CodeCache Done (%s)\n", script->srcName()->toUTF8StringData().data());
|
||||
|
||||
return true;
|
||||
} catch (CodeCacheReader::Error& error) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CodeCache::loadFunctionCache(Context* context, const CodeCacheIndex& cacheIndex, const CodeCacheEntry& entry, InterpretedCodeBlock* codeBlock)
|
||||
{
|
||||
ASSERT(m_enabled && cacheIndex.isValid());
|
||||
|
||||
if (m_status != Status::READY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// load function CodeBlock and its related information
|
||||
prepareCacheLoading(context, cacheIndex, entry);
|
||||
|
||||
codeBlock->m_byteCodeBlock = loadByteCodeBlock(context, codeBlock);
|
||||
|
||||
bool result = postCacheLoading();
|
||||
if (result) {
|
||||
ESCARGOT_LOG_INFO("[CodeCache] Load CodeCache Done (%s: index %zu size %zu)\n", codeBlock->script()->srcName()->toNonGCUTF8StringData().data(),
|
||||
codeBlock->functionStart().index, codeBlock->src().length());
|
||||
}
|
||||
return result;
|
||||
} catch (CodeCacheReader::Error& error) {
|
||||
return false;
|
||||
// load function CodeBlock and its related information
|
||||
prepareCacheLoading(context, cacheIndex, entry);
|
||||
|
||||
codeBlock->m_byteCodeBlock = loadByteCodeBlock(context, codeBlock);
|
||||
|
||||
bool result = postCacheLoading();
|
||||
if (result) {
|
||||
ESCARGOT_LOG_INFO("[CodeCache] Load CodeCache Done (%s: index %zu size %zu)\n", codeBlock->script()->srcName()->toNonGCUTF8StringData().data(),
|
||||
codeBlock->functionStart().index, codeBlock->src().length());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CodeCache::storeGlobalCache(Context* context, const CodeCacheIndex& cacheIndex, InterpretedCodeBlock* topCodeBlock, CodeBlockCacheInfo* codeBlockCacheInfo, Node* programNode, bool inWith)
|
||||
{
|
||||
ASSERT(m_enabled && cacheIndex.isValid());
|
||||
|
||||
if (m_status != Status::READY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// store global CodeBlock and its related information
|
||||
prepareCacheWriting(cacheIndex);
|
||||
|
||||
|
|
@ -514,10 +494,6 @@ bool CodeCache::storeFunctionCache(Context* context, const CodeCacheIndex& cache
|
|||
{
|
||||
ASSERT(m_enabled && cacheIndex.isValid());
|
||||
|
||||
if (m_status != Status::READY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// store function ByteCodeBlock and its related information
|
||||
prepareCacheWriting(cacheIndex);
|
||||
|
||||
|
|
|
|||
|
|
@ -69,9 +69,7 @@ void CacheStringTable::initAdd(const AtomicString& string)
|
|||
|
||||
AtomicString& CacheStringTable::get(size_t index)
|
||||
{
|
||||
if (index >= m_table.size()) {
|
||||
throw CodeCacheReader::Error("out of range");
|
||||
}
|
||||
ASSERT(index < m_table.size());
|
||||
return m_table[index];
|
||||
}
|
||||
|
||||
|
|
@ -192,7 +190,6 @@ void CodeCacheWriter::storeInterpretedCodeBlock(InterpretedCodeBlock* codeBlock)
|
|||
const InterpretedCodeBlock::BlockIdentifierInfo& info = infoVector[j];
|
||||
m_buffer.put(info.m_needToAllocateOnStack);
|
||||
m_buffer.put(info.m_isMutable);
|
||||
m_buffer.put(info.m_isUsing);
|
||||
m_buffer.put(info.m_indexForIndexedStorage);
|
||||
m_buffer.put(m_stringTable->add(info.m_name));
|
||||
}
|
||||
|
|
@ -212,12 +209,6 @@ void CodeCacheWriter::storeInterpretedCodeBlock(InterpretedCodeBlock* codeBlock)
|
|||
m_buffer.put(codeBlock->m_bodyEndLOC);
|
||||
#endif
|
||||
|
||||
#ifndef ESCARGOT_DEBUGGER
|
||||
// InterpretedCodeBlock::m_parameterUsed
|
||||
m_buffer.ensureSize(sizeof(uint16_t));
|
||||
m_buffer.put(codeBlock->m_parameterUsed);
|
||||
#endif
|
||||
|
||||
m_buffer.ensureSize(5 * sizeof(uint16_t));
|
||||
m_buffer.put(codeBlock->m_functionLength);
|
||||
m_buffer.put(codeBlock->m_parameterCount);
|
||||
|
|
@ -634,12 +625,6 @@ void CodeCacheWriter::storeByteCodeStream(ByteCodeBlock* block)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case FinalizeDisposableOpcode: {
|
||||
// add tail data length
|
||||
FinalizeDisposable* bc = static_cast<FinalizeDisposable*>(currentCode);
|
||||
code += bc->m_tailDataLength;
|
||||
break;
|
||||
}
|
||||
case ExecutionResumeOpcode:
|
||||
RELEASE_ASSERT_NOT_REACHED();
|
||||
break;
|
||||
|
|
@ -772,7 +757,6 @@ InterpretedCodeBlock* CodeCacheReader::loadInterpretedCodeBlock(Context* context
|
|||
InterpretedCodeBlock::BlockIdentifierInfo idInfo;
|
||||
idInfo.m_needToAllocateOnStack = m_buffer.get<bool>();
|
||||
idInfo.m_isMutable = m_buffer.get<bool>();
|
||||
idInfo.m_isUsing = m_buffer.get<bool>();
|
||||
idInfo.m_indexForIndexedStorage = m_buffer.get<size_t>();
|
||||
idInfo.m_name = m_stringTable->get(m_buffer.get<size_t>());
|
||||
info->identifiers()[j] = idInfo;
|
||||
|
|
@ -793,11 +777,6 @@ InterpretedCodeBlock* CodeCacheReader::loadInterpretedCodeBlock(Context* context
|
|||
codeBlock->m_bodyEndLOC = m_buffer.get<ExtendedNodeLOC>();
|
||||
#endif
|
||||
|
||||
#ifndef ESCARGOT_DEBUGGER
|
||||
// InterpretedCodeBlock::m_parameterUsed
|
||||
codeBlock->m_parameterUsed = m_buffer.get<uint16_t>();
|
||||
#endif
|
||||
|
||||
codeBlock->m_functionLength = m_buffer.get<uint16_t>();
|
||||
codeBlock->m_parameterCount = m_buffer.get<uint16_t>();
|
||||
codeBlock->m_identifierOnStackCount = m_buffer.get<uint16_t>();
|
||||
|
|
@ -934,9 +913,6 @@ CacheStringTable* CodeCacheReader::loadStringTable(Context* context)
|
|||
LChar* buffer = new LChar[maxLength + 1];
|
||||
for (size_t i = 0; i < tableSize; i++) {
|
||||
size_t length = m_buffer.get<size_t>();
|
||||
if (maxLength < length) {
|
||||
throw CodeCacheReader::Error("invalid maxLength");
|
||||
}
|
||||
m_buffer.getData(buffer, length);
|
||||
buffer[length] = '\0';
|
||||
|
||||
|
|
@ -955,9 +931,6 @@ CacheStringTable* CodeCacheReader::loadStringTable(Context* context)
|
|||
for (size_t i = 0; i < tableSize; i++) {
|
||||
bool is8Bit = m_buffer.get<bool>();
|
||||
size_t length = m_buffer.get<size_t>();
|
||||
if (maxLength < length) {
|
||||
throw CodeCacheReader::Error("invalid maxLength");
|
||||
}
|
||||
|
||||
if (is8Bit) {
|
||||
m_buffer.getData(lBuffer, length);
|
||||
|
|
@ -1026,10 +999,6 @@ void CodeCacheReader::loadByteCodeStream(Context* context, ByteCodeBlock* block)
|
|||
ByteCodeRelocInfo& info = relocInfoVector[i];
|
||||
ByteCode* currentCode = reinterpret_cast<ByteCode*>(code + info.codeOffset);
|
||||
|
||||
if (info.codeOffset >= byteCodeStream.size()) {
|
||||
throw CodeCacheReader::Error("out of range");
|
||||
}
|
||||
|
||||
#if defined(ESCARGOT_COMPUTED_GOTO_INTERPRETER)
|
||||
Opcode opcode = (Opcode)(size_t)currentCode->m_opcodeInAddress;
|
||||
#else
|
||||
|
|
@ -1043,7 +1012,7 @@ void CodeCacheReader::loadByteCodeStream(Context* context, ByteCodeBlock* block)
|
|||
switch (info.relocType) {
|
||||
case ByteCodeRelocType::RELOC_STRING: {
|
||||
if (UNLIKELY(dataIndex == SIZE_MAX)) {
|
||||
bc->m_value = String::emptyString();
|
||||
bc->m_value = String::emptyString;
|
||||
} else {
|
||||
ASSERT(dataIndex < stringLiteralData.size());
|
||||
String* string = stringLiteralData[dataIndex];
|
||||
|
|
@ -1166,7 +1135,7 @@ void CodeCacheReader::loadByteCodeStream(Context* context, ByteCodeBlock* block)
|
|||
bodyStringForLoadRegExp = false;
|
||||
} else {
|
||||
if (info.dataOffset == SIZE_MAX) {
|
||||
bc->m_option = String::emptyString();
|
||||
bc->m_option = String::emptyString;
|
||||
} else {
|
||||
ASSERT(info.dataOffset < stringLiteralData.size());
|
||||
bc->m_option = stringLiteralData[info.dataOffset];
|
||||
|
|
|
|||
|
|
@ -227,14 +227,6 @@ private:
|
|||
|
||||
class CodeCacheReader {
|
||||
public:
|
||||
class Error {
|
||||
public:
|
||||
std::string message;
|
||||
Error(const std::string& message)
|
||||
: message(message)
|
||||
{
|
||||
}
|
||||
};
|
||||
class CacheBuffer {
|
||||
public:
|
||||
CacheBuffer()
|
||||
|
|
@ -258,9 +250,7 @@ public:
|
|||
template <typename IntegralType>
|
||||
IntegralType get()
|
||||
{
|
||||
if (m_index + sizeof(IntegralType) > m_capacity) {
|
||||
throw Error("out of range");
|
||||
}
|
||||
ASSERT(m_index < m_capacity);
|
||||
IntegralType value;
|
||||
memcpy(&value, m_buffer + m_index, sizeof(IntegralType));
|
||||
m_index += sizeof(IntegralType);
|
||||
|
|
@ -270,13 +260,10 @@ public:
|
|||
template <typename IntegralType>
|
||||
void getData(IntegralType* data, size_t size)
|
||||
{
|
||||
size_t dataSize = size * sizeof(IntegralType);
|
||||
if (m_index + dataSize > m_capacity) {
|
||||
throw Error("out of range");
|
||||
}
|
||||
if (UNLIKELY(!size)) {
|
||||
return;
|
||||
}
|
||||
size_t dataSize = size * sizeof(IntegralType);
|
||||
memcpy(data, m_buffer + m_index, dataSize);
|
||||
m_index += dataSize;
|
||||
}
|
||||
|
|
@ -287,19 +274,16 @@ public:
|
|||
String* str = nullptr;
|
||||
bool is8Bit = get<bool>();
|
||||
size_t length = get<size_t>();
|
||||
if (length > STRING_MAXIMUM_LENGTH) {
|
||||
throw Error("out of range");
|
||||
}
|
||||
ASSERT(length);
|
||||
if (LIKELY(is8Bit)) {
|
||||
LChar* buffer = ALLOCA(sizeof(LChar) * (length + 1), LChar);
|
||||
getData(buffer, length);
|
||||
buffer[length] = '\0';
|
||||
getData(buffer, length);
|
||||
str = new Latin1String(buffer, length);
|
||||
} else {
|
||||
UChar* buffer = ALLOCA(sizeof(UChar) * (length + 1), UChar);
|
||||
getData(buffer, length);
|
||||
buffer[length] = '\0';
|
||||
getData(buffer, length);
|
||||
str = new UTF16String(buffer, length);
|
||||
}
|
||||
return str;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -20,22 +20,22 @@
|
|||
#ifndef __Debugger__
|
||||
#define __Debugger__
|
||||
|
||||
#include "runtime/Environment.h"
|
||||
#include "util/Vector.h"
|
||||
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
namespace Escargot {
|
||||
|
||||
class ByteCode;
|
||||
|
||||
#define ESCARGOT_DEBUGGER_MAX_STACK_TRACE_LENGTH 8
|
||||
|
||||
/* WebSocket max length encoded in one byte. */
|
||||
#define ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH 125
|
||||
#define ESCARGOT_DEBUGGER_VERSION 1
|
||||
#define ESCARGOT_DEBUGGER_MESSAGE_PROCESS_DELAY 10
|
||||
#define ESCARGOT_DEBUGGER_IN_WAIT_MODE (nullptr)
|
||||
#define ESCARGOT_DEBUGGER_IN_EVAL_MODE (reinterpret_cast<ExecutionState*>(0x1))
|
||||
#define ESCARGOT_DEBUGGER_ALWAYS_STOP (reinterpret_cast<ExecutionState*>(0x2))
|
||||
#define ESCARGOT_DEBUGGER_NO_STACK_TRACE_RESTORE (reinterpret_cast<ExecutionState*>(0x1))
|
||||
#define ESCARGOT_DEBUGGER_MAX_VARIABLE_LENGTH 128
|
||||
|
||||
class Context;
|
||||
class Object;
|
||||
|
|
@ -46,7 +46,7 @@ class InterpretedCodeBlock;
|
|||
|
||||
class Debugger : public gc {
|
||||
public:
|
||||
// The following code is the same as in EscargotPublic.h
|
||||
// The following code is the sam as in EscargotPublic.h
|
||||
class WeakCodeRef;
|
||||
|
||||
struct BreakpointLocation {
|
||||
|
|
@ -60,17 +60,6 @@ public:
|
|||
uint32_t offset; // bytecode offset
|
||||
};
|
||||
|
||||
struct BreakpointByteCodeLocation {
|
||||
BreakpointByteCodeLocation(const uint32_t line, ByteCode* breakpointByteCode)
|
||||
: line(line)
|
||||
, byteCode(breakpointByteCode)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t line; // source code line
|
||||
ByteCode* byteCode; // bytecode pointer
|
||||
};
|
||||
|
||||
typedef std::vector<BreakpointLocation> BreakpointLocationVector;
|
||||
|
||||
struct BreakpointLocationsInfo {
|
||||
|
|
@ -119,10 +108,6 @@ public:
|
|||
|
||||
void clearParsingData()
|
||||
{
|
||||
for (size_t i = 0; i < m_breakpointLocationsVector.size(); i++) {
|
||||
// delete each shared Debugger::BreakpointLocationsInfo here
|
||||
delete m_breakpointLocationsVector[i];
|
||||
}
|
||||
m_breakpointLocationsVector.clear();
|
||||
}
|
||||
|
||||
|
|
@ -142,7 +127,7 @@ public:
|
|||
return m_activeSavedStackTrace;
|
||||
}
|
||||
|
||||
inline bool processDisabledBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state)
|
||||
inline void processDisabledBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state)
|
||||
{
|
||||
if (m_stopState != ESCARGOT_DEBUGGER_ALWAYS_STOP && m_stopState != state) {
|
||||
m_delay--;
|
||||
|
|
@ -154,8 +139,6 @@ public:
|
|||
if (m_stopState == ESCARGOT_DEBUGGER_ALWAYS_STOP || m_stopState == state) {
|
||||
stopAtBreakpoint(byteCodeBlock, offset, state);
|
||||
}
|
||||
|
||||
return m_restartDebugging;
|
||||
}
|
||||
|
||||
static inline void updateStopState(Debugger* debugger, ExecutionState* state, ExecutionState* newState)
|
||||
|
|
@ -166,38 +149,20 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
static LexicalEnvironment* getFunctionLexEnv(ExecutionState* state)
|
||||
{
|
||||
LexicalEnvironment* lexEnv = state->lexicalEnvironment();
|
||||
|
||||
while (lexEnv) {
|
||||
EnvironmentRecord* record = lexEnv->record();
|
||||
|
||||
if (record->isDeclarativeEnvironmentRecord()
|
||||
&& record->asDeclarativeEnvironmentRecord()->isFunctionEnvironmentRecord()) {
|
||||
return lexEnv;
|
||||
}
|
||||
|
||||
lexEnv = lexEnv->outerEnvironment();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void setStopState(ExecutionState* stopState)
|
||||
{
|
||||
m_stopState = stopState;
|
||||
}
|
||||
|
||||
virtual void init(const char* options, Context* context) = 0;
|
||||
static void createDebuggerRemote(const char* options, Context* context);
|
||||
|
||||
virtual void parseCompleted(String* source, String* srcName, size_t originLineOffset, String* error = nullptr) = 0;
|
||||
virtual bool stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) = 0;
|
||||
virtual void stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) = 0;
|
||||
virtual void byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock) = 0;
|
||||
virtual void exceptionCaught(String* message, SavedStackTraceDataVector& exceptionTrace) = 0;
|
||||
virtual void consoleOut(String* output) = 0;
|
||||
virtual String* getClientSource(String** sourceName) = 0;
|
||||
virtual bool getWaitBeforeExitClient() = 0;
|
||||
virtual void deleteClient() {}
|
||||
virtual bool skipSourceCode(String* srcName) const
|
||||
{
|
||||
return false;
|
||||
|
|
@ -206,21 +171,6 @@ public:
|
|||
static SavedStackTraceDataVector* saveStackTrace(ExecutionState& state);
|
||||
|
||||
void pumpDebuggerEvents(ExecutionState* state);
|
||||
static void createDebugger(const char* options, Context* context);
|
||||
|
||||
void enable(Context* context);
|
||||
|
||||
Vector<Object*, GCUtil::gc_malloc_allocator<Object*>> m_activeObjects;
|
||||
|
||||
bool getRestart()
|
||||
{
|
||||
return m_restartDebugging;
|
||||
}
|
||||
|
||||
void setRestart(bool b)
|
||||
{
|
||||
m_restartDebugging = b;
|
||||
}
|
||||
|
||||
protected:
|
||||
Debugger()
|
||||
|
|
@ -238,6 +188,7 @@ protected:
|
|||
return m_context != nullptr;
|
||||
}
|
||||
|
||||
void enable(Context* context);
|
||||
void disable();
|
||||
|
||||
virtual bool processEvents(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, bool isBlockingRequest = true) = 0;
|
||||
|
|
@ -245,8 +196,6 @@ protected:
|
|||
uint32_t m_delay;
|
||||
ExecutionState* m_stopState;
|
||||
std::vector<BreakpointLocationsInfo*> m_breakpointLocationsVector;
|
||||
Vector<uintptr_t, GCUtil::gc_malloc_atomic_allocator<uintptr_t>> m_releasedFunctions;
|
||||
bool m_restartDebugging;
|
||||
|
||||
private:
|
||||
Context* m_context;
|
||||
|
|
@ -258,6 +207,232 @@ private:
|
|||
bool m_inDebuggingCodeMode;
|
||||
};
|
||||
|
||||
class DebuggerRemote : public Debugger {
|
||||
public:
|
||||
// Messages sent by Escargot to the debugger client
|
||||
enum {
|
||||
ESCARGOT_MESSAGE_VERSION = 0,
|
||||
ESCARGOT_MESSAGE_CONFIGURATION = 1,
|
||||
ESCARGOT_MESSAGE_CLOSE_CONNECTION = 2,
|
||||
ESCARGOT_MESSAGE_RELEASE_FUNCTION = 3,
|
||||
ESCARGOT_MESSAGE_PARSE_DONE = 4,
|
||||
ESCARGOT_MESSAGE_PARSE_ERROR = 5,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_SOURCE_8BIT = 6,
|
||||
ESCARGOT_MESSAGE_SOURCE_8BIT_END = 7,
|
||||
ESCARGOT_MESSAGE_SOURCE_16BIT = 8,
|
||||
ESCARGOT_MESSAGE_SOURCE_16BIT_END = 9,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_FILE_NAME_8BIT = 10,
|
||||
ESCARGOT_MESSAGE_FILE_NAME_8BIT_END = 11,
|
||||
ESCARGOT_MESSAGE_FILE_NAME_16BIT = 12,
|
||||
ESCARGOT_MESSAGE_FILE_NAME_16BIT_END = 13,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_FUNCTION_NAME_8BIT = 14,
|
||||
ESCARGOT_MESSAGE_FUNCTION_NAME_8BIT_END = 15,
|
||||
ESCARGOT_MESSAGE_FUNCTION_NAME_16BIT = 16,
|
||||
ESCARGOT_MESSAGE_FUNCTION_NAME_16BIT_END = 17,
|
||||
ESCARGOT_MESSAGE_BREAKPOINT_LOCATION = 18,
|
||||
ESCARGOT_MESSAGE_FUNCTION_PTR = 19,
|
||||
ESCARGOT_MESSAGE_BREAKPOINT_HIT = 20,
|
||||
ESCARGOT_MESSAGE_EXCEPTION_HIT = 21,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_EVAL_RESULT_8BIT = 22,
|
||||
ESCARGOT_MESSAGE_EVAL_RESULT_8BIT_END = 23,
|
||||
ESCARGOT_MESSAGE_EVAL_RESULT_16BIT = 24,
|
||||
ESCARGOT_MESSAGE_EVAL_RESULT_16BIT_END = 25,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_EVAL_FAILED_8BIT = 26,
|
||||
ESCARGOT_MESSAGE_EVAL_FAILED_8BIT_END = 27,
|
||||
ESCARGOT_MESSAGE_EVAL_FAILED_16BIT = 28,
|
||||
ESCARGOT_MESSAGE_EVAL_FAILED_16BIT_END = 29,
|
||||
ESCARGOT_MESSAGE_BACKTRACE_TOTAL = 30,
|
||||
ESCARGOT_MESSAGE_BACKTRACE = 31,
|
||||
ESCARGOT_MESSAGE_BACKTRACE_END = 32,
|
||||
ESCARGOT_MESSAGE_SCOPE_CHAIN = 33,
|
||||
ESCARGOT_MESSAGE_SCOPE_CHAIN_END = 34,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_STRING_8BIT = 35,
|
||||
ESCARGOT_MESSAGE_STRING_8BIT_END = 36,
|
||||
ESCARGOT_MESSAGE_STRING_16BIT = 37,
|
||||
ESCARGOT_MESSAGE_STRING_16BIT_END = 38,
|
||||
ESCARGOT_MESSAGE_VARIABLE = 39,
|
||||
ESCARGOT_MESSAGE_PRINT = 40,
|
||||
ESCARGOT_MESSAGE_EXCEPTION = 41,
|
||||
ESCARGOT_MESSAGE_EXCEPTION_BACKTRACE = 42,
|
||||
ESCARGOT_DEBUGGER_WAIT_FOR_SOURCE = 43,
|
||||
ESCARGOT_DEBUGGER_WAITING_AFTER_PENDING = 44,
|
||||
ESCARGOT_DEBUGGER_WAIT_FOR_WAIT_EXIT = 45,
|
||||
};
|
||||
|
||||
// Messages sent by the debugger client to Escargot
|
||||
enum {
|
||||
ESCARGOT_MESSAGE_FUNCTION_RELEASED = 0,
|
||||
ESCARGOT_MESSAGE_UPDATE_BREAKPOINT = 1,
|
||||
ESCARGOT_MESSAGE_CONTINUE = 2,
|
||||
ESCARGOT_MESSAGE_STEP = 3,
|
||||
ESCARGOT_MESSAGE_NEXT = 4,
|
||||
ESCARGOT_MESSAGE_FINISH = 5,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_EVAL_8BIT_START = 6,
|
||||
ESCARGOT_MESSAGE_EVAL_8BIT = 7,
|
||||
ESCARGOT_MESSAGE_EVAL_16BIT_START = 8,
|
||||
ESCARGOT_MESSAGE_EVAL_16BIT = 9,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_8BIT_START = 10,
|
||||
ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_8BIT = 11,
|
||||
ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_16BIT_START = 12,
|
||||
ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_16BIT = 13,
|
||||
ESCARGOT_MESSAGE_GET_BACKTRACE = 14,
|
||||
ESCARGOT_MESSAGE_GET_SCOPE_CHAIN = 15,
|
||||
ESCARGOT_MESSAGE_GET_SCOPE_VARIABLES = 16,
|
||||
ESCARGOT_MESSAGE_GET_OBJECT = 17,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_DEBUGGER_CLIENT_SOURCE_8BIT_START = 18,
|
||||
ESCARGOT_DEBUGGER_CLIENT_SOURCE_8BIT = 19,
|
||||
ESCARGOT_DEBUGGER_CLIENT_SOURCE_16BIT_START = 20,
|
||||
ESCARGOT_DEBUGGER_CLIENT_SOURCE_16BIT = 21,
|
||||
ESCARGOT_DEBUGGER_THERE_WAS_NO_SOURCE = 22,
|
||||
ESCARGOT_DEBUGGER_PENDING_CONFIG = 23,
|
||||
ESCARGOT_DEBUGGER_PENDING_RESUME = 24,
|
||||
ESCARGOT_DEBUGGER_WAIT_BEFORE_EXIT = 25,
|
||||
};
|
||||
|
||||
// Environment record types
|
||||
enum {
|
||||
ESCARGOT_RECORD_GLOBAL_ENVIRONMENT = 0,
|
||||
ESCARGOT_RECORD_FUNCTION_ENVIRONMENT = 1,
|
||||
ESCARGOT_RECORD_DECLARATIVE_ENVIRONMENT = 2,
|
||||
ESCARGOT_RECORD_OBJECT_ENVIRONMENT = 3,
|
||||
ESCARGOT_RECORD_MODULE_ENVIRONMENT = 4,
|
||||
ESCARGOT_RECORD_UNKNOWN_ENVIRONMENT = 5,
|
||||
};
|
||||
|
||||
// Variable types
|
||||
enum {
|
||||
ESCARGOT_VARIABLE_END = 0,
|
||||
ESCARGOT_VARIABLE_UNACCESSIBLE = 1,
|
||||
ESCARGOT_VARIABLE_UNDEFINED = 2,
|
||||
ESCARGOT_VARIABLE_NULL = 3,
|
||||
ESCARGOT_VARIABLE_TRUE = 4,
|
||||
ESCARGOT_VARIABLE_FALSE = 5,
|
||||
ESCARGOT_VARIABLE_NUMBER = 6,
|
||||
ESCARGOT_VARIABLE_STRING = 7,
|
||||
ESCARGOT_VARIABLE_SYMBOL = 8,
|
||||
ESCARGOT_VARIABLE_BIGINT = 9,
|
||||
// Only object types should be defined after this point.
|
||||
ESCARGOT_VARIABLE_OBJECT = 10,
|
||||
ESCARGOT_VARIABLE_ARRAY = 11,
|
||||
ESCARGOT_VARIABLE_FUNCTION = 12,
|
||||
ESCARGOT_VARIABLE_TYPE_MASK = 0x3f,
|
||||
ESCARGOT_VARIABLE_LONG_NAME = 0x40,
|
||||
ESCARGOT_VARIABLE_LONG_VALUE = 0x80,
|
||||
};
|
||||
|
||||
inline bool pendingWait(void)
|
||||
{
|
||||
return m_pendingWait;
|
||||
}
|
||||
|
||||
inline bool connected(void)
|
||||
{
|
||||
return enabled();
|
||||
}
|
||||
|
||||
void sendType(uint8_t type);
|
||||
void sendSubtype(uint8_t type, uint8_t subType);
|
||||
void sendString(uint8_t type, const String* string);
|
||||
void sendPointer(uint8_t type, const void* ptr);
|
||||
|
||||
virtual void init(const char* options, Context* context) = 0;
|
||||
virtual void parseCompleted(String* source, String* srcName, size_t originLineOffset, String* error = nullptr) override;
|
||||
virtual void stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) override;
|
||||
virtual void byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock) override;
|
||||
virtual void exceptionCaught(String* message, SavedStackTraceDataVector& exceptionTrace) override;
|
||||
virtual void consoleOut(String* output) override;
|
||||
virtual String* getClientSource(String** sourceName) override;
|
||||
virtual bool getWaitBeforeExitClient() override;
|
||||
|
||||
void sendBacktraceInfo(uint8_t type, ByteCodeBlock* byteCodeBlock, uint32_t line, uint32_t column, uint32_t executionStateDepth);
|
||||
void sendVariableObjectInfo(uint8_t subType, Object* object);
|
||||
void waitForResolvingPendingBreakpoints();
|
||||
|
||||
protected:
|
||||
enum CloseReason {
|
||||
CloseEndConnection,
|
||||
CloseAbortConnection,
|
||||
CloseProtocolUnsupported,
|
||||
CloseProtocolError,
|
||||
};
|
||||
|
||||
DebuggerRemote()
|
||||
: m_exitClient(false)
|
||||
, m_pendingWait(false)
|
||||
, m_waitForResume(false)
|
||||
, m_clientSourceData(nullptr)
|
||||
, m_clientSourceName(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool processEvents(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, bool isBlockingRequest = true) override;
|
||||
|
||||
virtual bool send(uint8_t type, const void* buffer, size_t length) = 0;
|
||||
virtual bool receive(uint8_t* buffer, size_t& length) = 0;
|
||||
virtual bool isThereAnyEvent() = 0;
|
||||
virtual void close(CloseReason reason) = 0;
|
||||
|
||||
private:
|
||||
// Packed structure definitions to reduce network traffic
|
||||
|
||||
struct MessageVersion {
|
||||
uint8_t littleEndian;
|
||||
uint8_t version[sizeof(uint32_t)];
|
||||
};
|
||||
|
||||
struct MessageConfiguration {
|
||||
uint8_t maxMessageSize;
|
||||
uint8_t pointerSize;
|
||||
};
|
||||
|
||||
struct FunctionInfo {
|
||||
uint8_t byteCodeStart[sizeof(void*)];
|
||||
uint8_t startLine[sizeof(uint32_t)];
|
||||
uint8_t startColumn[sizeof(uint32_t)];
|
||||
};
|
||||
|
||||
struct BreakpointOffset {
|
||||
uint8_t byteCodeStart[sizeof(void*)];
|
||||
uint8_t offset[sizeof(uint32_t)];
|
||||
};
|
||||
|
||||
struct BacktraceInfo {
|
||||
uint8_t byteCode[sizeof(void*)];
|
||||
uint8_t line[sizeof(uint32_t)];
|
||||
uint8_t column[sizeof(uint32_t)];
|
||||
uint8_t executionStateDepth[sizeof(uint32_t)];
|
||||
};
|
||||
|
||||
struct VariableObjectInfo {
|
||||
uint8_t subType;
|
||||
uint8_t index[sizeof(uint32_t)];
|
||||
};
|
||||
|
||||
uint32_t appendToActiveObjects(Object* object);
|
||||
bool doEval(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, uint8_t* buffer, size_t length);
|
||||
void getBacktrace(ExecutionState* state, uint32_t minDepth, uint32_t maxDepth, bool getTotal);
|
||||
void getScopeChain(ExecutionState* state, uint32_t stateIndex);
|
||||
void getScopeVariables(ExecutionState* state, uint32_t stateIndex, uint32_t index);
|
||||
|
||||
bool m_exitClient : 1;
|
||||
bool m_pendingWait : 1;
|
||||
bool m_waitForResume : 1;
|
||||
String* m_clientSourceData;
|
||||
String* m_clientSourceName;
|
||||
|
||||
Vector<uintptr_t, GCUtil::gc_malloc_atomic_allocator<uintptr_t>> m_releasedFunctions;
|
||||
Vector<Object*, GCUtil::gc_malloc_allocator<Object*>> m_activeObjects;
|
||||
};
|
||||
|
||||
} // namespace Escargot
|
||||
#endif /* ESCARGOT_DEBUGGER */
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,137 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2026-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#ifndef __DebuggerDevtools__
|
||||
#define __DebuggerDevtools__
|
||||
|
||||
#include "Debugger.h"
|
||||
#include "DebuggerTcp.h"
|
||||
#include "interpreter/ByteCode.h"
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/rapidjson.h"
|
||||
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
namespace Escargot {
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
typedef SOCKET EscargotSocket;
|
||||
#else /* !WIN32 */
|
||||
typedef int EscargotSocket;
|
||||
#endif /* WIN32 */
|
||||
|
||||
struct ScriptInfo {
|
||||
uint8_t scriptId;
|
||||
String* url;
|
||||
String* source;
|
||||
};
|
||||
|
||||
typedef HashMap<AtomicString, Value, std::hash<AtomicString>, std::equal_to<AtomicString>, GCUtil::gc_malloc_allocator<std::pair<AtomicString const, Value>>> PropertyNameValueMap;
|
||||
typedef Vector<PropertyNameValueMap*, GCUtil::gc_malloc_allocator<PropertyNameValueMap*>> PropertyNameValueMapVector;
|
||||
|
||||
class DebuggerDevtools : public DebuggerTcp {
|
||||
public:
|
||||
DebuggerDevtools(EscargotSocket socket, String* skipSource)
|
||||
: DebuggerTcp(socket, skipSource, ESCARGOT_WS_BUFFER_SIZE, ESCARGOT_DEBUGGER_WEBSOCKET_TEXT_FRAME)
|
||||
{
|
||||
}
|
||||
|
||||
bool sendMessage(const std::string& msg, size_t length = -1);
|
||||
bool sendJSONDocument(const rapidjson::Document& document);
|
||||
void init(const char* options, Context* context) override;
|
||||
bool skipSourceCode(String* srcName) const override;
|
||||
|
||||
void parseCompleted(String* source, String* srcName, size_t originLineOffset, String* error = nullptr) override;
|
||||
bool stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) override;
|
||||
void byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock) override;
|
||||
void exceptionCaught(String* message, SavedStackTraceDataVector& exceptionTrace) override;
|
||||
void consoleOut(String* output) override;
|
||||
String* getClientSource(String** sourceName) override;
|
||||
bool getWaitBeforeExitClient() override;
|
||||
|
||||
|
||||
void sendPausedEvent(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state, bool breakpoint = false);
|
||||
|
||||
protected:
|
||||
bool processEvents(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, bool isBlockingRequest = true) override;
|
||||
|
||||
private:
|
||||
bool evaluate(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool sendProperties(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool resume(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool stepOver(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool stepOut(rapidjson::Document& jsonMessage, ExecutionState* state);
|
||||
bool stepInto(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool sendSourceCode(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool replyOK(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool enableNetwork(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool enableDebugger(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool enableRuntime(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool enableProfiler(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool setPauseOnExceptions(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool setBreakpointsActive(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool setBreakpointByUrl(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool removeBreakpoint(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool sendPossibleBreakpoints(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool takeHeapSnapshot(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
bool replyMethodNotFound(rapidjson::Document& jsonMessage, ExecutionState* state = nullptr);
|
||||
|
||||
uint32_t registerValuesMap(PropertyNameValueMap* newPropertyMap);
|
||||
|
||||
static bool compareBreakpointLocations(const BreakpointByteCodeLocation& a, const BreakpointByteCodeLocation& b)
|
||||
{
|
||||
if (LIKELY(a.line != b.line)) {
|
||||
return a.line < b.line;
|
||||
}
|
||||
return a.byteCode->m_loc.column <= b.byteCode->m_loc.column;
|
||||
}
|
||||
|
||||
uint8_t registerScript(String* url, String* source);
|
||||
|
||||
bool m_networkEnabled = false;
|
||||
bool m_debuggerEnabled = false;
|
||||
bool m_runtimeEnabled = false;
|
||||
bool m_profilerEnabled = false;
|
||||
bool m_pauseOnExceptions = false;
|
||||
bool m_breakpointsActive = false;
|
||||
bool m_startBreakpoint = true;
|
||||
|
||||
std::unordered_map<uint8_t, ScriptInfo> m_scriptsById;
|
||||
std::unordered_map<std::string, uint8_t> m_scriptIdByUrl;
|
||||
uint8_t m_nextScriptId = 1;
|
||||
std::unordered_map<uint8_t, std::set<BreakpointByteCodeLocation, decltype(compareBreakpointLocations)*>> m_breakpointInfo;
|
||||
PropertyNameValueMapVector m_propertyMapsById;
|
||||
uint32_t m_objectIdVectorIndexOffset = 0;
|
||||
uint32_t m_nextObjectId = 0;
|
||||
uint32_t m_nextCallFrameId = 1;
|
||||
|
||||
std::vector<std::string> m_pendingMessages;
|
||||
std::set<ByteCode*> m_setBreakPoints; // stores set breakpoints for enabling/disabling in bulk with the `Deactivate Breakpoints` button
|
||||
};
|
||||
|
||||
using MessageHandler = bool (DebuggerDevtools::*)(rapidjson::Document&, ExecutionState* state);
|
||||
|
||||
struct MessageType {
|
||||
const char* methodName;
|
||||
MessageHandler handler;
|
||||
};
|
||||
} // namespace Escargot
|
||||
#endif /* ESCARGOT_DEBUGGER */
|
||||
|
||||
#endif
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2026-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "DebuggerDevtoolsMessageBuilder.h"
|
||||
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
|
||||
#include "Escargot.h"
|
||||
|
||||
#include "runtime/String.h" // for split function
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/prettywriter.h"
|
||||
#include "rapidjson/stringbuffer.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
std::string DebuggerDevtoolsMessageBuilder::buildEmptyMessage(const uint32_t id)
|
||||
{
|
||||
char buffer[64];
|
||||
const int written = snprintf(buffer, sizeof(buffer),
|
||||
R"({"id":%u,"result":{}})",
|
||||
id);
|
||||
|
||||
if (written < 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { buffer, static_cast<size_t>(written) };
|
||||
}
|
||||
|
||||
std::string DebuggerDevtoolsMessageBuilder::buildSourceCodeMessage(const uint8_t requestId, const String* source)
|
||||
{
|
||||
if (!source || !source->is8Bit()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const LChar* sourceCode = source->characters8();
|
||||
const size_t sourceCodeLength = source->length();
|
||||
|
||||
rapidjson::StringBuffer sb;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
|
||||
writer.String(reinterpret_cast<const rapidjson::Writer<rapidjson::GenericStringBuffer<rapidjson::UTF8<>>>::Ch*>(sourceCode), sourceCodeLength);
|
||||
const std::string escaped = sb.GetString();
|
||||
|
||||
// FIXME: buffer size depends on length of source.
|
||||
char buffer[4096];
|
||||
const int written = snprintf(buffer, sizeof(buffer),
|
||||
"{\"id\":%u,"
|
||||
"\"result\":{"
|
||||
"\"scriptSource\":%s"
|
||||
"}"
|
||||
"}",
|
||||
requestId,
|
||||
escaped.c_str());
|
||||
|
||||
if (written < 0 || static_cast<size_t>(written) >= sizeof(buffer)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return { buffer, static_cast<size_t>(written) };
|
||||
}
|
||||
|
||||
} // namespace Escargot
|
||||
|
||||
#endif /* ESCARGOT_DEBUGGER */
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2026-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#ifndef __DebuggerDevtoolsMessageBuilder__
|
||||
#define __DebuggerDevtoolsMessageBuilder__
|
||||
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/rapidjson.h"
|
||||
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
namespace Escargot {
|
||||
|
||||
class String;
|
||||
|
||||
class DebuggerDevtoolsMessageBuilder {
|
||||
public:
|
||||
static std::string buildEmptyMessage(uint32_t id);
|
||||
static std::string buildScriptParsedMessage(uint8_t scriptId, const String* source, const String* srcName);
|
||||
static std::string buildSourceCodeMessage(uint8_t requestId, const String* source);
|
||||
};
|
||||
|
||||
} // namespace Escargot
|
||||
#endif /* ESCARGOT_DEBUGGER */
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,270 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2016-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#ifndef __DebuggerEscargot__
|
||||
#define __DebuggerEscargot__
|
||||
|
||||
#include "Debugger.h"
|
||||
#include "DebuggerTcp.h"
|
||||
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
|
||||
#define ESCARGOT_DEBUGGER_VERSION 1
|
||||
#define ESCARGOT_DEBUGGER_MAX_VARIABLE_LENGTH 128
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
class Context;
|
||||
class Object;
|
||||
class String;
|
||||
class ExecutionState;
|
||||
class ByteCodeBlock;
|
||||
class InterpretedCodeBlock;
|
||||
|
||||
class DebuggerEscargot : public DebuggerTcp {
|
||||
public:
|
||||
// Messages sent by Escargot to the debugger client
|
||||
enum {
|
||||
ESCARGOT_MESSAGE_VERSION = 0,
|
||||
ESCARGOT_MESSAGE_CONFIGURATION = 1,
|
||||
ESCARGOT_MESSAGE_CLOSE_CONNECTION = 2,
|
||||
ESCARGOT_MESSAGE_RELEASE_FUNCTION = 3,
|
||||
ESCARGOT_MESSAGE_PARSE_DONE = 4,
|
||||
ESCARGOT_MESSAGE_PARSE_ERROR = 5,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_SOURCE_8BIT = 6,
|
||||
ESCARGOT_MESSAGE_SOURCE_8BIT_END = 7,
|
||||
ESCARGOT_MESSAGE_SOURCE_16BIT = 8,
|
||||
ESCARGOT_MESSAGE_SOURCE_16BIT_END = 9,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_FILE_NAME_8BIT = 10,
|
||||
ESCARGOT_MESSAGE_FILE_NAME_8BIT_END = 11,
|
||||
ESCARGOT_MESSAGE_FILE_NAME_16BIT = 12,
|
||||
ESCARGOT_MESSAGE_FILE_NAME_16BIT_END = 13,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_FUNCTION_NAME_8BIT = 14,
|
||||
ESCARGOT_MESSAGE_FUNCTION_NAME_8BIT_END = 15,
|
||||
ESCARGOT_MESSAGE_FUNCTION_NAME_16BIT = 16,
|
||||
ESCARGOT_MESSAGE_FUNCTION_NAME_16BIT_END = 17,
|
||||
ESCARGOT_MESSAGE_BREAKPOINT_LOCATION = 18,
|
||||
ESCARGOT_MESSAGE_FUNCTION_PTR = 19,
|
||||
ESCARGOT_MESSAGE_BREAKPOINT_HIT = 20,
|
||||
ESCARGOT_MESSAGE_EXCEPTION_HIT = 21,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_EVAL_RESULT_8BIT = 22,
|
||||
ESCARGOT_MESSAGE_EVAL_RESULT_8BIT_END = 23,
|
||||
ESCARGOT_MESSAGE_EVAL_RESULT_16BIT = 24,
|
||||
ESCARGOT_MESSAGE_EVAL_RESULT_16BIT_END = 25,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_EVAL_FAILED_8BIT = 26,
|
||||
ESCARGOT_MESSAGE_EVAL_FAILED_8BIT_END = 27,
|
||||
ESCARGOT_MESSAGE_EVAL_FAILED_16BIT = 28,
|
||||
ESCARGOT_MESSAGE_EVAL_FAILED_16BIT_END = 29,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_WATCH_RESULT_8BIT = 30,
|
||||
ESCARGOT_MESSAGE_WATCH_RESULT_8BIT_END = 31,
|
||||
ESCARGOT_MESSAGE_WATCH_RESULT_16BIT = 32,
|
||||
ESCARGOT_MESSAGE_WATCH_RESULT_16BIT_END = 33,
|
||||
ESCARGOT_MESSAGE_BACKTRACE_TOTAL = 34,
|
||||
ESCARGOT_MESSAGE_BACKTRACE = 35,
|
||||
ESCARGOT_MESSAGE_BACKTRACE_END = 36,
|
||||
ESCARGOT_MESSAGE_SCOPE_CHAIN = 37,
|
||||
ESCARGOT_MESSAGE_SCOPE_CHAIN_END = 38,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_STRING_8BIT = 39,
|
||||
ESCARGOT_MESSAGE_STRING_8BIT_END = 40,
|
||||
ESCARGOT_MESSAGE_STRING_16BIT = 41,
|
||||
ESCARGOT_MESSAGE_STRING_16BIT_END = 42,
|
||||
ESCARGOT_MESSAGE_VARIABLE = 43,
|
||||
ESCARGOT_MESSAGE_PRINT = 44,
|
||||
ESCARGOT_MESSAGE_EXCEPTION = 45,
|
||||
ESCARGOT_MESSAGE_EXCEPTION_BACKTRACE = 46,
|
||||
ESCARGOT_DEBUGGER_WAIT_FOR_SOURCE = 47,
|
||||
ESCARGOT_DEBUGGER_WAITING_AFTER_PENDING = 48,
|
||||
ESCARGOT_DEBUGGER_WAIT_FOR_WAIT_EXIT = 49,
|
||||
ESCARGOT_DEBUGGER_SNAPSHOT_FINISHED = 50
|
||||
};
|
||||
|
||||
// Messages sent by the debugger client to Escargot
|
||||
enum {
|
||||
ESCARGOT_MESSAGE_FUNCTION_RELEASED = 0,
|
||||
ESCARGOT_MESSAGE_UPDATE_BREAKPOINT = 1,
|
||||
ESCARGOT_MESSAGE_CONTINUE = 2,
|
||||
ESCARGOT_MESSAGE_STEP = 3,
|
||||
ESCARGOT_MESSAGE_NEXT = 4,
|
||||
ESCARGOT_MESSAGE_FINISH = 5,
|
||||
ESCARGOT_MESSAGE_RESTART = 6,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_EVAL_8BIT_START = 7,
|
||||
ESCARGOT_MESSAGE_EVAL_8BIT = 8,
|
||||
ESCARGOT_MESSAGE_EVAL_16BIT_START = 9,
|
||||
ESCARGOT_MESSAGE_EVAL_16BIT = 10,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_8BIT_START = 11,
|
||||
ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_8BIT = 12,
|
||||
ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_16BIT_START = 13,
|
||||
ESCARGOT_MESSAGE_EVAL_WITHOUT_STOP_16BIT = 14,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_MESSAGE_WATCH_8BIT_START = 15,
|
||||
ESCARGOT_MESSAGE_WATCH_8BIT = 16,
|
||||
ESCARGOT_MESSAGE_WATCH_16BIT_START = 17,
|
||||
ESCARGOT_MESSAGE_WATCH_16BIT = 18,
|
||||
ESCARGOT_MESSAGE_GET_BACKTRACE = 19,
|
||||
ESCARGOT_MESSAGE_GET_SCOPE_CHAIN = 20,
|
||||
ESCARGOT_MESSAGE_GET_SCOPE_VARIABLES = 21,
|
||||
ESCARGOT_MESSAGE_GET_OBJECT = 22,
|
||||
// These four must be in the same order.
|
||||
ESCARGOT_DEBUGGER_CLIENT_SOURCE_8BIT_START = 23,
|
||||
ESCARGOT_DEBUGGER_CLIENT_SOURCE_8BIT = 24,
|
||||
ESCARGOT_DEBUGGER_CLIENT_SOURCE_16BIT_START = 25,
|
||||
ESCARGOT_DEBUGGER_CLIENT_SOURCE_16BIT = 26,
|
||||
ESCARGOT_DEBUGGER_THERE_WAS_NO_SOURCE = 27,
|
||||
ESCARGOT_DEBUGGER_PENDING_CONFIG = 28,
|
||||
ESCARGOT_DEBUGGER_PENDING_RESUME = 29,
|
||||
ESCARGOT_DEBUGGER_WAIT_BEFORE_EXIT = 30,
|
||||
ESCARGOT_DEBUGGER_TAKE_HEAP_SNAPSHOT = 31,
|
||||
ESCARGOT_DEBUGGER_STOP = 32
|
||||
};
|
||||
|
||||
|
||||
// Environment record types
|
||||
enum {
|
||||
ESCARGOT_RECORD_GLOBAL_ENVIRONMENT = 0,
|
||||
ESCARGOT_RECORD_FUNCTION_ENVIRONMENT = 1,
|
||||
ESCARGOT_RECORD_DECLARATIVE_ENVIRONMENT = 2,
|
||||
ESCARGOT_RECORD_OBJECT_ENVIRONMENT = 3,
|
||||
ESCARGOT_RECORD_MODULE_ENVIRONMENT = 4,
|
||||
ESCARGOT_RECORD_UNKNOWN_ENVIRONMENT = 5,
|
||||
};
|
||||
|
||||
// Variable types
|
||||
enum {
|
||||
ESCARGOT_VARIABLE_END = 0,
|
||||
ESCARGOT_VARIABLE_UNACCESSIBLE = 1,
|
||||
ESCARGOT_VARIABLE_UNDEFINED = 2,
|
||||
ESCARGOT_VARIABLE_NULL = 3,
|
||||
ESCARGOT_VARIABLE_TRUE = 4,
|
||||
ESCARGOT_VARIABLE_FALSE = 5,
|
||||
ESCARGOT_VARIABLE_NUMBER = 6,
|
||||
ESCARGOT_VARIABLE_STRING = 7,
|
||||
ESCARGOT_VARIABLE_SYMBOL = 8,
|
||||
ESCARGOT_VARIABLE_BIGINT = 9,
|
||||
// Only object types should be defined after this point.
|
||||
ESCARGOT_VARIABLE_OBJECT = 10,
|
||||
ESCARGOT_VARIABLE_ARRAY = 11,
|
||||
ESCARGOT_VARIABLE_FUNCTION = 12,
|
||||
ESCARGOT_VARIABLE_TYPE_MASK = 0x3f,
|
||||
ESCARGOT_VARIABLE_LONG_NAME = 0x40,
|
||||
ESCARGOT_VARIABLE_LONG_VALUE = 0x80,
|
||||
};
|
||||
|
||||
inline bool pendingWait(void)
|
||||
{
|
||||
return m_pendingWait;
|
||||
}
|
||||
|
||||
inline bool connected(void)
|
||||
{
|
||||
return enabled();
|
||||
}
|
||||
|
||||
void sendType(uint8_t type);
|
||||
void sendSubtype(uint8_t type, uint8_t subType);
|
||||
void sendString(uint8_t type, const String* string);
|
||||
void sendPointer(uint8_t type, const void* ptr);
|
||||
|
||||
void init(const char* options, Context* context) override;
|
||||
void parseCompleted(String* source, String* srcName, size_t originLineOffset, String* error = nullptr) override;
|
||||
bool stopAtBreakpoint(ByteCodeBlock* byteCodeBlock, uint32_t offset, ExecutionState* state) override;
|
||||
void byteCodeReleaseNotification(ByteCodeBlock* byteCodeBlock) override;
|
||||
void exceptionCaught(String* message, SavedStackTraceDataVector& exceptionTrace) override;
|
||||
void consoleOut(String* output) override;
|
||||
String* getClientSource(String** sourceName) override;
|
||||
bool getWaitBeforeExitClient() override;
|
||||
|
||||
void sendBacktraceInfo(uint8_t type, ByteCodeBlock* byteCodeBlock, uint32_t line, uint32_t column, uint32_t executionStateDepth);
|
||||
void sendVariableObjectInfo(uint8_t subType, Object* object);
|
||||
void waitForResolvingPendingBreakpoints();
|
||||
|
||||
DebuggerEscargot(EscargotSocket socket, String* skipSource)
|
||||
: DebuggerTcp(socket, skipSource, ESCARGOT_WS_HEADER_BASE_SIZE + ESCARGOT_WS_MASK_SIZE + ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH, ESCARGOT_DEBUGGER_WEBSOCKET_BINARY_FRAME)
|
||||
, m_exitClient(false)
|
||||
, m_pendingWait(false)
|
||||
, m_waitForResume(false)
|
||||
, m_watchEval(false)
|
||||
, m_clientSourceData(nullptr)
|
||||
, m_clientSourceName(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
bool processEvents(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, bool isBlockingRequest = true) override;
|
||||
|
||||
private:
|
||||
// Packed structure definitions to reduce network traffic
|
||||
|
||||
struct MessageVersion {
|
||||
uint8_t littleEndian;
|
||||
uint8_t version[sizeof(uint32_t)];
|
||||
};
|
||||
|
||||
struct MessageConfiguration {
|
||||
uint8_t maxMessageSize;
|
||||
uint8_t pointerSize;
|
||||
};
|
||||
|
||||
struct FunctionInfo {
|
||||
uint8_t byteCodeStart[sizeof(void*)];
|
||||
uint8_t startLine[sizeof(uint32_t)];
|
||||
uint8_t startColumn[sizeof(uint32_t)];
|
||||
};
|
||||
|
||||
struct BreakpointOffset {
|
||||
uint8_t byteCodeStart[sizeof(void*)];
|
||||
uint8_t offset[sizeof(uint32_t)];
|
||||
};
|
||||
|
||||
struct BacktraceInfo {
|
||||
uint8_t byteCode[sizeof(void*)];
|
||||
uint8_t line[sizeof(uint32_t)];
|
||||
uint8_t column[sizeof(uint32_t)];
|
||||
uint8_t executionStateDepth[sizeof(uint32_t)];
|
||||
};
|
||||
|
||||
struct VariableObjectInfo {
|
||||
uint8_t subType;
|
||||
uint8_t index[sizeof(uint32_t)];
|
||||
};
|
||||
|
||||
bool doEval(ExecutionState* state, Optional<ByteCodeBlock*> byteCodeBlock, uint8_t* buffer, size_t length);
|
||||
void getBacktrace(ExecutionState* state, uint32_t minDepth, uint32_t maxDepth, bool getTotal);
|
||||
void getScopeChain(ExecutionState* state, uint32_t stateIndex);
|
||||
void getScopeVariables(ExecutionState* state, uint32_t stateIndex, uint32_t index);
|
||||
|
||||
bool m_exitClient : 1;
|
||||
bool m_pendingWait : 1;
|
||||
bool m_waitForResume : 1;
|
||||
bool m_watchEval : 1;
|
||||
String* m_clientSourceData;
|
||||
String* m_clientSourceName;
|
||||
};
|
||||
|
||||
} // namespace Escargot
|
||||
#endif /* ESCARGOT_DEBUGGER */
|
||||
|
||||
#endif
|
||||
|
|
@ -1,321 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2026-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#include "Escargot.h"
|
||||
#include "DebuggerHttpRouter.h"
|
||||
#include "DebuggerTcp.h"
|
||||
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
namespace Escargot {
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
static uint8_t toBase64Character(uint8_t value)
|
||||
{
|
||||
if (value < 26) {
|
||||
return static_cast<uint8_t>(value + 'A');
|
||||
}
|
||||
|
||||
if (value < 52) {
|
||||
return static_cast<uint8_t>(value - 26 + 'a');
|
||||
}
|
||||
|
||||
if (value < 62) {
|
||||
return static_cast<uint8_t>(value - 52 + '0');
|
||||
}
|
||||
|
||||
if (value == 62) {
|
||||
return '+';
|
||||
}
|
||||
|
||||
return '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode a byte sequence into Base64 string.
|
||||
*/
|
||||
static void toBase64(const uint8_t* source, uint8_t* destination, size_t length)
|
||||
{
|
||||
while (length >= 3) {
|
||||
uint8_t value = (source[0] >> 2);
|
||||
destination[0] = toBase64Character(value);
|
||||
|
||||
value = static_cast<uint8_t>(((source[0] << 4) | (source[1] >> 4)) & 0x3f);
|
||||
destination[1] = toBase64Character(value);
|
||||
|
||||
value = static_cast<uint8_t>(((source[1] << 2) | (source[2] >> 6)) & 0x3f);
|
||||
destination[2] = toBase64Character(value);
|
||||
|
||||
value = static_cast<uint8_t>(source[2] & 0x3f);
|
||||
destination[3] = toBase64Character(value);
|
||||
|
||||
source += 3;
|
||||
destination += 4;
|
||||
length -= 3;
|
||||
}
|
||||
}
|
||||
|
||||
bool DebuggerHttpRouter::requestStartsWith(const uint8_t* buffer, const size_t length, const char* prefix, const size_t prefixLength)
|
||||
{
|
||||
if (length < prefixLength)
|
||||
return false;
|
||||
|
||||
return memcmp(buffer, prefix, prefixLength) == 0;
|
||||
}
|
||||
|
||||
static bool buildHttpResponse(const char* body, uint8_t* buffer, size_t bufferSize, size_t& outLen)
|
||||
{
|
||||
size_t bodyLen = strlen(body);
|
||||
|
||||
int len = snprintf(reinterpret_cast<char*>(buffer), bufferSize,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: application/json; charset=UTF-8\r\n"
|
||||
"Cache-Control: no-cache\r\n"
|
||||
"Content-Length: %zu\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n"
|
||||
"%s",
|
||||
bodyLen,
|
||||
body);
|
||||
|
||||
if (len < 0 || static_cast<size_t>(len) >= bufferSize)
|
||||
return false;
|
||||
|
||||
outLen = static_cast<size_t>(len);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool buildVersionResponse(uint8_t* buffer, size_t bufferSize, size_t& outLen)
|
||||
{
|
||||
static constexpr const char body[] = "{"
|
||||
"\"Browser\":\"Escargot/1.0\","
|
||||
"\"Protocol-Version\":\"1.3\""
|
||||
"}";
|
||||
|
||||
return buildHttpResponse(body, buffer, bufferSize, outLen);
|
||||
}
|
||||
|
||||
static bool buildListResponse(uint8_t* buffer, size_t bufferSize, const char* ip, uint16_t port, size_t& outLen)
|
||||
{
|
||||
char body[512];
|
||||
|
||||
int bodyLen = snprintf(body, sizeof(body),
|
||||
"[{"
|
||||
"\"description\":\"Escargot CDP target\","
|
||||
"\"devtoolsFrontendUrl\":\"/devtools/inspector.html?ws=%s:%u/devtools/page/1\","
|
||||
"\"id\":\"1\","
|
||||
"\"title\":\"Escargot\","
|
||||
"\"type\":\"node\","
|
||||
"\"url\":\"file:///\","
|
||||
"\"webSocketDebuggerUrl\":\"ws://%s:%u/devtools/page/1\""
|
||||
"}]",
|
||||
ip, static_cast<unsigned>(port),
|
||||
ip, static_cast<unsigned>(port));
|
||||
|
||||
if (bodyLen < 0 || static_cast<size_t>(bodyLen) >= sizeof(body)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return buildHttpResponse(body, buffer, bufferSize, outLen);
|
||||
}
|
||||
|
||||
static bool webSocketHandshake(EscargotSocket socket, uint8_t* buffer, size_t length)
|
||||
{
|
||||
uint8_t* websocketKey = buffer;
|
||||
|
||||
constexpr char expectedWebsocketKey[] = "Sec-WebSocket-Key:";
|
||||
constexpr size_t expectedWebsocketKeyLength = sizeof(expectedWebsocketKey) - 1;
|
||||
|
||||
while (true) {
|
||||
if (length < expectedWebsocketKeyLength) {
|
||||
ESCARGOT_LOG_ERROR("Sec-WebSocket-Key not found.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (websocketKey[0] == 'S'
|
||||
&& websocketKey[-1] == '\n'
|
||||
&& websocketKey[-2] == '\r'
|
||||
&& memcmp(websocketKey, expectedWebsocketKey, expectedWebsocketKeyLength) == 0) {
|
||||
websocketKey += expectedWebsocketKeyLength;
|
||||
break;
|
||||
}
|
||||
|
||||
websocketKey++;
|
||||
}
|
||||
|
||||
/* String terminated by double newlines. */
|
||||
while (*websocketKey == ' ') {
|
||||
websocketKey++;
|
||||
}
|
||||
|
||||
uint8_t* websocketKeyEnd = websocketKey;
|
||||
|
||||
while (*websocketKeyEnd > ' ') {
|
||||
websocketKeyEnd++;
|
||||
}
|
||||
|
||||
/* Since the buffer is not needed anymore it can
|
||||
* be reused for storing the SHA-1 key and Base64 string. */
|
||||
constexpr size_t sha1Length = 20;
|
||||
|
||||
DebuggerTcp::computeSha1(websocketKey,
|
||||
static_cast<size_t>(websocketKeyEnd - websocketKey),
|
||||
reinterpret_cast<const uint8_t*>("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"),
|
||||
36,
|
||||
buffer);
|
||||
|
||||
/* The SHA-1 key is 20 bytes long but toBase64 expects a length
|
||||
* divisible by 3 so an extra 0 is appended at the end. */
|
||||
buffer[sha1Length] = 0;
|
||||
|
||||
toBase64(buffer, buffer + sha1Length + 1, sha1Length + 1);
|
||||
|
||||
/* Last value must be replaced by equal sign. */
|
||||
constexpr uint8_t responsePrefix[] = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ";
|
||||
|
||||
if (!DebuggerTcp::tcpSend(socket, responsePrefix, sizeof(responsePrefix) - 1)
|
||||
|| !DebuggerTcp::tcpSend(socket, buffer + sha1Length + 1, 27)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr uint8_t responseSuffix[] = "=\r\n\r\n";
|
||||
return DebuggerTcp::tcpSend(socket, responseSuffix, sizeof(responseSuffix) - 1);
|
||||
}
|
||||
|
||||
bool DebuggerHttpRouter::webSocketEstablished() const
|
||||
{
|
||||
return m_client != DebuggerClient::None;
|
||||
}
|
||||
|
||||
DebuggerClient DebuggerHttpRouter::client() const
|
||||
{
|
||||
return m_client;
|
||||
}
|
||||
|
||||
bool handleWebSocketRequest(const RequestContext& ctx)
|
||||
{
|
||||
return webSocketHandshake(ctx.socket, ctx.request, ctx.requestLength);
|
||||
}
|
||||
|
||||
bool handleJsonVersion(const RequestContext& ctx)
|
||||
{
|
||||
uint8_t buffer[1024];
|
||||
size_t responseLength = 0;
|
||||
|
||||
if (!buildVersionResponse(buffer, sizeof(buffer), responseLength)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return DebuggerTcp::tcpSend(ctx.socket, buffer, responseLength);
|
||||
}
|
||||
|
||||
bool handleJsonList(const RequestContext& ctx)
|
||||
{
|
||||
struct sockaddr_in addr{};
|
||||
socklen_t len = sizeof(addr);
|
||||
|
||||
getsockname(ctx.socket, reinterpret_cast<struct sockaddr*>(&addr), &len);
|
||||
|
||||
char ip[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &addr.sin_addr, ip, sizeof(ip));
|
||||
const uint16_t port = ntohs(addr.sin_port);
|
||||
|
||||
uint8_t buffer[1024];
|
||||
size_t responseLength = 0;
|
||||
|
||||
if (!buildListResponse(buffer, sizeof(buffer), ip, port, responseLength)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return DebuggerTcp::tcpSend(ctx.socket, buffer, responseLength);
|
||||
}
|
||||
|
||||
static bool readHttpMessage(EscargotSocket socket, uint8_t* buffer, size_t capacity, size_t& messageLength)
|
||||
{
|
||||
size_t remainingLength = capacity;
|
||||
uint8_t* message = buffer;
|
||||
|
||||
while (true) {
|
||||
size_t receivedLength = 0;
|
||||
if (!DebuggerTcp::tcpReceive(socket, message, remainingLength, &receivedLength)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
message += receivedLength;
|
||||
remainingLength -= receivedLength;
|
||||
|
||||
if (message > (buffer + 4) && memcmp(message - 4, "\r\n\r\n", 4) == 0) {
|
||||
messageLength = static_cast<size_t>(message - buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (remainingLength == 0) {
|
||||
ESCARGOT_LOG_ERROR("WebSocket Error: Request too long\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
constexpr Route route(const char (&prefix)[N], DebuggerClient client, RouteHandler handler)
|
||||
{
|
||||
return Route{ prefix, N - 1, client, handler };
|
||||
}
|
||||
|
||||
bool DebuggerHttpRouter::handleHttpRequest(EscargotSocket socket)
|
||||
{
|
||||
uint8_t buffer[1024];
|
||||
size_t messageLength = 0;
|
||||
|
||||
if (!readHttpMessage(socket, buffer, sizeof(buffer), messageLength)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static constexpr Route routes[] = {
|
||||
route("GET /escargot-debugger", DebuggerClient::Escargot, handleWebSocketRequest),
|
||||
route("GET /devtools/page/1", DebuggerClient::DevTools, handleWebSocketRequest),
|
||||
route("GET /json/version", DebuggerClient::None, handleJsonVersion),
|
||||
route("GET /json/list", DebuggerClient::None, handleJsonList),
|
||||
route("GET /json", DebuggerClient::None, handleJsonList)
|
||||
};
|
||||
|
||||
for (const auto& route : routes) {
|
||||
if (!requestStartsWith(buffer, messageLength, route.prefix, route.prefixLength)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_client = route.client;
|
||||
// Skip the matched route prefix
|
||||
uint8_t* remainder = buffer + route.prefixLength;
|
||||
size_t remainderLength = messageLength - route.prefixLength;
|
||||
|
||||
return route.handler(RequestContext{ socket, remainder, remainderLength });
|
||||
}
|
||||
|
||||
ESCARGOT_LOG_ERROR("Unsupported http request\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Escargot
|
||||
#endif /* ESCARGOT_DEBUGGER */
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2026-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#ifndef __DebuggerHttpRouter__
|
||||
#define __DebuggerHttpRouter__
|
||||
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
namespace Escargot {
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
typedef SOCKET EscargotSocket;
|
||||
#else /* !WIN32 */
|
||||
typedef int EscargotSocket;
|
||||
#endif /* WIN32 */
|
||||
|
||||
enum class DebuggerClient : uint8_t {
|
||||
None,
|
||||
Escargot,
|
||||
DevTools
|
||||
};
|
||||
|
||||
struct RequestContext {
|
||||
EscargotSocket socket;
|
||||
uint8_t* request;
|
||||
size_t requestLength;
|
||||
};
|
||||
|
||||
using RouteHandler = bool (*)(const RequestContext&);
|
||||
|
||||
struct Route {
|
||||
const char* prefix;
|
||||
size_t prefixLength;
|
||||
DebuggerClient client;
|
||||
RouteHandler handler;
|
||||
};
|
||||
|
||||
class DebuggerHttpRouter {
|
||||
public:
|
||||
DebuggerHttpRouter() = default;
|
||||
|
||||
static bool requestStartsWith(const uint8_t* buffer, size_t length, const char* prefix, size_t prefixLength);
|
||||
bool handleHttpRequest(EscargotSocket socket);
|
||||
bool webSocketEstablished() const;
|
||||
DebuggerClient client() const;
|
||||
|
||||
private:
|
||||
DebuggerClient m_client{ DebuggerClient::None };
|
||||
};
|
||||
|
||||
} // namespace Escargot
|
||||
#endif /* ESCARGOT_DEBUGGER */
|
||||
|
||||
#endif
|
||||
|
|
@ -19,9 +19,6 @@
|
|||
|
||||
#include "Escargot.h"
|
||||
#include "DebuggerTcp.h"
|
||||
#include "DebuggerDevtools.h"
|
||||
#include "DebuggerEscargot.h"
|
||||
#include "DebuggerHttpRouter.h"
|
||||
#include "runtime/String.h" // for split function
|
||||
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
|
|
@ -113,7 +110,7 @@ static inline void tcpCloseSocket(EscargotSocket socket)
|
|||
#endif /* WIN32 */
|
||||
}
|
||||
|
||||
bool DebuggerTcp::tcpSend(EscargotSocket socket, const uint8_t* message, size_t messageLength)
|
||||
static bool tcpSend(EscargotSocket socket, const uint8_t* message, size_t messageLength)
|
||||
{
|
||||
do {
|
||||
#ifdef OS_POSIX
|
||||
|
|
@ -125,7 +122,7 @@ bool DebuggerTcp::tcpSend(EscargotSocket socket, const uint8_t* message, size_t
|
|||
}
|
||||
#endif /* OS_POSIX */
|
||||
|
||||
ssize_t sentBytes = Escargot::send(socket, message, messageLength, 0);
|
||||
ssize_t sentBytes = send(socket, message, messageLength, 0);
|
||||
|
||||
if (sentBytes < 0) {
|
||||
int errorNumber = tcpGetErrno();
|
||||
|
|
@ -145,7 +142,7 @@ bool DebuggerTcp::tcpSend(EscargotSocket socket, const uint8_t* message, size_t
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DebuggerTcp::tcpReceive(EscargotSocket socket, uint8_t* message, size_t maxLength, size_t* receivedLength)
|
||||
static bool tcpReceive(EscargotSocket socket, uint8_t* message, size_t maxLength, size_t* receivedLength)
|
||||
{
|
||||
*receivedLength = 0;
|
||||
|
||||
|
|
@ -165,168 +162,152 @@ bool DebuggerTcp::tcpReceive(EscargotSocket socket, uint8_t* message, size_t max
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DebuggerTcp::send(const uint8_t type, const void* buffer, const size_t length)
|
||||
static uint8_t toBase64Character(uint8_t value)
|
||||
{
|
||||
ASSERT(enabled());
|
||||
ASSERT(m_websocketMessageType == ESCARGOT_DEBUGGER_WEBSOCKET_TEXT_FRAME
|
||||
|| (m_websocketMessageType == ESCARGOT_DEBUGGER_WEBSOCKET_BINARY_FRAME && length <= ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH));
|
||||
|
||||
if (length > ESCARGOT_WS_MAX_MESSAGE_LENGTH) {
|
||||
ESCARGOT_LOG_ERROR("Cannot send WebSocket payload: 64-bit payload length is not supported.\n");
|
||||
close(CloseAbortConnection);
|
||||
return false;
|
||||
if (value < 26) {
|
||||
return (uint8_t)(value + 'A');
|
||||
}
|
||||
|
||||
size_t headerLength = 0;
|
||||
uint8_t message[ESCARGOT_WS_HEADER_BASE_SIZE + ESCARGOT_WS_EXT_LEN16_SIZE + length];
|
||||
message[0] = ESCARGOT_DEBUGGER_WEBSOCKET_FIN_BIT | m_websocketMessageType;
|
||||
|
||||
// Server-to-client WebSocket frames are not masked,
|
||||
// therefore the masking key is not included in the header.
|
||||
if (length <= ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH) {
|
||||
const uint8_t type_byte = (m_websocketMessageType == ESCARGOT_DEBUGGER_WEBSOCKET_TEXT_FRAME ? 0 : 1);
|
||||
headerLength = ESCARGOT_WS_HEADER_SIZE - ESCARGOT_WS_MASK_SIZE + type_byte;
|
||||
|
||||
message[1] = static_cast<uint8_t>(length + type_byte);
|
||||
if (type_byte) {
|
||||
message[2] = type;
|
||||
}
|
||||
} else {
|
||||
headerLength = ESCARGOT_WS_HEADER_LEN16_SIZE - ESCARGOT_WS_MASK_SIZE;
|
||||
|
||||
message[1] = ESCARGOT_WS_MESSAGE_16BIT_LENGTH_MARKER;
|
||||
message[2] = static_cast<uint8_t>((length >> 8) & 0xFF);
|
||||
message[3] = static_cast<uint8_t>(length & 0xFF);
|
||||
if (value < 52) {
|
||||
return (uint8_t)(value - 26 + 'a');
|
||||
}
|
||||
|
||||
memcpy(message + headerLength, buffer, length);
|
||||
|
||||
if (!tcpSend(m_socket, message, headerLength + length)) {
|
||||
ESCARGOT_LOG_ERROR("Failed to send data via WebSocket connection.\n");
|
||||
close(CloseAbortConnection);
|
||||
return false;
|
||||
if (value < 62) {
|
||||
return (uint8_t)(value - 52 + '0');
|
||||
}
|
||||
|
||||
return true;
|
||||
if (value == 62) {
|
||||
return (uint8_t)'+';
|
||||
}
|
||||
|
||||
return (uint8_t)'/';
|
||||
}
|
||||
|
||||
bool DebuggerTcp::receive(uint8_t* buffer, size_t& length)
|
||||
/**
|
||||
* Encode a byte sequence into Base64 string.
|
||||
*/
|
||||
static void toBase64(const uint8_t* source, uint8_t* destination, size_t length)
|
||||
{
|
||||
size_t receivedLength = 0;
|
||||
while (length >= 3) {
|
||||
uint8_t value = (source[0] >> 2);
|
||||
destination[0] = toBase64Character(value);
|
||||
|
||||
if (m_payloadLength == 0 || m_receiveBufferFill < m_headerLength + m_payloadLength) {
|
||||
/* Cannot extract a whole message from the buffer. */
|
||||
if (!tcpReceive(m_socket,
|
||||
m_receiveBuffer + m_receiveBufferFill,
|
||||
m_bufferSize - m_receiveBufferFill,
|
||||
&receivedLength)) {
|
||||
ESCARGOT_LOG_ERROR("Failed to receive data from WebSocket connection.\n");
|
||||
close(CloseAbortConnection);
|
||||
return false;
|
||||
}
|
||||
value = (uint8_t)(((source[0] << 4) | (source[1] >> 4)) & 0x3f);
|
||||
destination[1] = toBase64Character(value);
|
||||
|
||||
if (receivedLength == 0 && m_receiveBufferFill < m_headerLength) {
|
||||
// ESCARGOT_LOG_INFO("Incomplete WebSocket frame header, waiting for more data.\n");
|
||||
return false;
|
||||
}
|
||||
value = (uint8_t)(((source[1] << 2) | (source[2] >> 6)) & 0x3f);
|
||||
destination[2] = toBase64Character(value);
|
||||
|
||||
m_receiveBufferFill = static_cast<uint32_t>(m_receiveBufferFill + receivedLength);
|
||||
value = (uint8_t)(source[2] & 0x3f);
|
||||
destination[3] = toBase64Character(value);
|
||||
|
||||
source += 3;
|
||||
destination += 4;
|
||||
length -= 3;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_payloadLength == 0) {
|
||||
if (m_receiveBufferFill < ESCARGOT_WS_HEADER_BASE_SIZE) {
|
||||
static bool webSocketHandshake(EscargotSocket socket)
|
||||
{
|
||||
uint8_t buffer[1024];
|
||||
size_t remainingLength = sizeof(buffer);
|
||||
uint8_t* message = buffer;
|
||||
|
||||
while (true) {
|
||||
size_t receivedLength;
|
||||
if (!tcpReceive(socket, message, remainingLength, &receivedLength)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((m_receiveBuffer[0] & ESCARGOT_DEBUGGER_WEBSOCKET_OPCODE_MASK) != m_websocketMessageType) {
|
||||
if ((m_receiveBuffer[0] & ESCARGOT_DEBUGGER_WEBSOCKET_OPCODE_MASK) == ESCARGOT_DEBUGGER_WEBSOCKET_CLOSE_FRAME) {
|
||||
close(CloseEndConnection);
|
||||
return false;
|
||||
}
|
||||
message += receivedLength;
|
||||
remainingLength -= receivedLength;
|
||||
|
||||
ESCARGOT_LOG_ERROR("Unsupported Websocket opcode.\n");
|
||||
close(CloseProtocolUnsupported);
|
||||
return false;
|
||||
if (message > (buffer + 4) && memcmp(message - 4, "\r\n\r\n", 4) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((m_receiveBuffer[0] & ~ESCARGOT_DEBUGGER_WEBSOCKET_OPCODE_MASK) != ESCARGOT_DEBUGGER_WEBSOCKET_FIN_BIT
|
||||
|| !(m_receiveBuffer[1] & ESCARGOT_DEBUGGER_WEBSOCKET_MASK_BIT)) {
|
||||
ESCARGOT_LOG_ERROR("Unsupported Websocket message.\n");
|
||||
close(CloseProtocolUnsupported);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t payloadLengthField = m_receiveBuffer[1] & ESCARGOT_DEBUGGER_WEBSOCKET_LENGTH_MASK;
|
||||
|
||||
if (payloadLengthField > ESCARGOT_WS_MESSAGE_16BIT_LENGTH_MARKER) {
|
||||
ESCARGOT_LOG_ERROR("64-bit WebSocket payload length is not supported.\n");
|
||||
close(CloseProtocolUnsupported);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (payloadLengthField <= ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH) {
|
||||
m_payloadLength = payloadLengthField;
|
||||
m_headerLength = ESCARGOT_WS_HEADER_SIZE;
|
||||
} else {
|
||||
ASSERT(payloadLengthField == ESCARGOT_WS_MESSAGE_16BIT_LENGTH_MARKER);
|
||||
// Ensure that the extended payload length field is available
|
||||
if (m_receiveBufferFill < ESCARGOT_WS_HEADER_LEN16_SIZE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_headerLength = ESCARGOT_WS_HEADER_LEN16_SIZE;
|
||||
m_payloadLength = (static_cast<uint16_t>(m_receiveBuffer[2]) << 8) | static_cast<uint16_t>(m_receiveBuffer[3]);
|
||||
}
|
||||
|
||||
if (m_payloadLength == 0) {
|
||||
ESCARGOT_LOG_ERROR("Invalid WebSocket payload length: zero-length messages are not supported.\n");
|
||||
close(CloseProtocolUnsupported);
|
||||
if (remainingLength == 0) {
|
||||
ESCARGOT_LOG_ERROR("WebSocket Error: Request too long\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t totalSize = m_headerLength + m_payloadLength;
|
||||
/* Check protocol. */
|
||||
const char expectedProtocol[] = "GET /escargot-debugger";
|
||||
const size_t expectedProtocolLength = sizeof(expectedProtocol) - 1;
|
||||
|
||||
if (m_receiveBufferFill < totalSize) {
|
||||
if ((size_t)(message - buffer) < expectedProtocolLength
|
||||
|| memcmp(buffer, expectedProtocol, expectedProtocolLength) != 0) {
|
||||
ESCARGOT_LOG_ERROR("WebSocket Error: Invalid handshake format.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t* mask = m_receiveBuffer + m_headerLength - ESCARGOT_WS_MASK_SIZE;
|
||||
const uint8_t* mask_end = mask + ESCARGOT_WS_MASK_SIZE;
|
||||
const uint8_t* source = mask_end;
|
||||
uint8_t* buffer_end = buffer + m_payloadLength;
|
||||
uint8_t* websocketKey = buffer + expectedProtocolLength;
|
||||
|
||||
while (buffer < buffer_end) {
|
||||
*buffer++ = *source++ ^ *mask++;
|
||||
const char expectedWebsocketKey[] = "Sec-WebSocket-Key:";
|
||||
const size_t expectedWebsocketKeyLength = sizeof(expectedWebsocketKey) - 1;
|
||||
|
||||
if (mask >= mask_end) {
|
||||
mask -= 4;
|
||||
while (true) {
|
||||
if ((size_t)(message - websocketKey) < expectedWebsocketKeyLength) {
|
||||
ESCARGOT_LOG_ERROR("Sec-WebSocket-Key not found.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
*buffer_end = 0;
|
||||
|
||||
length = m_payloadLength;
|
||||
m_headerLength = ESCARGOT_WS_HEADER_SIZE;
|
||||
m_payloadLength = 0;
|
||||
if (websocketKey[0] == 'S'
|
||||
&& websocketKey[-1] == '\n'
|
||||
&& websocketKey[-2] == '\r'
|
||||
&& memcmp(websocketKey, expectedWebsocketKey, expectedWebsocketKeyLength) == 0) {
|
||||
websocketKey += expectedWebsocketKeyLength;
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_receiveBufferFill == totalSize) {
|
||||
m_receiveBufferFill = 0;
|
||||
return true;
|
||||
websocketKey++;
|
||||
}
|
||||
|
||||
m_receiveBufferFill = static_cast<uint32_t>(m_receiveBufferFill - totalSize);
|
||||
memmove(m_receiveBuffer, m_receiveBuffer + totalSize, m_receiveBufferFill);
|
||||
return true;
|
||||
/* String terminated by double newlines. */
|
||||
while (*websocketKey == ' ') {
|
||||
websocketKey++;
|
||||
}
|
||||
|
||||
uint8_t* websocketKeyEnd = websocketKey;
|
||||
|
||||
while (*websocketKeyEnd > ' ') {
|
||||
websocketKeyEnd++;
|
||||
}
|
||||
|
||||
/* Since the buffer is not needed anymore it can
|
||||
* be reused for storing the SHA-1 key and Base64 string. */
|
||||
const size_t sha1Length = 20;
|
||||
|
||||
DebuggerTcp::computeSha1(websocketKey,
|
||||
(size_t)(websocketKeyEnd - websocketKey),
|
||||
(const uint8_t*)"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
|
||||
36,
|
||||
buffer);
|
||||
|
||||
/* The SHA-1 key is 20 bytes long but toBase64 expects a length
|
||||
* divisible by 3 so an extra 0 is appended at the end. */
|
||||
buffer[sha1Length] = 0;
|
||||
|
||||
toBase64(buffer, buffer + sha1Length + 1, sha1Length + 1);
|
||||
|
||||
/* Last value must be replaced by equal sign. */
|
||||
const uint8_t responsePrefix[] = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ";
|
||||
|
||||
if (!tcpSend(socket, responsePrefix, sizeof(responsePrefix) - 1)
|
||||
|| !tcpSend(socket, buffer + sha1Length + 1, 27)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t responseSuffix[] = "=\r\n\r\n";
|
||||
return tcpSend(socket, responseSuffix, sizeof(responseSuffix) - 1);
|
||||
}
|
||||
|
||||
Debugger* DebuggerTcp::createDebugger(const char* options, Context* context)
|
||||
void DebuggerTcp::init(const char* options, Context* context)
|
||||
{
|
||||
uint16_t port = 6501;
|
||||
int timeout = -1;
|
||||
|
||||
String* skipSourceName = nullptr;
|
||||
EscargotSocket clientSocket = 0;
|
||||
|
||||
if (options) {
|
||||
auto v = split(options, ';');
|
||||
const char portOption[] = "--port=";
|
||||
|
|
@ -344,29 +325,31 @@ Debugger* DebuggerTcp::createDebugger(const char* options, Context* context)
|
|||
} else if (s.find(skipOption) == 0) {
|
||||
const char* skipStr = const_cast<const char*>(s.data() + sizeof(skipOption) - 1);
|
||||
size_t skipLen = strlen(skipStr);
|
||||
skipSourceName = String::fromASCII(skipStr, skipLen);
|
||||
m_skipSourceName = String::fromASCII(skipStr, skipLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(enabled() == false);
|
||||
|
||||
#ifdef WIN32
|
||||
WSADATA wsaData;
|
||||
int wsa_init_status = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (wsa_init_status != NO_ERROR) {
|
||||
return nullptr;
|
||||
return;
|
||||
}
|
||||
#endif /* WIN32*/
|
||||
|
||||
EscargotSocket serverSocket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (serverSocket == ESCARGOT_INVALID_SOCKET) {
|
||||
return nullptr;
|
||||
if (m_socket == ESCARGOT_INVALID_SOCKET) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tcpConfigureSocket(serverSocket, port)) {
|
||||
int error = tcpGetErrno();
|
||||
tcpCloseSocket(serverSocket);
|
||||
tcpLogError(error);
|
||||
return nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
ESCARGOT_LOG_INFO("Waiting for client connection 0.0.0.0:%hd\n", port);
|
||||
|
|
@ -389,72 +372,58 @@ Debugger* DebuggerTcp::createDebugger(const char* options, Context* context)
|
|||
if (timeout < 0) {
|
||||
ESCARGOT_LOG_ERROR("Waiting for client connection error: timeout reached\n");
|
||||
tcpCloseSocket(serverSocket);
|
||||
return nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sockaddr_in addr;
|
||||
socklen_t sinSize = sizeof(sockaddr_in);
|
||||
DebuggerHttpRouter httpRouter;
|
||||
|
||||
while (true) {
|
||||
clientSocket = accept(serverSocket, (sockaddr*)&addr, &sinSize);
|
||||
|
||||
if (clientSocket == ESCARGOT_INVALID_SOCKET) {
|
||||
tcpLogError(tcpGetErrno());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
u_long nonblockingEnabled = 1;
|
||||
|
||||
/* Set non-blocking mode. */
|
||||
if (ioctlsocket(clientSocket, FIONBIO, &nonblockingEnabled) != NO_ERROR) {
|
||||
tcpCloseSocket(clientSocket);
|
||||
return nullptr;
|
||||
}
|
||||
#else /* !WIN32 */
|
||||
int socketFlags = fcntl(clientSocket, F_GETFL, 0);
|
||||
|
||||
if (socketFlags < 0) {
|
||||
tcpCloseSocket(clientSocket);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Set non-blocking mode. */
|
||||
if (fcntl(clientSocket, F_SETFL, socketFlags | O_NONBLOCK) == -1) {
|
||||
tcpCloseSocket(clientSocket);
|
||||
return nullptr;
|
||||
}
|
||||
#endif /* WIN32 */
|
||||
|
||||
ESCARGOT_LOG_INFO("Connected from: %s\n", inet_ntoa(addr.sin_addr));
|
||||
|
||||
if (!httpRouter.handleHttpRequest(clientSocket)) {
|
||||
tcpCloseSocket(clientSocket);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (httpRouter.webSocketEstablished()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Close connection, waiting for the next request
|
||||
tcpCloseSocket(clientSocket);
|
||||
}
|
||||
m_socket = accept(serverSocket, (sockaddr*)&addr, &sinSize);
|
||||
|
||||
tcpCloseSocket(serverSocket);
|
||||
|
||||
Debugger* client;
|
||||
|
||||
if (httpRouter.client() == DebuggerClient::Escargot) {
|
||||
client = new DebuggerEscargot(clientSocket, skipSourceName);
|
||||
} else {
|
||||
client = new DebuggerDevtools(clientSocket, skipSourceName);
|
||||
if (m_socket == ESCARGOT_INVALID_SOCKET) {
|
||||
tcpLogError(tcpGetErrno());
|
||||
return;
|
||||
}
|
||||
|
||||
client->enable(context);
|
||||
return client;
|
||||
#ifdef WIN32
|
||||
u_long nonblockingEnabled = 1;
|
||||
|
||||
/* Set non-blocking mode. */
|
||||
if (ioctlsocket(m_socket, FIONBIO, &nonblockingEnabled) != NO_ERROR) {
|
||||
tcpCloseSocket(m_socket);
|
||||
return;
|
||||
}
|
||||
#else /* !WIN32 */
|
||||
int socketFlags = fcntl(m_socket, F_GETFL, 0);
|
||||
|
||||
if (socketFlags < 0) {
|
||||
tcpCloseSocket(m_socket);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set non-blocking mode. */
|
||||
if (fcntl(m_socket, F_SETFL, socketFlags | O_NONBLOCK) == -1) {
|
||||
tcpCloseSocket(m_socket);
|
||||
return;
|
||||
}
|
||||
#endif /* WIN32 */
|
||||
|
||||
ESCARGOT_LOG_INFO("Connected from: %s\n", inet_ntoa(addr.sin_addr));
|
||||
|
||||
if (!webSocketHandshake(m_socket)) {
|
||||
tcpCloseSocket(m_socket);
|
||||
return;
|
||||
}
|
||||
|
||||
m_receiveBufferFill = 0;
|
||||
m_messageLength = 0;
|
||||
|
||||
enable(context);
|
||||
|
||||
return DebuggerRemote::init(nullptr, context);
|
||||
}
|
||||
|
||||
bool DebuggerTcp::skipSourceCode(String* srcName) const
|
||||
|
|
@ -467,6 +436,34 @@ bool DebuggerTcp::skipSourceCode(String* srcName) const
|
|||
return srcName->contains(m_skipSourceName);
|
||||
}
|
||||
|
||||
#define ESCARGOT_DEBUGGER_WEBSOCKET_FIN_BIT 0x80
|
||||
#define ESCARGOT_DEBUGGER_WEBSOCKET_BINARY_FRAME 2
|
||||
#define ESCARGOT_DEBUGGER_WEBSOCKET_CLOSE_FRAME 8
|
||||
#define ESCARGOT_DEBUGGER_WEBSOCKET_OPCODE_MASK 0x0f
|
||||
#define ESCARGOT_DEBUGGER_WEBSOCKET_LENGTH_MASK 0x7f
|
||||
#define ESCARGOT_DEBUGGER_WEBSOCKET_ONE_BYTE_LEN_MAX 125
|
||||
#define ESCARGOT_DEBUGGER_WEBSOCKET_MASK_BIT 0x80
|
||||
|
||||
bool DebuggerTcp::send(uint8_t type, const void* buffer, size_t length)
|
||||
{
|
||||
ASSERT(enabled());
|
||||
ASSERT(length < ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH);
|
||||
|
||||
uint8_t message[ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH + 2];
|
||||
|
||||
message[0] = ESCARGOT_DEBUGGER_WEBSOCKET_FIN_BIT | ESCARGOT_DEBUGGER_WEBSOCKET_BINARY_FRAME;
|
||||
message[1] = (uint8_t)(length + 1);
|
||||
message[2] = type;
|
||||
memcpy(message + 3, buffer, length);
|
||||
|
||||
if (tcpSend(m_socket, message, length + 3)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
close(CloseAbortConnection);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DebuggerTcp::isThereAnyEvent()
|
||||
{
|
||||
// if there is remained receive buffer data,
|
||||
|
|
@ -487,6 +484,91 @@ bool DebuggerTcp::isThereAnyEvent()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DebuggerTcp::receive(uint8_t* buffer, size_t& length)
|
||||
{
|
||||
size_t receivedLength;
|
||||
|
||||
if (m_messageLength == 0 || m_receiveBufferFill < 2 + sizeof(uint32_t) + m_messageLength) {
|
||||
/* Cannot extract a whole message from the buffer. */
|
||||
if (!tcpReceive(m_socket,
|
||||
m_receiveBuffer + m_receiveBufferFill,
|
||||
ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH + 2 + sizeof(uint32_t) - m_receiveBufferFill,
|
||||
&receivedLength)) {
|
||||
close(CloseAbortConnection);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (receivedLength == 0 && m_receiveBufferFill < (2 + sizeof(uint32_t))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_receiveBufferFill = (uint8_t)(m_receiveBufferFill + receivedLength);
|
||||
}
|
||||
|
||||
if (m_messageLength == 0) {
|
||||
if (m_receiveBufferFill < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((m_receiveBuffer[0] & ESCARGOT_DEBUGGER_WEBSOCKET_OPCODE_MASK) != ESCARGOT_DEBUGGER_WEBSOCKET_BINARY_FRAME) {
|
||||
if ((m_receiveBuffer[0] & ESCARGOT_DEBUGGER_WEBSOCKET_OPCODE_MASK) == ESCARGOT_DEBUGGER_WEBSOCKET_CLOSE_FRAME) {
|
||||
close(CloseEndConnection);
|
||||
return false;
|
||||
}
|
||||
|
||||
ESCARGOT_LOG_ERROR("Unsupported Websocket opcode.\n");
|
||||
close(CloseProtocolUnsupported);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((m_receiveBuffer[0] & ~ESCARGOT_DEBUGGER_WEBSOCKET_OPCODE_MASK) != ESCARGOT_DEBUGGER_WEBSOCKET_FIN_BIT
|
||||
|| !(m_receiveBuffer[1] & ESCARGOT_DEBUGGER_WEBSOCKET_MASK_BIT)) {
|
||||
ESCARGOT_LOG_ERROR("Unsupported Websocket message.\n");
|
||||
close(CloseProtocolUnsupported);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_messageLength = (uint8_t)(m_receiveBuffer[1] & ESCARGOT_DEBUGGER_WEBSOCKET_LENGTH_MASK);
|
||||
|
||||
if (m_messageLength == 0 || m_messageLength > ESCARGOT_DEBUGGER_WEBSOCKET_ONE_BYTE_LEN_MAX) {
|
||||
ESCARGOT_LOG_ERROR("Unsupported Websocket message size.\n");
|
||||
close(CloseProtocolUnsupported);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t totalSize = 2 + sizeof(uint32_t) + m_messageLength;
|
||||
|
||||
if (m_receiveBufferFill < totalSize) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* mask = m_receiveBuffer + 2;
|
||||
uint8_t* mask_end = mask + sizeof(uint32_t);
|
||||
uint8_t* source = mask_end;
|
||||
uint8_t* buffer_end = buffer + m_messageLength;
|
||||
|
||||
while (buffer < buffer_end) {
|
||||
*buffer++ = *source++ ^ *mask++;
|
||||
|
||||
if (mask >= mask_end) {
|
||||
mask -= 4;
|
||||
}
|
||||
}
|
||||
|
||||
length = m_messageLength;
|
||||
m_messageLength = 0;
|
||||
|
||||
if (m_receiveBufferFill == totalSize) {
|
||||
m_receiveBufferFill = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
m_receiveBufferFill = (uint8_t)(m_receiveBufferFill - totalSize);
|
||||
memmove(m_receiveBuffer, m_receiveBuffer + totalSize, m_receiveBufferFill);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DebuggerTcp::close(CloseReason reason)
|
||||
{
|
||||
if (!enabled()) {
|
||||
|
|
|
|||
|
|
@ -31,75 +31,39 @@ typedef SOCKET EscargotSocket;
|
|||
#else /* !WIN32 */
|
||||
typedef int EscargotSocket;
|
||||
#endif /* WIN32 */
|
||||
#define ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH 125
|
||||
|
||||
#define ESCARGOT_DEBUGGER_WEBSOCKET_FIN_BIT 0x80
|
||||
#define ESCARGOT_DEBUGGER_WEBSOCKET_TEXT_FRAME 1
|
||||
#define ESCARGOT_DEBUGGER_WEBSOCKET_BINARY_FRAME 2
|
||||
#define ESCARGOT_DEBUGGER_WEBSOCKET_CLOSE_FRAME 8
|
||||
#define ESCARGOT_DEBUGGER_WEBSOCKET_OPCODE_MASK 0x0f
|
||||
#define ESCARGOT_DEBUGGER_WEBSOCKET_LENGTH_MASK 0x7f
|
||||
#define ESCARGOT_DEBUGGER_WEBSOCKET_ONE_BYTE_LEN_MAX 125
|
||||
#define ESCARGOT_DEBUGGER_WEBSOCKET_MASK_BIT 0x80
|
||||
|
||||
#define ESCARGOT_WS_HEADER_BASE_SIZE 2
|
||||
#define ESCARGOT_WS_EXT_LEN16_SIZE 2
|
||||
#define ESCARGOT_WS_MASK_SIZE 4
|
||||
|
||||
#define ESCARGOT_WS_MESSAGE_16BIT_LENGTH_MARKER 126
|
||||
#define ESCARGOT_WS_MAX_MESSAGE_LENGTH 65535
|
||||
|
||||
#define ESCARGOT_WS_HEADER_SIZE (ESCARGOT_WS_HEADER_BASE_SIZE + ESCARGOT_WS_MASK_SIZE)
|
||||
#define ESCARGOT_WS_HEADER_LEN16_SIZE (ESCARGOT_WS_HEADER_SIZE + ESCARGOT_WS_EXT_LEN16_SIZE)
|
||||
#define ESCARGOT_WS_BUFFER_SIZE (ESCARGOT_WS_HEADER_LEN16_SIZE + ESCARGOT_WS_MAX_MESSAGE_LENGTH)
|
||||
|
||||
class DebuggerTcp : public Debugger {
|
||||
class DebuggerTcp : public DebuggerRemote {
|
||||
public:
|
||||
DebuggerTcp(const EscargotSocket socket, String* skipSource, const uint32_t bufferSize, const uint8_t type)
|
||||
: m_socket(socket)
|
||||
, m_receiveBuffer(new uint8_t[bufferSize])
|
||||
, m_bufferSize(bufferSize)
|
||||
DebuggerTcp()
|
||||
: m_socket(0)
|
||||
, m_receiveBuffer{}
|
||||
, m_receiveBufferFill(0)
|
||||
, m_payloadLength(0)
|
||||
, m_headerLength(0)
|
||||
, m_websocketMessageType(type)
|
||||
, m_skipSourceName(skipSource)
|
||||
, m_messageLength(0)
|
||||
, m_skipSourceName(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
static Debugger* createDebugger(const char* options, Context* context);
|
||||
void init(const char* options, Context* context) override {}
|
||||
virtual void init(const char* options, Context* context) override;
|
||||
|
||||
bool skipSourceCode(String* srcName) const override;
|
||||
virtual bool skipSourceCode(String* srcName) const override;
|
||||
|
||||
static void computeSha1(const uint8_t* source1, size_t source1Length,
|
||||
const uint8_t* source2, size_t source2Length,
|
||||
uint8_t destination[20]);
|
||||
|
||||
static bool tcpReceive(EscargotSocket socket, uint8_t* message, size_t maxLength, size_t* receivedLength);
|
||||
static bool tcpSend(EscargotSocket socket, const uint8_t* message, size_t messageLength);
|
||||
|
||||
protected:
|
||||
enum CloseReason {
|
||||
CloseEndConnection,
|
||||
CloseAbortConnection,
|
||||
CloseProtocolUnsupported,
|
||||
CloseProtocolError,
|
||||
};
|
||||
virtual bool send(uint8_t type, const void* buffer, size_t length) override;
|
||||
virtual bool receive(uint8_t* buffer, size_t& length) override;
|
||||
virtual bool isThereAnyEvent() override;
|
||||
virtual void close(CloseReason reason) override;
|
||||
|
||||
bool send(uint8_t type, const void* buffer, size_t length);
|
||||
bool receive(uint8_t* buffer, size_t& length);
|
||||
bool isThereAnyEvent();
|
||||
void close(CloseReason reason);
|
||||
private:
|
||||
void receiveData();
|
||||
|
||||
EscargotSocket m_socket;
|
||||
uint8_t* m_receiveBuffer;
|
||||
uint32_t m_bufferSize;
|
||||
uint32_t m_receiveBufferFill;
|
||||
uint16_t m_payloadLength;
|
||||
uint8_t m_headerLength;
|
||||
uint8_t m_websocketMessageType;
|
||||
|
||||
uint8_t m_receiveBuffer[2 + sizeof(uint32_t) + ESCARGOT_DEBUGGER_MAX_MESSAGE_LENGTH];
|
||||
uint8_t m_receiveBufferFill;
|
||||
uint8_t m_messageLength;
|
||||
|
||||
// skip generating debugging bytecode for source code whose name contains m_skipSourceName
|
||||
String* m_skipSourceName;
|
||||
|
|
|
|||
|
|
@ -1,529 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2026-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
#include <cstdio>
|
||||
|
||||
#include "Escargot.h"
|
||||
#include "HeapSnapshot.h"
|
||||
|
||||
#include "Debugger.h"
|
||||
#include "runtime/Context.h"
|
||||
#include "runtime/Environment.h"
|
||||
#include "runtime/EnvironmentRecord.h"
|
||||
#include "runtime/GlobalObject.h"
|
||||
#include "parser/Script.h"
|
||||
#include "util/Util.h"
|
||||
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/filewritestream.h"
|
||||
#include "rapidjson/writer.h"
|
||||
#include "rapidjson/prettywriter.h"
|
||||
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
namespace Escargot {
|
||||
|
||||
uint64_t HeapSnapshot::addNode(Node::nodeType type, const std::string& name, uint64_t& id, uint64_t size, bool detachedness)
|
||||
{
|
||||
auto iter = m_strings.find(name);
|
||||
if (iter == m_strings.end()) {
|
||||
auto result = m_strings.insert(name);
|
||||
iter = result.first;
|
||||
}
|
||||
|
||||
m_nodes.push_back(Node(type, &(*iter), id, size, detachedness));
|
||||
id += 1;
|
||||
m_nodeCount++;
|
||||
|
||||
return id - 1;
|
||||
}
|
||||
|
||||
void HeapSnapshot::addEdge(Node::Edge::edgeType type, uint64_t from, uint64_t to, const std::string& name)
|
||||
{
|
||||
Node& node = m_nodes[from];
|
||||
node.m_edges.push_back(Node::Edge(type, &name, to * heapSnapshotNodeFieldCount));
|
||||
|
||||
m_edgeCount++;
|
||||
}
|
||||
|
||||
uint64_t HeapSnapshot::addNodeAndEdge(Node::nodeType nodeType, Node::Edge::edgeType edgeType, const std::string& name, uint64_t& id, uint64_t ownerId, uint64_t size, bool detachedness)
|
||||
{
|
||||
uint64_t node = addNode(nodeType, name, id, size, detachedness);
|
||||
addEdge(edgeType, ownerId, node, name);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void HeapSnapshot::addObjectProperties(ExecutionState* state, uint64_t& id)
|
||||
{
|
||||
Object* current = nullptr;
|
||||
uint64_t owner = 0;
|
||||
while (!m_objects.empty()) {
|
||||
current = m_objects.front().first;
|
||||
owner = m_objects.front().second;
|
||||
m_objects.pop();
|
||||
|
||||
Object::OwnPropertyKeyVector objectKeys = current->ownPropertyKeys(*state);
|
||||
|
||||
for (size_t i = 0; i < objectKeys.size(); i++) {
|
||||
ObjectPropertyName currentName(*state, objectKeys[i]);
|
||||
ObjectGetResult ownProperty = current->getOwnProperty(*state, currentName);
|
||||
|
||||
if (ownProperty.isDataAccessorProperty() && !ownProperty.isDataProperty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Value val = ownProperty.value(*state, val);
|
||||
|
||||
Value currPropertyName = currentName.toPlainValue();
|
||||
std::string name = "";
|
||||
if (currPropertyName.isString()) {
|
||||
name = currPropertyName.asString()->toNonGCUTF8StringData();
|
||||
} else if (currPropertyName.isSymbol()) {
|
||||
name = currPropertyName.asSymbol()->descriptionString()->toNonGCUTF8StringData();
|
||||
}
|
||||
|
||||
Node::Edge::edgeType edgeType = Node::Edge::property;
|
||||
if (m_nodes.at(owner).m_type == Node::array) {
|
||||
edgeType = Node::Edge::element;
|
||||
}
|
||||
|
||||
uint64_t propertyId = 0;
|
||||
if (val.isString()) {
|
||||
propertyId = addNode(Node::string, name, id, sizeof(String));
|
||||
addEdge(Node::Edge::property, owner, propertyId, val.toString(*state)->toNonGCUTF8StringData());
|
||||
} else if (val.isNumber() || val.isBoolean()) {
|
||||
propertyId = addNode(Node::number, name, id, sizeof(val.asNumber()));
|
||||
addEdge(Node::Edge::property, owner, propertyId, val.toString(*state)->toNonGCUTF8StringData());
|
||||
} else if (val.isSymbol()) {
|
||||
propertyId = addNode(Node::symbol, name, id, sizeof(*val.asSymbol()));
|
||||
std::string str = val.asSymbol()->symbolDescriptiveString()->toNonGCUTF8StringData();
|
||||
addEdge(Node::Edge::property, owner, propertyId, str);
|
||||
} else if (val.isUndefined()) {
|
||||
propertyId = addNode(Node::hidden, name, id, sizeof(Value));
|
||||
addEdge(Node::Edge::property, owner, propertyId, val.toString(*state)->toNonGCUTF8StringData());
|
||||
} else if (val.isObject()) {
|
||||
if (m_seenObjects.find(val.asObject()) != m_seenObjects.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (val.isFunction()) {
|
||||
propertyId = addNode(Node::closure, name, id, sizeof(*val.asFunction()));
|
||||
addEdge(Node::Edge::property, owner, propertyId, "Function");
|
||||
} else if (val.isBigInt()) {
|
||||
propertyId = addNode(Node::bigint, name, id, sizeof(*val.asBigInt()));
|
||||
addEdge(Node::Edge::property, owner, propertyId, "BigInt");
|
||||
} else if (val.asObject()->isArray(*state)) {
|
||||
propertyId = addNode(Node::array, name, id, sizeof(*val.asObject()));
|
||||
addEdge(Node::Edge::property, owner, propertyId, "Array");
|
||||
} else {
|
||||
propertyId = addNode(Node::object, name, id, sizeof(*val.asObject()));
|
||||
addEdge(Node::Edge::property, owner, propertyId, "Object");
|
||||
}
|
||||
|
||||
m_seenObjects.insert(val.asObject());
|
||||
m_objects.push(std::pair<Object*, uint64_t>(val.asObject(), propertyId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HeapSnapshot::addGlobalEnvrionmentRecord(ExecutionState* state, GlobalEnvironmentRecord* env, uint64_t& id, uint64_t LexicalEnvIdx)
|
||||
{
|
||||
GlobalObject* globalObj = env->globalObject();
|
||||
uint64_t gObj = addNodeAndEdge(Node::hidden, Node::Edge::property, "Global Object", id, LexicalEnvIdx, sizeof(GlobalObject));
|
||||
uint64_t varDeclarations = addNodeAndEdge(Node::synthetic, Node::Edge::property, "Var Declarations", id, gObj, 0);
|
||||
|
||||
InterpretedCodeBlock* gCodeBlock = env->globalCodeBlock();
|
||||
for (size_t i = 0; i < gCodeBlock->identifierInfos().size(); i++) {
|
||||
if (!gCodeBlock->identifierInfos()[i].m_isVarDeclaration) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t propertyId = 0;
|
||||
std::string name = gCodeBlock->identifierInfos()[i].m_name.string()->toNonGCUTF8StringData();
|
||||
auto result = env->getBindingValue(*state, gCodeBlock->identifierInfos()[i].m_name);
|
||||
if (result.m_hasBindingValue) {
|
||||
Value val = result.m_value;
|
||||
|
||||
if (val.isString()) {
|
||||
propertyId = addNode(Node::string, name, id, sizeof(String));
|
||||
addEdge(Node::Edge::property, varDeclarations, propertyId, val.toString(*state)->toNonGCUTF8StringData());
|
||||
} else if (val.isNumber() || val.isBoolean()) {
|
||||
propertyId = addNode(Node::number, name, id, sizeof(val.asNumber()));
|
||||
addEdge(Node::Edge::property, varDeclarations, propertyId, val.toString(*state)->toNonGCUTF8StringData());
|
||||
} else if (val.isSymbol()) {
|
||||
propertyId = addNode(Node::symbol, name, id, sizeof(*val.asSymbol()));
|
||||
std::string str = val.asSymbol()->symbolDescriptiveString()->toNonGCUTF8StringData();
|
||||
addEdge(Node::Edge::property, varDeclarations, propertyId, str);
|
||||
} else if (val.isUndefined()) {
|
||||
propertyId = addNode(Node::hidden, name, id, sizeof(Value));
|
||||
addEdge(Node::Edge::property, varDeclarations, propertyId, val.toString(*state)->toNonGCUTF8StringData());
|
||||
} else if (val.isObject()) {
|
||||
propertyId = addNode(Node::object, name, id, sizeof(Value));
|
||||
|
||||
m_seenObjects.insert(val.asObject());
|
||||
m_objects.push(std::pair<Object*, uint64_t>(val.asObject(), propertyId));
|
||||
addObjectProperties(state, id);
|
||||
}
|
||||
} else {
|
||||
propertyId = addNode(Node::hidden, name, id, sizeof(0));
|
||||
}
|
||||
|
||||
addEdge(Node::Edge::property, varDeclarations, propertyId, "Var Declaration");
|
||||
}
|
||||
|
||||
m_objects.push(std::pair<Object*, uint64_t>(env->globalObject(), gObj));
|
||||
addObjectProperties(state, id);
|
||||
}
|
||||
|
||||
void HeapSnapshot::addObjectEnvironmentRecord(ExecutionState* state, ObjectEnvironmentRecord* env, uint64_t& id, uint64_t& LexicalEnvIdx)
|
||||
{
|
||||
Object* bindingObj = env->bindingObject();
|
||||
uint64_t objId = addNodeAndEdge(Node::object, Node::Edge::element, "Object", id, LexicalEnvIdx, sizeof(Object));
|
||||
m_objects.push(std::pair<Object*, uint64_t>(bindingObj, objId));
|
||||
addObjectProperties(state, id);
|
||||
}
|
||||
|
||||
void HeapSnapshot::addFunctionEnvironmentRecord(ExecutionState* state, FunctionEnvironmentRecord* env, uint64_t& id, uint64_t& LexicalEnvIdx)
|
||||
{
|
||||
ScriptFunctionObject* obj = env->functionObject();
|
||||
uint64_t funcObj = addNodeAndEdge(Node::object, Node::Edge::element, "Function Object", id, LexicalEnvIdx, sizeof(ScriptFunctionObject));
|
||||
m_objects.push(std::pair<Object*, uint64_t>(obj, funcObj));
|
||||
addObjectProperties(state, id);
|
||||
}
|
||||
|
||||
void HeapSnapshot::addModuleEnvironmentRecord(ExecutionState* state, ModuleEnvironmentRecord* env, uint64_t& id, uint64_t& owner)
|
||||
{
|
||||
std::string scriptName = env->script()->srcName()->toNonGCUTF8StringData();
|
||||
uint64_t module = addNodeAndEdge(Node::code, Node::Edge::property, scriptName, id, owner, sizeof(ScriptFunctionObject));
|
||||
|
||||
for (uint64_t i = 0; i < env->moduleBindings().size(); i++) {
|
||||
std::string name = env->moduleBindings()[i].m_localName.string()->toNonGCUTF8StringData();
|
||||
Value val = env->moduleBindings()[i].m_value.toValue();
|
||||
if (val.isEmpty()) {
|
||||
uint64_t node = addNode(Node::object, name, id, sizeof(String));
|
||||
addEdge(Node::Edge::property, module, node, "Empty");
|
||||
} else if (val.isString()) {
|
||||
uint64_t node = addNode(Node::string, name, id, sizeof(String));
|
||||
addEdge(Node::Edge::property, module, node, val.toString(*state)->toNonGCUTF8StringData());
|
||||
} else if (val.isNumber() || val.isBoolean()) {
|
||||
uint64_t node = addNode(Node::number, name, id, sizeof(val.asNumber()));
|
||||
addEdge(Node::Edge::property, module, node, val.toString(*state)->toNonGCUTF8StringData());
|
||||
} else if (val.isSymbol()) {
|
||||
uint64_t node = addNode(Node::symbol, name, id, sizeof(val.asSymbol()));
|
||||
addEdge(Node::Edge::property, module, node, val.toString(*state)->toNonGCUTF8StringData());
|
||||
} else if (val.isUndefined()) {
|
||||
uint64_t node = addNode(Node::hidden, name, id, sizeof(Value));
|
||||
addEdge(Node::Edge::property, module, node, val.toString(*state)->toNonGCUTF8StringData());
|
||||
} else if (val.isObject()) {
|
||||
uint64_t node = addNode(Node::object, name, id, sizeof(Object));
|
||||
addEdge(Node::Edge::property, module, node, "Object");
|
||||
|
||||
if (m_seenObjects.find(val.asObject()) != m_seenObjects.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_seenObjects.insert(val.asObject());
|
||||
m_objects.push(std::pair<Object*, uint64_t>(val.asObject(), node));
|
||||
|
||||
addObjectProperties(state, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HeapSnapshot::addIndexedDeclarativeEnvironmentRecord(ExecutionState* state, DeclarativeEnvironmentRecordIndexed* env, uint64_t& id, uint64_t& LexicalEnvIdx)
|
||||
{
|
||||
uint64_t declEnv = addNode(Node::object, "declarative environment record", id, sizeof(*env), false);
|
||||
addEdge(Node::Edge::element, declEnv, LexicalEnvIdx, "");
|
||||
}
|
||||
|
||||
void HeapSnapshot::addNotIndexedDeclarativeEnvironmentRecord(ExecutionState* state, DeclarativeEnvironmentRecordNotIndexed* env, uint64_t& id, uint64_t& LexicalEnvIdx)
|
||||
{
|
||||
uint64_t declEnv = addNode(Node::object, "not indexed declarative environment record", id, sizeof(*env), false);
|
||||
addEdge(Node::Edge::element, LexicalEnvIdx, declEnv, "");
|
||||
|
||||
IdentifierRecordVector recordVector = env->getIdentifierRecordVector();
|
||||
for (uint64_t i = 0; i < recordVector.size(); i++) {
|
||||
uint64_t identifier = addNode(Node::object, "identifier record vector element", id, sizeof(recordVector[i]), false);
|
||||
addEdge(Node::Edge::element, declEnv, identifier, "");
|
||||
|
||||
uint64_t name = addNode(Node::string, recordVector[i].m_name.string()->toUTF8StringData().data(), id, sizeof(recordVector[i].m_name), false);
|
||||
addEdge(Node::Edge::element, identifier, name, "");
|
||||
}
|
||||
}
|
||||
|
||||
std::string HeapSnapshot::prepareHeapSnapshotFile()
|
||||
{
|
||||
auto addFields = [](rapidjson::Value& array, Vector<std::string, GCUtil::gc_malloc_allocator<std::string>>& strings, rapidjson::Document::AllocatorType& allocator) {
|
||||
for (const std::string& elem : strings) {
|
||||
rapidjson::Value s;
|
||||
s.SetString(elem.c_str(), elem.length(), allocator);
|
||||
array.PushBack(s, allocator);
|
||||
}
|
||||
};
|
||||
|
||||
rapidjson::Document jsonDoc;
|
||||
rapidjson::Document::AllocatorType& allocator = jsonDoc.GetAllocator();
|
||||
jsonDoc.SetObject();
|
||||
|
||||
rapidjson::Value meta(rapidjson::kObjectType);
|
||||
|
||||
rapidjson::Value node_fields(rapidjson::kArrayType);
|
||||
{
|
||||
Vector<std::string, GCUtil::gc_malloc_allocator<std::string>> strings{
|
||||
"type",
|
||||
"name",
|
||||
"id",
|
||||
"self_size",
|
||||
"edge_count",
|
||||
"trace_node_id",
|
||||
"detachedness",
|
||||
"script_id",
|
||||
"line",
|
||||
"column"
|
||||
};
|
||||
addFields(node_fields, strings, allocator);
|
||||
meta.AddMember("node_fields", node_fields, allocator);
|
||||
}
|
||||
|
||||
rapidjson::Value types(rapidjson::kArrayType);
|
||||
{
|
||||
Vector<std::string, GCUtil::gc_malloc_allocator<std::string>> strings{
|
||||
"hidden", "array", "string", "object", "code", "closure", "regexp",
|
||||
"number", "native", "synthetic", "concatenated string",
|
||||
"sliced string", "symbol", "bigint", "object shape"
|
||||
};
|
||||
addFields(types, strings, allocator);
|
||||
}
|
||||
|
||||
rapidjson::Value node_types(rapidjson::kArrayType);
|
||||
{
|
||||
node_types.PushBack(types, allocator);
|
||||
Vector<std::string, GCUtil::gc_malloc_allocator<std::string>> strings{
|
||||
"string", "number", "number", "number", "number"
|
||||
};
|
||||
addFields(node_types, strings, allocator);
|
||||
meta.AddMember("node_types", node_types, allocator);
|
||||
}
|
||||
|
||||
rapidjson::Value edge_fields(rapidjson::kArrayType);
|
||||
{
|
||||
Vector<std::string, GCUtil::gc_malloc_allocator<std::string>> strings{ "type", "name_or_index", "to_node" };
|
||||
addFields(edge_fields, strings, allocator);
|
||||
meta.AddMember("edge_fields", edge_fields, allocator);
|
||||
}
|
||||
|
||||
rapidjson::Value edge_types_inner(rapidjson::kArrayType);
|
||||
{
|
||||
Vector<std::string, GCUtil::gc_malloc_allocator<std::string>> strings{ "type", "name_or_index", "to_node" };
|
||||
addFields(edge_types_inner, strings, allocator);
|
||||
}
|
||||
|
||||
rapidjson::Value edge_types(rapidjson::kArrayType);
|
||||
{
|
||||
edge_types.PushBack(edge_types_inner, allocator);
|
||||
Vector<std::string, GCUtil::gc_malloc_allocator<std::string>> strings{ "string_or_number", "node" };
|
||||
addFields(edge_types, strings, allocator);
|
||||
meta.AddMember("edge_types", edge_types, allocator);
|
||||
}
|
||||
|
||||
rapidjson::Value trace_function_info_fields(rapidjson::kArrayType);
|
||||
{
|
||||
Vector<std::string, GCUtil::gc_malloc_allocator<std::string>> strings{ "function_id", "name", "script_name", "script_id", "line", "column" };
|
||||
addFields(trace_function_info_fields, strings, allocator);
|
||||
meta.AddMember("trace_function_info_fields", trace_function_info_fields, allocator);
|
||||
}
|
||||
|
||||
rapidjson::Value trace_node_fields(rapidjson::kArrayType);
|
||||
{
|
||||
Vector<std::string, GCUtil::gc_malloc_allocator<std::string>> strings{ "id", "function_info_index", "count", "size", "children" };
|
||||
addFields(trace_node_fields, strings, allocator);
|
||||
meta.AddMember("trace_node_fields", trace_node_fields, allocator);
|
||||
}
|
||||
|
||||
rapidjson::Value sample_fields(rapidjson::kArrayType);
|
||||
{
|
||||
Vector<std::string, GCUtil::gc_malloc_allocator<std::string>> strings{ "timestamp_us", "last_assigned_id" };
|
||||
addFields(sample_fields, strings, allocator);
|
||||
meta.AddMember("sample_fields", sample_fields, allocator);
|
||||
}
|
||||
|
||||
rapidjson::Value location_fields(rapidjson::kArrayType);
|
||||
{
|
||||
Vector<std::string, GCUtil::gc_malloc_allocator<std::string>> strings{ "object_index", "script_id", "line", "column" };
|
||||
addFields(location_fields, strings, allocator);
|
||||
meta.AddMember("location_fields", location_fields, allocator);
|
||||
}
|
||||
|
||||
rapidjson::Value snapshot(rapidjson::kObjectType);
|
||||
{
|
||||
snapshot.AddMember("meta", meta, allocator);
|
||||
snapshot.AddMember("node_count", rapidjson::Value(m_nodeCount * heapSnapshotNodeFieldCount), allocator);
|
||||
snapshot.AddMember("edge_count", rapidjson::Value(m_edgeCount * heapSnapshotEdgeFieldCount), allocator);
|
||||
snapshot.AddMember("extra_native_bytes", 0, allocator);
|
||||
snapshot.AddMember("trace_function_count", 0, allocator);
|
||||
}
|
||||
|
||||
rapidjson::Value nodes(rapidjson::kArrayType);
|
||||
rapidjson::Value edges(rapidjson::kArrayType);
|
||||
for (Node& node : m_nodes) {
|
||||
nodes.PushBack(node.m_type, allocator);
|
||||
auto iter = m_strings.find(*node.m_name);
|
||||
auto index = std::distance(m_strings.begin(), iter);
|
||||
nodes.PushBack(index, allocator);
|
||||
nodes.PushBack(node.m_id, allocator);
|
||||
nodes.PushBack(node.m_self_size, allocator);
|
||||
nodes.PushBack(node.m_edges.size(), allocator);
|
||||
nodes.PushBack(0, allocator);
|
||||
nodes.PushBack((uint32_t)node.m_detachedness, allocator);
|
||||
nodes.PushBack(0, allocator);
|
||||
nodes.PushBack(0, allocator);
|
||||
nodes.PushBack(0, allocator);
|
||||
|
||||
for (Node::Edge& edge : node.m_edges) {
|
||||
edges.PushBack(edge.m_type, allocator);
|
||||
edges.PushBack(index, allocator);
|
||||
edges.PushBack(edge.m_to_node_idx, allocator);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
rapidjson::Value trace_function_infos(rapidjson::kArrayType);
|
||||
rapidjson::Value trace_tree(rapidjson::kArrayType);
|
||||
rapidjson::Value samples(rapidjson::kArrayType);
|
||||
rapidjson::Value locations(rapidjson::kArrayType);
|
||||
|
||||
rapidjson::Value strings(rapidjson::kArrayType);
|
||||
for (const std::string& str : m_strings) {
|
||||
rapidjson::Value s;
|
||||
s = rapidjson::StringRef(str.c_str());
|
||||
strings.PushBack(s, allocator);
|
||||
}
|
||||
|
||||
|
||||
jsonDoc.AddMember("snapshot", snapshot, allocator);
|
||||
jsonDoc.AddMember("nodes", nodes, allocator);
|
||||
jsonDoc.AddMember("edges", edges, allocator);
|
||||
jsonDoc.AddMember("trace_function_infos", trace_function_infos, allocator);
|
||||
jsonDoc.AddMember("trace_tree", trace_tree, allocator);
|
||||
jsonDoc.AddMember("samples", samples, allocator);
|
||||
jsonDoc.AddMember("locations", locations, allocator);
|
||||
jsonDoc.AddMember("strings", strings, allocator);
|
||||
|
||||
std::string outputName = "output-" + std::to_string(timestamp()) + ".heapsnapshot";
|
||||
auto deleter = [&](std::FILE* fp) { fclose(fp); };
|
||||
std::unique_ptr<std::FILE, decltype(deleter)> output(fopen(outputName.c_str(), "w"), deleter);
|
||||
|
||||
if (output.get() == nullptr) {
|
||||
ESCARGOT_LOG_ERROR("Could not create heap snapshot file.\n");
|
||||
return "";
|
||||
}
|
||||
|
||||
char buffer[65536];
|
||||
rapidjson::FileWriteStream os(output.get(), buffer, sizeof(buffer));
|
||||
rapidjson::Writer<rapidjson::FileWriteStream> writer(os);
|
||||
jsonDoc.Accept(writer);
|
||||
|
||||
return outputName;
|
||||
}
|
||||
|
||||
std::string HeapSnapshot::takeHeapSnapshot(ExecutionState* state)
|
||||
{
|
||||
uint64_t id = 0;
|
||||
uint64_t stateIdx = 0;
|
||||
|
||||
uint64_t root = addNode(HeapSnapshot::Node::hidden, "Root", id, sizeof(ExecutionState));
|
||||
uint64_t context = addNodeAndEdge(Node::hidden, Node::Edge::property, "Context", id, root, sizeof(Context));
|
||||
uint64_t staticStrings = addNodeAndEdge(Node::hidden, Node::Edge::property, "Static Strings", id, context, sizeof(StaticStrings));
|
||||
|
||||
for (auto& str : *state->context()->atomicStringMap()) {
|
||||
addNodeAndEdge(Node::string, Node::Edge::property, str->toNonGCUTF8StringData(), id, staticStrings, sizeof(String));
|
||||
}
|
||||
|
||||
uint64_t userDefined = addNodeAndEdge(Node::synthetic, Node::Edge::property, "User Defined variables", id, root, 0);
|
||||
for (size_t i = 0; i < state->context()->globalDeclarativeRecord()->size(); i++) {
|
||||
std::string name = state->context()->globalDeclarativeRecord()->at(i).m_name.string()->toNonGCUTF8StringData();
|
||||
|
||||
if (state->context()->globalDeclarativeStorage()->at(i).isEmpty()) {
|
||||
addNodeAndEdge(Node::hidden, Node::Edge::property, name, id, userDefined, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
Value val = state->context()->globalDeclarativeStorage()->at(i).toValue();
|
||||
if (val.isString()) {
|
||||
uint64_t node = addNode(Node::string, name, id, sizeof(String));
|
||||
addEdge(Node::Edge::property, userDefined, node, val.toString(*state)->toNonGCUTF8StringData());
|
||||
} else if (val.isNumber() || val.isBoolean()) {
|
||||
uint64_t node = addNode(Node::number, name, id, sizeof(val.asNumber()));
|
||||
addEdge(Node::Edge::property, userDefined, node, val.toString(*state)->toNonGCUTF8StringData());
|
||||
} else if (val.isSymbol()) {
|
||||
uint64_t node = addNode(Node::symbol, name, id, sizeof(val.asSymbol()));
|
||||
addEdge(Node::Edge::property, userDefined, node, val.toString(*state)->toNonGCUTF8StringData());
|
||||
} else if (val.isUndefined()) {
|
||||
uint64_t node = addNode(Node::hidden, name, id, sizeof(Value));
|
||||
addEdge(Node::Edge::property, userDefined, node, val.toString(*state)->toNonGCUTF8StringData());
|
||||
} else if (val.isObject()) {
|
||||
uint64_t node = addNode(Node::object, name, id, sizeof(Object));
|
||||
addEdge(Node::Edge::property, userDefined, node, "Object");
|
||||
|
||||
if (m_seenObjects.find(val.asObject()) != m_seenObjects.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_seenObjects.insert(val.asObject());
|
||||
m_objects.push(std::pair<Object*, uint64_t>(val.asObject(), node));
|
||||
|
||||
addObjectProperties(state, id);
|
||||
}
|
||||
}
|
||||
|
||||
LexicalEnvironment* lexEnv = state->lexicalEnvironment();
|
||||
while (lexEnv) {
|
||||
uint64_t lexEnvId = addNodeAndEdge(Node::hidden, Node::Edge::property, "LexicalEnvironment", id, root, sizeof(LexicalEnvironment));
|
||||
EnvironmentRecord* envRecord = lexEnv->record();
|
||||
|
||||
if (envRecord->isGlobalEnvironmentRecord()) {
|
||||
addGlobalEnvrionmentRecord(state, envRecord->asGlobalEnvironmentRecord(), id, lexEnvId);
|
||||
} else if (envRecord->isObjectEnvironmentRecord()) {
|
||||
addObjectEnvironmentRecord(state, envRecord->asObjectEnvironmentRecord(), id, lexEnvId);
|
||||
} else if (envRecord->isDeclarativeEnvironmentRecord()) {
|
||||
DeclarativeEnvironmentRecord* declarativeRecord = envRecord->asDeclarativeEnvironmentRecord();
|
||||
if (declarativeRecord->isFunctionEnvironmentRecord()) {
|
||||
FunctionEnvironmentRecord* funcEnv = envRecord->asDeclarativeEnvironmentRecord()->asFunctionEnvironmentRecord();
|
||||
addFunctionEnvironmentRecord(state, funcEnv, id, lexEnvId);
|
||||
} else if (envRecord->isModuleEnvironmentRecord()) {
|
||||
ModuleEnvironmentRecord* moduleEnv = envRecord->asDeclarativeEnvironmentRecord()->asModuleEnvironmentRecord();
|
||||
uint64_t modules = addNodeAndEdge(Node::synthetic, Node::Edge::property, "Modules", id, root, 0);
|
||||
addModuleEnvironmentRecord(state, moduleEnv, id, modules);
|
||||
} else if (envRecord->asDeclarativeEnvironmentRecord()->isDeclarativeEnvironmentRecordIndexed()) {
|
||||
DeclarativeEnvironmentRecordIndexed* declEnv = envRecord->asDeclarativeEnvironmentRecord()->asDeclarativeEnvironmentRecordIndexed();
|
||||
addIndexedDeclarativeEnvironmentRecord(state, declEnv, id, lexEnvId);
|
||||
} else {
|
||||
DeclarativeEnvironmentRecordNotIndexed* declEnv = envRecord->asDeclarativeEnvironmentRecord()->asDeclarativeEnvironmentRecordNotIndexed();
|
||||
addNotIndexedDeclarativeEnvironmentRecord(state, declEnv, id, lexEnvId);
|
||||
}
|
||||
}
|
||||
|
||||
lexEnv = lexEnv->outerEnvironment();
|
||||
}
|
||||
|
||||
ESCARGOT_LOG_INFO("nodes: %ld | edges: %ld | strings: %zu\n", m_nodeCount, m_edgeCount, m_strings.size());
|
||||
return prepareHeapSnapshotFile();
|
||||
}
|
||||
} // namespace Escargot
|
||||
#endif
|
||||
|
|
@ -1,148 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2026-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
#ifndef __EscargotHeapSnapshot__
|
||||
#define __EscargotHeapSnapshot__
|
||||
|
||||
#include "runtime/Object.h"
|
||||
#include "util/Vector.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
class Object;
|
||||
class EnvironmentRecord;
|
||||
class ObjectEnvironmentRecord;
|
||||
class GlobalEnvironmentRecord;
|
||||
class ModuleEnvironmentRecord;
|
||||
class FunctionEnvironmentRecord;
|
||||
class DeclarativeEnvironmentRecord;
|
||||
class DeclarativeEnvironmentRecordIndexed;
|
||||
class DeclarativeEnvironmentRecordNotIndexed;
|
||||
|
||||
class HeapSnapshot : public gc {
|
||||
public:
|
||||
const uint32_t heapSnapshotNodeFieldCount = 10;
|
||||
const uint32_t heapSnapshotEdgeFieldCount = 3;
|
||||
|
||||
struct Node {
|
||||
// /"edge_types":[["context","element","property","internal","hidden","shortcut","weak"]
|
||||
struct Edge {
|
||||
enum edgeType {
|
||||
context,
|
||||
element,
|
||||
property,
|
||||
internal,
|
||||
hidden,
|
||||
shortcut,
|
||||
weak,
|
||||
};
|
||||
|
||||
Edge(edgeType type, const std::string* name, uint64_t to_node_idx)
|
||||
: m_type(type)
|
||||
, m_name(const_cast<std::string*>(name))
|
||||
, m_to_node_idx(to_node_idx)
|
||||
{
|
||||
}
|
||||
|
||||
edgeType m_type;
|
||||
std::string* m_name;
|
||||
uint64_t m_to_node_idx;
|
||||
};
|
||||
|
||||
enum nodeType {
|
||||
hidden,
|
||||
array,
|
||||
string,
|
||||
object,
|
||||
code,
|
||||
closure,
|
||||
regexp,
|
||||
number,
|
||||
native,
|
||||
synthetic,
|
||||
concatenated_string,
|
||||
sliced_string,
|
||||
symbol,
|
||||
bigint,
|
||||
object_shape
|
||||
};
|
||||
|
||||
Node(nodeType type, const std::string* name, uint64_t id, uint64_t self_size, bool detachedness)
|
||||
: m_type(type)
|
||||
, m_name(const_cast<std::string*>(name))
|
||||
, m_id(id)
|
||||
, m_self_size(self_size)
|
||||
, m_detachedness(detachedness)
|
||||
{
|
||||
}
|
||||
|
||||
~Node()
|
||||
{
|
||||
m_edges.clear();
|
||||
}
|
||||
|
||||
nodeType m_type;
|
||||
const std::string* m_name;
|
||||
uint64_t m_id;
|
||||
uint64_t m_self_size;
|
||||
bool m_detachedness;
|
||||
Vector<Edge, GCUtil::gc_malloc_allocator<Edge>> m_edges;
|
||||
};
|
||||
|
||||
|
||||
HeapSnapshot()
|
||||
{
|
||||
m_nodeCount = 0;
|
||||
m_edgeCount = 0;
|
||||
}
|
||||
|
||||
~HeapSnapshot()
|
||||
{
|
||||
m_nodes.clear();
|
||||
m_strings.clear();
|
||||
}
|
||||
|
||||
uint64_t addNode(Node::nodeType type, const std::string& name, uint64_t& id, uint64_t size, bool detachedness = false);
|
||||
void addEdge(Node::Edge::edgeType type, uint64_t from, uint64_t to, const std::string& name);
|
||||
uint64_t addNodeAndEdge(Node::nodeType nodeType, Node::Edge::edgeType edgeType, const std::string& name, uint64_t& id, uint64_t ownerId, uint64_t size, bool detachedness = false);
|
||||
void addObjectProperties(ExecutionState* state, uint64_t& id);
|
||||
|
||||
void addGlobalEnvrionmentRecord(ExecutionState* state, GlobalEnvironmentRecord* env, uint64_t& id, uint64_t LexicalEnvIdx);
|
||||
void addObjectEnvironmentRecord(ExecutionState* state, ObjectEnvironmentRecord* env, uint64_t& id, uint64_t& LexicalEnvIdx);
|
||||
void addFunctionEnvironmentRecord(ExecutionState* state, FunctionEnvironmentRecord* env, uint64_t& id, uint64_t& LexicalEnvIdx);
|
||||
void addModuleEnvironmentRecord(ExecutionState* state, ModuleEnvironmentRecord* env, uint64_t& id, uint64_t& LexicalEnvIdx);
|
||||
void addIndexedDeclarativeEnvironmentRecord(ExecutionState* state, DeclarativeEnvironmentRecordIndexed* env, uint64_t& id, uint64_t& LexicalEnvIdx);
|
||||
void addNotIndexedDeclarativeEnvironmentRecord(ExecutionState* state, DeclarativeEnvironmentRecordNotIndexed* env, uint64_t& id, uint64_t& LexicalEnvIdx);
|
||||
|
||||
std::string prepareHeapSnapshotFile();
|
||||
std::string takeHeapSnapshot(ExecutionState* state);
|
||||
|
||||
private:
|
||||
uint64_t m_nodeCount;
|
||||
uint64_t m_edgeCount;
|
||||
|
||||
Vector<Node, GCUtil::gc_malloc_allocator<Node>> m_nodes;
|
||||
std::unordered_set<std::string> m_strings;
|
||||
std::unordered_set<Object*> m_seenObjects;
|
||||
std::queue<std::pair<Object*, uint64_t>> m_objects;
|
||||
};
|
||||
|
||||
} // namespace Escargot
|
||||
|
||||
#endif
|
||||
|
|
@ -199,6 +199,18 @@ int getValidValueInInterpretedCodeBlockWithRareData(void* ptr, GC_mark_custom_re
|
|||
return 0;
|
||||
}
|
||||
|
||||
int getValidValueInWeakMapObjectDataItemObject(void* ptr, GC_mark_custom_result* arr)
|
||||
{
|
||||
WeakMapObject::WeakMapObjectDataItem* current = (WeakMapObject::WeakMapObjectDataItem*)ptr;
|
||||
arr[0].from = (GC_word*)¤t->data;
|
||||
if (current->data.isStoredInHeap()) {
|
||||
arr[0].to = (GC_word*)current->data.payload();
|
||||
} else {
|
||||
arr[0].to = nullptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getValidValueInWeakRefObject(void* ptr, GC_mark_custom_result* arr)
|
||||
{
|
||||
WeakRefObject* current = (WeakRefObject*)ptr;
|
||||
|
|
@ -224,18 +236,6 @@ int getValidValueInFinalizationRegistryObjectItem(void* ptr, GC_mark_custom_resu
|
|||
arr[1].to = (GC_word*)current->source;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getValidValueInWeakMapObjectDataItem(void* ptr, GC_mark_custom_result* arr)
|
||||
{
|
||||
WeakMapObject::WeakMapObjectDataItem* current = (WeakMapObject::WeakMapObjectDataItem*)ptr;
|
||||
arr[0].from = (GC_word*)¤t->data;
|
||||
if (current->data.isStoredInHeap()) {
|
||||
arr[0].to = (GC_word*)current->data.payload();
|
||||
} else {
|
||||
arr[0].to = nullptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
void initializeCustomAllocators()
|
||||
|
|
@ -299,6 +299,11 @@ void initializeCustomAllocators()
|
|||
FALSE,
|
||||
TRUE);
|
||||
|
||||
s_gcKinds[HeapObjectKind::WeakMapObjectDataItemKind] = GC_new_kind(GC_new_free_list(),
|
||||
GC_MAKE_PROC(GC_new_proc(markAndPushCustom<getValidValueInWeakMapObjectDataItemObject, 1>), 0),
|
||||
FALSE,
|
||||
TRUE);
|
||||
|
||||
s_gcKinds[HeapObjectKind::WeakRefObjectKind] = GC_new_kind(GC_new_free_list(),
|
||||
GC_MAKE_PROC(GC_new_proc(markAndPushCustom<getValidValueInWeakRefObject, 3>), 0),
|
||||
FALSE,
|
||||
|
|
@ -308,11 +313,6 @@ void initializeCustomAllocators()
|
|||
GC_MAKE_PROC(GC_new_proc(markAndPushCustom<getValidValueInFinalizationRegistryObjectItem, 2>), 0),
|
||||
FALSE,
|
||||
TRUE);
|
||||
|
||||
s_gcKinds[HeapObjectKind::WeakMapObjectDataItemKind] = GC_new_kind(GC_new_free_list(),
|
||||
GC_MAKE_PROC(GC_new_proc(markAndPushCustom<getValidValueInWeakMapObjectDataItem, 1>), 0),
|
||||
FALSE,
|
||||
TRUE);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -336,11 +336,7 @@ void iterateSpecificKindOfObject(ExecutionState& state, HeapObjectKind kind, Hea
|
|||
|
||||
HeapObjectIteratorData* data = (HeapObjectIteratorData*)cd;
|
||||
if (kind == data->kind) {
|
||||
#if defined(NDEBUG)
|
||||
data->callback(data->state, obj);
|
||||
#else
|
||||
data->callback(data->state, GC_USR_PTR_FROM_BASE(obj));
|
||||
#endif
|
||||
}
|
||||
},
|
||||
(void*)(&data));
|
||||
|
|
@ -455,6 +451,16 @@ InterpretedCodeBlockWithRareData* CustomAllocator<InterpretedCodeBlockWithRareDa
|
|||
return (InterpretedCodeBlockWithRareData*)GC_GENERIC_MALLOC(sizeof(InterpretedCodeBlockWithRareData), kind);
|
||||
}
|
||||
|
||||
template <>
|
||||
WeakMapObject::WeakMapObjectDataItem* CustomAllocator<WeakMapObject::WeakMapObjectDataItem>::allocate(size_type GC_n, const void*)
|
||||
{
|
||||
// Un-comment this to use default allocator
|
||||
// return (WeakRefObject*)GC_MALLOC(sizeof(WeakMapObject::WeakMapObjectDataItem));
|
||||
ASSERT(GC_n == 1);
|
||||
int kind = s_gcKinds[HeapObjectKind::WeakMapObjectDataItemKind];
|
||||
return (WeakMapObject::WeakMapObjectDataItem*)GC_GENERIC_MALLOC(sizeof(WeakMapObject::WeakMapObjectDataItem), kind);
|
||||
}
|
||||
|
||||
template <>
|
||||
WeakRefObject* CustomAllocator<WeakRefObject>::allocate(size_type GC_n, const void*)
|
||||
{
|
||||
|
|
@ -474,16 +480,6 @@ FinalizationRegistryObject::FinalizationRegistryObjectItem* CustomAllocator<Fina
|
|||
int kind = s_gcKinds[HeapObjectKind::FinalizationRegistryObjectItemKind];
|
||||
return (FinalizationRegistryObject::FinalizationRegistryObjectItem*)GC_GENERIC_MALLOC(sizeof(FinalizationRegistryObject::FinalizationRegistryObjectItem), kind);
|
||||
}
|
||||
|
||||
template <>
|
||||
WeakMapObject::WeakMapObjectDataItem* CustomAllocator<WeakMapObject::WeakMapObjectDataItem>::allocate(size_type GC_n, const void*)
|
||||
{
|
||||
// Un-comment this to use default allocator
|
||||
// return (WeakMapObject::WeakMapObjectDataItem*)GC_MALLOC(sizeof(WeakMapObject::WeakMapObjectDataItem));
|
||||
ASSERT(GC_n == 1);
|
||||
int kind = s_gcKinds[HeapObjectKind::WeakMapObjectDataItemKind];
|
||||
return (WeakMapObject::WeakMapObjectDataItem*)GC_GENERIC_MALLOC(sizeof(WeakMapObject::WeakMapObjectDataItem), kind);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Escargot
|
||||
|
|
|
|||
|
|
@ -59,9 +59,9 @@ enum HeapObjectKind : unsigned {
|
|||
ArrayBufferObjectKind,
|
||||
InterpretedCodeBlockKind,
|
||||
InterpretedCodeBlockWithRareDataKind,
|
||||
WeakMapObjectDataItemKind,
|
||||
WeakRefObjectKind,
|
||||
FinalizationRegistryObjectItemKind,
|
||||
WeakMapObjectDataItemKind,
|
||||
#endif
|
||||
NumberOfKind,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -37,16 +37,6 @@ void Heap::initialize()
|
|||
});
|
||||
#endif
|
||||
|
||||
#if defined(NDEBUG)
|
||||
GC_set_warn_proc(GC_ignore_warn_proc);
|
||||
#endif
|
||||
|
||||
GC_set_oom_fn([](size_t sz) -> void* {
|
||||
ESCARGOT_LOG_ERROR("Out of memory!");
|
||||
abort();
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
GC_set_force_unmap_on_gcollect(1);
|
||||
initializeCustomAllocators();
|
||||
|
||||
|
|
|
|||
|
|
@ -73,8 +73,6 @@ void ByteCode::dumpCode(const uint8_t* byteCodeStart, const size_t endPos)
|
|||
} else {
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
} else if (curCode->m_orgOpcode == FinalizeDisposableOpcode) {
|
||||
curPos += static_cast<FinalizeDisposable*>(curCode)->m_tailDataLength;
|
||||
}
|
||||
|
||||
curPos += byteCodeLengths[curCode->m_orgOpcode];
|
||||
|
|
@ -113,11 +111,9 @@ ByteCodeBlock::ByteCodeBlock()
|
|||
static void clearByteCodeBlock(ByteCodeBlock* self)
|
||||
{
|
||||
#ifdef ESCARGOT_DEBUGGER
|
||||
if (!self->m_isOwnerMayFreed) {
|
||||
Debugger* debugger = self->codeBlock()->context()->debugger();
|
||||
if (debugger != nullptr && self->codeBlock()->markDebugging()) {
|
||||
debugger->byteCodeReleaseNotification(self);
|
||||
}
|
||||
Debugger* debugger = self->codeBlock()->context()->debugger();
|
||||
if (debugger != nullptr && self->codeBlock()->markDebugging()) {
|
||||
debugger->byteCodeReleaseNotification(self);
|
||||
}
|
||||
#endif
|
||||
self->m_code.clear();
|
||||
|
|
@ -143,7 +139,9 @@ ByteCodeBlock::ByteCodeBlock(InterpretedCodeBlock* codeBlock)
|
|||
v.push_back(this);
|
||||
GC_REGISTER_FINALIZER_NO_ORDER(this, [](void* obj, void*) {
|
||||
ByteCodeBlock* self = (ByteCodeBlock*)obj;
|
||||
clearByteCodeBlock(self); }, nullptr, nullptr, nullptr);
|
||||
clearByteCodeBlock(self);
|
||||
},
|
||||
nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void* ByteCodeBlock::operator new(size_t size)
|
||||
|
|
@ -189,7 +187,7 @@ ExtendedNodeLOC ByteCodeBlock::computeNodeLOCFromByteCode(Context* c, size_t cod
|
|||
fillLOCData(c, locData);
|
||||
}
|
||||
|
||||
size_t index = SIZE_MAX;
|
||||
size_t index = 0;
|
||||
for (size_t i = 0; i < locData->size(); i++) {
|
||||
if ((*locData)[i].first == codePosition) {
|
||||
index = (*locData)[i].second;
|
||||
|
|
@ -200,13 +198,8 @@ ExtendedNodeLOC ByteCodeBlock::computeNodeLOCFromByteCode(Context* c, size_t cod
|
|||
}
|
||||
}
|
||||
|
||||
size_t indexRelatedWithScript = 0;
|
||||
if (index == SIZE_MAX) {
|
||||
indexRelatedWithScript = cb->functionStart().index;
|
||||
} else {
|
||||
ASSERT(index >= cb->functionStart().index);
|
||||
indexRelatedWithScript = index;
|
||||
}
|
||||
ASSERT(index >= cb->functionStart().index);
|
||||
size_t indexRelatedWithScript = index;
|
||||
index -= cb->functionStart().index;
|
||||
|
||||
auto result = computeNodeLOC(cb->src(), cb->functionStart(), index);
|
||||
|
|
@ -220,13 +213,12 @@ ExtendedNodeLOC ByteCodeBlock::computeNodeLOC(StringView src, ExtendedNodeLOC so
|
|||
size_t line = sourceElementStart.line;
|
||||
size_t column = sourceElementStart.column;
|
||||
size_t srcLength = src.length();
|
||||
auto bad = src.bufferAccessData();
|
||||
for (size_t i = 0; i < index && i < srcLength; i++) {
|
||||
char16_t c = bad.charAt(i);
|
||||
char16_t c = src.charAt(i);
|
||||
column++;
|
||||
if (EscargotLexer::isLineTerminator(c)) {
|
||||
// skip \r\n
|
||||
if (c == 13 && (i + 1 < index) && bad.charAt(i + 1) == 10) {
|
||||
if (c == 13 && (i + 1 < index) && src.charAt(i + 1) == 10) {
|
||||
i++;
|
||||
}
|
||||
line++;
|
||||
|
|
@ -286,52 +278,12 @@ void ByteCodeBlock::initFunctionDeclarationWithinBlock(ByteCodeGenerateContext*
|
|||
}
|
||||
}
|
||||
|
||||
static TryStatementNode::TryStatementByteCodeContext initUsingBlock(ByteCodeBlock* byteCodeBlock, ByteCodeGenerateContext* context, size_t loc, InterpretedCodeBlock::BlockInfo* bi, size_t reg)
|
||||
{
|
||||
TryStatementNode::TryStatementByteCodeContext tryCtx;
|
||||
byteCodeBlock->pushCode(LoadLiteral(ByteCodeLOC(loc), reg, Value()), context, SIZE_MAX);
|
||||
BlockStatementNode fakeNode(nullptr);
|
||||
fakeNode.m_loc.index = loc;
|
||||
TryStatementNode::generateTryStatementStartByteCode(byteCodeBlock, context, &fakeNode, tryCtx);
|
||||
|
||||
return tryCtx;
|
||||
}
|
||||
|
||||
static void finalizeUsingBlock(ByteCodeBlock* byteCodeBlock, ByteCodeGenerateContext* context, size_t loc, InterpretedCodeBlock::BlockInfo* blockInfo, size_t reg, size_t tryStartPos)
|
||||
{
|
||||
TryStatementNode::TryStatementByteCodeContext tryCtx;
|
||||
tryCtx.tryStartPosition = tryStartPos;
|
||||
BlockStatementNode fakeNode(nullptr);
|
||||
fakeNode.m_loc.index = loc;
|
||||
TryStatementNode::generateTryStatementBodyEndByteCode(byteCodeBlock, context, &fakeNode, tryCtx);
|
||||
TryStatementNode::generateTryFinalizerStatementStartByteCode(byteCodeBlock, context, &fakeNode, tryCtx, true);
|
||||
|
||||
const auto& ids = blockInfo->identifiers();
|
||||
for (size_t i = 0; i < ids.size(); i++) {
|
||||
if (ids[i].m_isUsing) {
|
||||
size_t tailDataLength = context->m_recursiveStatementStack.size() * (sizeof(ByteCodeGenerateContext::RecursiveStatementKind) + sizeof(size_t));
|
||||
byteCodeBlock->pushCode(FinalizeDisposable(ByteCodeLOC(SIZE_MAX), reg, tailDataLength), context, SIZE_MAX);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TryStatementNode::generateTryFinalizerStatementEndByteCode(byteCodeBlock, context, &fakeNode, tryCtx, true);
|
||||
|
||||
context->giveUpRegister();
|
||||
}
|
||||
|
||||
|
||||
ByteCodeBlock::ByteCodeLexicalBlockContext ByteCodeBlock::pushLexicalBlock(ByteCodeGenerateContext* context, void* bi, Node* node, bool initFunctionDeclarationInside, bool initUsingBlockInside)
|
||||
ByteCodeBlock::ByteCodeLexicalBlockContext ByteCodeBlock::pushLexicalBlock(ByteCodeGenerateContext* context, void* bi, Node* node, bool initFunctionDeclarationInside)
|
||||
{
|
||||
ASSERT(!!bi);
|
||||
InterpretedCodeBlock::BlockInfo* blockInfo = reinterpret_cast<InterpretedCodeBlock::BlockInfo*>(bi);
|
||||
|
||||
ByteCodeBlock::ByteCodeLexicalBlockContext ctx;
|
||||
ctx.loc = node->m_loc.index;
|
||||
ctx.blockInfo = bi;
|
||||
#if defined(ENABLE_TCO)
|
||||
ctx.tcoDisabledBefore = context->m_tcoDisabled;
|
||||
#endif
|
||||
ctx.lexicallyDeclaredNamesCount = context->m_lexicallyDeclaredNames->size();
|
||||
|
||||
if (blockInfo->shouldAllocateEnvironment()) {
|
||||
|
|
@ -341,54 +293,18 @@ ByteCodeBlock::ByteCodeLexicalBlockContext ByteCodeBlock::pushLexicalBlock(ByteC
|
|||
context->m_needsExtendedExecutionState = true;
|
||||
}
|
||||
|
||||
if (initUsingBlockInside) {
|
||||
const auto& ids = blockInfo->identifiers();
|
||||
for (size_t i = 0; i < ids.size(); i++) {
|
||||
if (ids[i].m_isUsing) {
|
||||
#if defined(ENABLE_TCO)
|
||||
// disable tco since meet using statement
|
||||
context->m_tcoDisabled = true;
|
||||
#endif
|
||||
// don't use reg0 for Script result
|
||||
auto reg = context->getRegister();
|
||||
if (reg == 0) {
|
||||
reg = context->getRegister();
|
||||
context->pushRegister(0);
|
||||
}
|
||||
context->m_disposableRecordRegisterStack->push_back(reg);
|
||||
ctx.usingBlockTryStartPosition = initUsingBlock(this, context, ctx.loc, blockInfo, context->m_disposableRecordRegisterStack->back()).tryStartPosition;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (initFunctionDeclarationInside) {
|
||||
initFunctionDeclarationWithinBlock(context, blockInfo, node);
|
||||
}
|
||||
ctx.lexicalBlockStartPosition = currentCodeSize();
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void ByteCodeBlock::finalizeLexicalBlock(ByteCodeGenerateContext* context, const ByteCodeBlock::ByteCodeLexicalBlockContext& ctx)
|
||||
{
|
||||
InterpretedCodeBlock::BlockInfo* blockInfo = reinterpret_cast<InterpretedCodeBlock::BlockInfo*>(ctx.blockInfo);
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
context->m_tcoDisabled = ctx.tcoDisabledBefore;
|
||||
#endif
|
||||
context->m_lexicallyDeclaredNames->resize(ctx.lexicallyDeclaredNamesCount);
|
||||
|
||||
if (ctx.usingBlockTryStartPosition != SIZE_MAX) {
|
||||
const auto& ids = blockInfo->identifiers();
|
||||
for (size_t i = 0; i < ids.size(); i++) {
|
||||
if (ids[i].m_isUsing) {
|
||||
finalizeUsingBlock(this, context, ctx.loc, blockInfo, context->m_disposableRecordRegisterStack->back(), ctx.usingBlockTryStartPosition);
|
||||
context->m_disposableRecordRegisterStack->pop_back();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx.lexicalBlockSetupStartPosition == SIZE_MAX) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@
|
|||
#include "interpreter/ByteCodeBlockData.h"
|
||||
#include "interpreter/ByteCodeGenerator.h"
|
||||
#include "runtime/ExecutionPauser.h"
|
||||
#include "util/BloomFilter.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include "parser/CodeBlock.h"
|
||||
|
|
@ -94,7 +93,6 @@ struct GlobalVariableAccessCacheItem;
|
|||
F(ToNumericIncrement) \
|
||||
F(ToNumericDecrement) \
|
||||
F(ToNumber) \
|
||||
F(ToPropertyKey) \
|
||||
F(UnaryMinus) \
|
||||
F(UnaryNot) \
|
||||
F(UnaryBitwiseNot) \
|
||||
|
|
@ -137,9 +135,6 @@ struct GlobalVariableAccessCacheItem;
|
|||
F(BindingCalleeIntoRegister) \
|
||||
F(ResolveNameAddress) \
|
||||
F(StoreByNameWithAddress) \
|
||||
F(InitializeDisposable) \
|
||||
F(FinalizeDisposable) \
|
||||
F(SetExecutionStateInStrictMode) \
|
||||
F(FillOpcodeTable) \
|
||||
F(End)
|
||||
|
||||
|
|
@ -966,31 +961,19 @@ public:
|
|||
DefineGetterSetter,
|
||||
};
|
||||
|
||||
typedef BloomFilter<1024> CreateObjectPropertyFilter;
|
||||
typedef BloomFilter<1024 * 64> CreateObjectBigPropertyFilter;
|
||||
|
||||
struct CreateObjectData : public gc {
|
||||
bool m_allPrecomputed : 1;
|
||||
bool m_wasStructureComputed : 1;
|
||||
bool m_canStoreStructureOnCode : 1;
|
||||
bool m_needsToUsePropertyFilterOnInterpreter : 1;
|
||||
bool m_needsToUseBigPropertyFilterOnInterpreter : 1;
|
||||
bool m_allPrecomputed;
|
||||
bool m_wasStructureComputed;
|
||||
bool m_canStoreStructureOnCode;
|
||||
VectorWithInlineStorage<6, ObjectStructureItem, GCUtil::gc_malloc_allocator<ObjectStructureItem>> m_properties;
|
||||
EncodedValueVector m_values;
|
||||
Object* m_target;
|
||||
CreateObjectPrepare* m_initCode;
|
||||
union {
|
||||
Optional<CreateObjectPropertyFilter*> m_filter;
|
||||
Optional<CreateObjectBigPropertyFilter*> m_bigFilter;
|
||||
};
|
||||
CreateObjectData(bool allPrecomputed, bool wasStructureComputed, bool canStoreStructureOnCode,
|
||||
bool needsToUsePropertyFilterOnInterpreter, bool needsToUseBigPropertyFilterOnInterpreter,
|
||||
size_t reserveSize, Object* target, CreateObjectPrepare* initCode)
|
||||
: m_allPrecomputed(allPrecomputed)
|
||||
, m_wasStructureComputed(wasStructureComputed)
|
||||
, m_canStoreStructureOnCode(canStoreStructureOnCode)
|
||||
, m_needsToUsePropertyFilterOnInterpreter(needsToUsePropertyFilterOnInterpreter)
|
||||
, m_needsToUseBigPropertyFilterOnInterpreter(needsToUseBigPropertyFilterOnInterpreter)
|
||||
, m_target(target)
|
||||
, m_initCode(initCode)
|
||||
{
|
||||
|
|
@ -1001,13 +984,10 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
CreateObjectPrepare(const ByteCodeLOC& loc, const size_t dataRegisterIndex, const size_t objectIndex, bool needsToUseNameFilterOnIntepreter,
|
||||
bool needsToUseBigPropertyFilterOnInterpreter)
|
||||
CreateObjectPrepare(const ByteCodeLOC& loc, const size_t dataRegisterIndex, const size_t objectIndex)
|
||||
: ByteCode(Opcode::CreateObjectPrepareOpcode, loc)
|
||||
, m_stage(Stage::Init)
|
||||
, m_allPrecomputed(false)
|
||||
, m_needsToUsePropertyFilterOnInterpreter(needsToUseNameFilterOnIntepreter)
|
||||
, m_needsToUseBigPropertyFilterOnInterpreter(needsToUseBigPropertyFilterOnInterpreter)
|
||||
, m_hasPrecomputedKey(false)
|
||||
, m_needsToUpdateFunctionName(false)
|
||||
, m_isGetter(false)
|
||||
|
|
@ -1021,8 +1001,6 @@ public:
|
|||
: ByteCode(Opcode::CreateObjectPrepareOpcode, loc)
|
||||
, m_stage(Stage::FillKeyValue)
|
||||
, m_allPrecomputed(false)
|
||||
, m_needsToUsePropertyFilterOnInterpreter(false)
|
||||
, m_needsToUseBigPropertyFilterOnInterpreter(false)
|
||||
, m_hasPrecomputedKey(hasPreComputedKey)
|
||||
, m_needsToUpdateFunctionName(needsToUpdateFunctionName)
|
||||
, m_isGetter(false)
|
||||
|
|
@ -1036,8 +1014,6 @@ public:
|
|||
: ByteCode(Opcode::CreateObjectPrepareOpcode, loc)
|
||||
, m_stage(Stage::DefineGetterSetter)
|
||||
, m_allPrecomputed(false)
|
||||
, m_needsToUsePropertyFilterOnInterpreter(false)
|
||||
, m_needsToUseBigPropertyFilterOnInterpreter(false)
|
||||
, m_hasPrecomputedKey(hasPreComputedKey)
|
||||
, m_needsToUpdateFunctionName(false)
|
||||
, m_isGetter(isGetter)
|
||||
|
|
@ -1049,8 +1025,6 @@ public:
|
|||
|
||||
Stage m_stage : 2;
|
||||
bool m_allPrecomputed : 1;
|
||||
bool m_needsToUsePropertyFilterOnInterpreter : 1;
|
||||
bool m_needsToUseBigPropertyFilterOnInterpreter : 1;
|
||||
bool m_hasPrecomputedKey : 1;
|
||||
bool m_needsToUpdateFunctionName : 1;
|
||||
bool m_isGetter : 1; // other case, this is setter
|
||||
|
|
@ -1572,26 +1546,6 @@ public:
|
|||
#endif
|
||||
};
|
||||
|
||||
class ToPropertyKey : public ByteCode {
|
||||
public:
|
||||
ToPropertyKey(const ByteCodeLOC& loc, const size_t srcIndex, const size_t dstIndex)
|
||||
: ByteCode(Opcode::ToPropertyKeyOpcode, loc)
|
||||
, m_srcIndex(srcIndex)
|
||||
, m_dstIndex(dstIndex)
|
||||
{
|
||||
}
|
||||
|
||||
ByteCodeRegisterIndex m_srcIndex;
|
||||
ByteCodeRegisterIndex m_dstIndex;
|
||||
|
||||
#ifndef NDEBUG
|
||||
void dump()
|
||||
{
|
||||
printf("to property key r%u <- r%u", m_dstIndex, m_srcIndex);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class Increment : public ByteCode {
|
||||
public:
|
||||
Increment(const ByteCodeLOC& loc, const size_t srcIndex, const size_t dstIndex)
|
||||
|
|
@ -3140,32 +3094,6 @@ public:
|
|||
#endif
|
||||
};
|
||||
|
||||
// A class definition is always strict mode code, including its heritage
|
||||
// expression and computed property names, even when it appears inside
|
||||
// non-strict code. This opcode temporarily forces the running execution state
|
||||
// into strict mode while those parts are evaluated.
|
||||
// When m_enter is true, the current strict flag is saved into m_savedStrictRegisterIndex
|
||||
// and strict mode is turned on. When m_enter is false, the strict flag is restored
|
||||
// from m_savedStrictRegisterIndex (so that nested classes restore correctly).
|
||||
class SetExecutionStateInStrictMode : public ByteCode {
|
||||
public:
|
||||
SetExecutionStateInStrictMode(const ByteCodeLOC& loc, bool enter, const size_t savedStrictRegisterIndex)
|
||||
: ByteCode(Opcode::SetExecutionStateInStrictModeOpcode, loc)
|
||||
, m_enter(enter)
|
||||
, m_savedStrictRegisterIndex(savedStrictRegisterIndex)
|
||||
{
|
||||
}
|
||||
|
||||
bool m_enter;
|
||||
ByteCodeRegisterIndex m_savedStrictRegisterIndex;
|
||||
#ifndef NDEBUG
|
||||
void dump()
|
||||
{
|
||||
printf("set execution state in strict mode (%s) <-> r%u", m_enter ? "enter" : "exit", m_savedStrictRegisterIndex);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class BindingCalleeIntoRegister : public ByteCode {
|
||||
public:
|
||||
BindingCalleeIntoRegister(const ByteCodeLOC& loc)
|
||||
|
|
@ -3222,45 +3150,6 @@ public:
|
|||
#endif
|
||||
};
|
||||
|
||||
class InitializeDisposable : public ByteCode {
|
||||
public:
|
||||
InitializeDisposable(const ByteCodeLOC& loc, bool isAsyncDisposable, const size_t srcRegisterIndex, const size_t dstRegisterIndex)
|
||||
: ByteCode(Opcode::InitializeDisposableOpcode, loc)
|
||||
, m_isAsyncDisposable(isAsyncDisposable)
|
||||
, m_srcRegisterIndex(srcRegisterIndex)
|
||||
, m_dstRegisterIndex(dstRegisterIndex)
|
||||
{
|
||||
}
|
||||
bool m_isAsyncDisposable;
|
||||
ByteCodeRegisterIndex m_srcRegisterIndex;
|
||||
ByteCodeRegisterIndex m_dstRegisterIndex;
|
||||
|
||||
#ifndef NDEBUG
|
||||
void dump()
|
||||
{
|
||||
printf("initialize disposable r%u <- r%u", m_dstRegisterIndex, m_srcRegisterIndex);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class FinalizeDisposable : public ByteCode {
|
||||
public:
|
||||
FinalizeDisposable(const ByteCodeLOC& loc, const size_t dataRegisterIndex, const size_t tailDataLength)
|
||||
: ByteCode(Opcode::FinalizeDisposableOpcode, loc)
|
||||
, m_dataRegisterIndex(dataRegisterIndex)
|
||||
, m_tailDataLength(tailDataLength)
|
||||
{
|
||||
}
|
||||
ByteCodeRegisterIndex m_dataRegisterIndex;
|
||||
ByteCodeRegisterIndex m_tailDataLength;
|
||||
#ifndef NDEBUG
|
||||
void dump()
|
||||
{
|
||||
printf("finalize disposable r%u", m_dataRegisterIndex);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
class FillOpcodeTable : public ByteCode {
|
||||
public:
|
||||
explicit FillOpcodeTable(const ByteCodeLOC& loc)
|
||||
|
|
@ -3335,57 +3224,38 @@ public:
|
|||
context->m_locData->push_back(std::make_pair(start, idx));
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG) && defined(ESCARGOT_DEBUGGER)
|
||||
const auto loc = computeNodeLOC(m_codeBlock->src(), m_codeBlock->functionStart(), idx);
|
||||
ByteCodeLOC* bytecodeLoc = &reinterpret_cast<ByteCode*>(first)->m_loc;
|
||||
bytecodeLoc->index = loc.index;
|
||||
bytecodeLoc->line = loc.line;
|
||||
bytecodeLoc->column = loc.column;
|
||||
#endif
|
||||
|
||||
m_code.resizeWithUninitializedValues(m_code.size() + sizeof(CodeType));
|
||||
for (size_t i = 0; i < sizeof(CodeType); i++) {
|
||||
m_code[start++] = *first;
|
||||
first++;
|
||||
}
|
||||
|
||||
m_requiredOperandRegisterNumber = std::max(m_requiredOperandRegisterNumber, (ByteCodeRegisterIndex)context->m_baseRegisterCount);
|
||||
|
||||
// TODO throw exception
|
||||
RELEASE_ASSERT(m_requiredOperandRegisterNumber < REGISTER_LIMIT);
|
||||
|
||||
if (std::is_same<CodeType, ExecutionPause>::value || std::is_same<CodeType, FinalizeDisposable>::value) {
|
||||
if (std::is_same<CodeType, ExecutionPause>::value) {
|
||||
pushPauseStatementExtraData(context);
|
||||
}
|
||||
}
|
||||
|
||||
struct ByteCodeLexicalBlockContext {
|
||||
size_t loc;
|
||||
size_t lexicalBlockSetupStartPosition;
|
||||
size_t lexicalBlockStartPosition;
|
||||
size_t lexicallyDeclaredNamesCount;
|
||||
size_t lexicallyDeclaredNamesCountBefore;
|
||||
size_t usingBlockTryStartPosition;
|
||||
void* blockInfo;
|
||||
#if defined(ENABLE_TCO)
|
||||
bool tcoDisabledBefore;
|
||||
#endif
|
||||
|
||||
ByteCodeLexicalBlockContext()
|
||||
: loc(SIZE_MAX)
|
||||
, lexicalBlockSetupStartPosition(SIZE_MAX)
|
||||
: lexicalBlockSetupStartPosition(SIZE_MAX)
|
||||
, lexicalBlockStartPosition(SIZE_MAX)
|
||||
, lexicallyDeclaredNamesCount(SIZE_MAX)
|
||||
, lexicallyDeclaredNamesCountBefore(SIZE_MAX)
|
||||
, usingBlockTryStartPosition(SIZE_MAX)
|
||||
, blockInfo(nullptr)
|
||||
#if defined(ENABLE_TCO)
|
||||
, tcoDisabledBefore(false)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
ByteCodeLexicalBlockContext pushLexicalBlock(ByteCodeGenerateContext* context, void* bi, Node* node, bool initFunctionDeclarationInside = true, bool initUsingBlockInside = true);
|
||||
ByteCodeLexicalBlockContext pushLexicalBlock(ByteCodeGenerateContext* context, void* bi, Node* node, bool initFunctionDeclarationInside = true);
|
||||
void finalizeLexicalBlock(ByteCodeGenerateContext* context, const ByteCodeBlock::ByteCodeLexicalBlockContext& ctx);
|
||||
void initFunctionDeclarationWithinBlock(ByteCodeGenerateContext* context, void* bi, Node* node);
|
||||
|
||||
|
|
|
|||
|
|
@ -43,8 +43,6 @@ ByteCodeGenerateContext::ByteCodeGenerateContext(InterpretedCodeBlock* codeBlock
|
|||
, m_isFunctionDeclarationBindingInitialization(false)
|
||||
, m_isVarDeclaredBindingInitialization(false)
|
||||
, m_isLexicallyDeclaredBindingInitialization(false)
|
||||
, m_isUsingBindingInitialization(false)
|
||||
, m_isAwaitUsingBindingInitialization(false)
|
||||
, m_canSkipCopyToRegister(true)
|
||||
, m_keepNumberalLiteralsInRegisterFile(numeralLiteralData)
|
||||
, m_inCallingExpressionScope(false)
|
||||
|
|
@ -58,7 +56,6 @@ ByteCodeGenerateContext::ByteCodeGenerateContext(InterpretedCodeBlock* codeBlock
|
|||
#endif
|
||||
, m_needsExtendedExecutionState(codeBlock->isAsync() || codeBlock->isGenerator())
|
||||
, m_registerStack(new std::vector<ByteCodeRegisterIndex>())
|
||||
, m_disposableRecordRegisterStack(new std::vector<ByteCodeRegisterIndex>())
|
||||
, m_lexicallyDeclaredNames(new std::vector<std::pair<size_t, AtomicString>>())
|
||||
, m_positionToContinue(0)
|
||||
, m_lexicalBlockIndex(0)
|
||||
|
|
@ -213,22 +210,11 @@ ByteCodeBreakpointContext::ByteCodeBreakpointContext(Debugger* debugger, Interpr
|
|||
: m_lastBreakpointLineOffset(0)
|
||||
, m_lastBreakpointIndexOffset(0)
|
||||
, m_originSourceLineOffset(codeBlock->script()->originSourceLineOffset())
|
||||
, m_breakpointLocations(nullptr)
|
||||
, m_sharedWithDebugger(false)
|
||||
, m_breakpointLocations()
|
||||
{
|
||||
m_breakpointLocations = new Debugger::BreakpointLocationsInfo(reinterpret_cast<Debugger::WeakCodeRef*>(codeBlock));
|
||||
if (debugger && codeBlock->markDebugging() && addBreakpointLocationsInfoToDebugger) {
|
||||
debugger->appendBreakpointLocations(m_breakpointLocations);
|
||||
m_sharedWithDebugger = true;
|
||||
}
|
||||
}
|
||||
|
||||
ByteCodeBreakpointContext::~ByteCodeBreakpointContext()
|
||||
{
|
||||
if (!m_sharedWithDebugger) {
|
||||
// directly delete each BreakpointLocationsInfo
|
||||
// because BreakpointLocationsInfo is not shared with Debugger
|
||||
delete m_breakpointLocations;
|
||||
}
|
||||
}
|
||||
#endif /* ESCARGOT_DEBUGGER */
|
||||
|
|
@ -319,22 +305,14 @@ void ByteCodeGenerator::collectByteCodeLOCData(Context* context, InterpretedCode
|
|||
|
||||
// Parsing
|
||||
Node* ast = nullptr;
|
||||
// Parsing
|
||||
try {
|
||||
if (codeBlock->isGlobalCodeBlock() || codeBlock->isEvalCode()) {
|
||||
InterpretedCodeBlock* parentCodeBlock = codeBlock->parent();
|
||||
bool allowSC = parentCodeBlock ? parentCodeBlock->allowSuperCall() : false;
|
||||
bool allowSP = parentCodeBlock ? parentCodeBlock->allowSuperProperty() : false;
|
||||
ast = esprima::parseProgram(context, codeBlock->src(), esprima::generateClassInfoFrom(context, codeBlock->parent()),
|
||||
codeBlock->script()->isModule(), codeBlock->isStrict(), codeBlock->inWith(), allowSC, allowSP, false, true);
|
||||
} else {
|
||||
ast = esprima::parseSingleFunction(context, codeBlock);
|
||||
}
|
||||
} catch (esprima::Error* orgError) {
|
||||
// ignore error
|
||||
context->astAllocator().reset();
|
||||
GC_enable();
|
||||
return;
|
||||
if (codeBlock->isGlobalCodeBlock() || codeBlock->isEvalCode()) {
|
||||
InterpretedCodeBlock* parentCodeBlock = codeBlock->parent();
|
||||
bool allowSC = parentCodeBlock ? parentCodeBlock->allowSuperCall() : false;
|
||||
bool allowSP = parentCodeBlock ? parentCodeBlock->allowSuperProperty() : false;
|
||||
ast = esprima::parseProgram(context, codeBlock->src(), esprima::generateClassInfoFrom(context, codeBlock->parent()),
|
||||
codeBlock->script()->isModule(), codeBlock->isStrict(), codeBlock->inWith(), allowSC, allowSP, false, true);
|
||||
} else {
|
||||
ast = esprima::parseSingleFunction(context, codeBlock);
|
||||
}
|
||||
|
||||
// Generate ByteCode
|
||||
|
|
@ -573,7 +551,6 @@ void ByteCodeGenerator::relocateByteCode(ByteCodeBlock* block)
|
|||
break;
|
||||
}
|
||||
case ToNumberOpcode:
|
||||
case ToPropertyKeyOpcode:
|
||||
case IncrementOpcode:
|
||||
case DecrementOpcode:
|
||||
case UnaryMinusOpcode:
|
||||
|
|
@ -889,18 +866,6 @@ void ByteCodeGenerator::relocateByteCode(ByteCodeBlock* block)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case InitializeDisposableOpcode: {
|
||||
InitializeDisposable* cd = (InitializeDisposable*)currentCode;
|
||||
ASSIGN_STACKINDEX_IF_NEEDED(cd->m_srcRegisterIndex, stackBase, stackBaseWillBe, stackVariableSize);
|
||||
ASSIGN_STACKINDEX_IF_NEEDED(cd->m_dstRegisterIndex, stackBase, stackBaseWillBe, stackVariableSize);
|
||||
break;
|
||||
}
|
||||
case FinalizeDisposableOpcode: {
|
||||
FinalizeDisposable* cd = (FinalizeDisposable*)currentCode;
|
||||
ASSIGN_STACKINDEX_IF_NEEDED(cd->m_dataRegisterIndex, stackBase, stackBaseWillBe, stackVariableSize);
|
||||
code += cd->m_tailDataLength;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -936,12 +901,15 @@ void ByteCodeGenerator::printByteCode(Context* context, ByteCodeBlock* block)
|
|||
}
|
||||
|
||||
size_t localStart = 0;
|
||||
if (codeBlock->isFunctionNameSaveOnHeap() || codeBlock->isFunctionNameUsedBySelf()) {
|
||||
if (codeBlock->isFunctionExpression() && !codeBlock->isFunctionNameSaveOnHeap()) {
|
||||
localStart = 1;
|
||||
}
|
||||
for (size_t i = localStart; i < codeBlock->identifierInfos().size(); i++) {
|
||||
if (codeBlock->identifierInfos()[i].m_needToAllocateOnStack) {
|
||||
auto name = codeBlock->identifierInfos()[i].m_name.string()->toNonGCUTF8StringData();
|
||||
if (i == 0 && codeBlock->isFunctionExpression()) {
|
||||
name += "(function name)";
|
||||
}
|
||||
printf("`r%d,var %s`,", (int)b++, name.data());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ struct ClassContextInformation {
|
|||
, m_prototypeIndex(SIZE_MAX)
|
||||
, m_superIndex(SIZE_MAX)
|
||||
, m_name()
|
||||
, m_src(String::emptyString())
|
||||
, m_src(String::emptyString)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -58,12 +58,8 @@ struct ByteCodeBreakpointContext {
|
|||
size_t m_originSourceLineOffset; // original source's start line offset
|
||||
Debugger::BreakpointLocationsInfo* m_breakpointLocations;
|
||||
bool m_parsingEnabled;
|
||||
bool m_sharedWithDebugger;
|
||||
|
||||
ByteCodeBreakpointContext(Debugger* debugger, InterpretedCodeBlock* codeBlock, bool addBreakpointLocationsInfoToDebugger = true);
|
||||
~ByteCodeBreakpointContext();
|
||||
|
||||
MAKE_STACK_ALLOCATED();
|
||||
};
|
||||
#endif /* ESCARGOT_DEBUGGER */
|
||||
|
||||
|
|
@ -83,8 +79,6 @@ struct ByteCodeGenerateContext {
|
|||
, m_isFunctionDeclarationBindingInitialization(contextBefore.m_isFunctionDeclarationBindingInitialization)
|
||||
, m_isVarDeclaredBindingInitialization(contextBefore.m_isVarDeclaredBindingInitialization)
|
||||
, m_isLexicallyDeclaredBindingInitialization(contextBefore.m_isLexicallyDeclaredBindingInitialization)
|
||||
, m_isUsingBindingInitialization(contextBefore.m_isUsingBindingInitialization)
|
||||
, m_isAwaitUsingBindingInitialization(contextBefore.m_isAwaitUsingBindingInitialization)
|
||||
, m_canSkipCopyToRegister(contextBefore.m_canSkipCopyToRegister)
|
||||
, m_keepNumberalLiteralsInRegisterFile(contextBefore.m_keepNumberalLiteralsInRegisterFile)
|
||||
, m_inCallingExpressionScope(contextBefore.m_inCallingExpressionScope)
|
||||
|
|
@ -98,14 +92,12 @@ struct ByteCodeGenerateContext {
|
|||
#endif
|
||||
, m_needsExtendedExecutionState(contextBefore.m_needsExtendedExecutionState)
|
||||
, m_registerStack(contextBefore.m_registerStack)
|
||||
, m_disposableRecordRegisterStack(contextBefore.m_disposableRecordRegisterStack)
|
||||
, m_lexicallyDeclaredNames(contextBefore.m_lexicallyDeclaredNames)
|
||||
, m_positionToContinue(contextBefore.m_positionToContinue)
|
||||
, m_recursiveStatementStack(contextBefore.m_recursiveStatementStack)
|
||||
, m_lexicalBlockIndex(contextBefore.m_lexicalBlockIndex)
|
||||
, m_classInfo(contextBefore.m_classInfo)
|
||||
, m_numeralLiteralData(contextBefore.m_numeralLiteralData) // should be NumeralLiteralVector
|
||||
, m_currentLoopLabels(contextBefore.m_currentLoopLabels)
|
||||
#if defined(ENABLE_TCO)
|
||||
, m_returnRegister(contextBefore.m_returnRegister)
|
||||
#endif
|
||||
|
|
@ -183,33 +175,26 @@ struct ByteCodeGenerateContext {
|
|||
void registerJumpPositionsToComplexCase(size_t frontlimit)
|
||||
{
|
||||
ASSERT(tryCatchWithBlockStatementCount());
|
||||
// `frontlimit` is the byte position of the first instruction of the block body
|
||||
// (i.e. lexicalBlockStartPosition, set right after the BlockOperation opcode).
|
||||
// A break/continue jump located at exactly that position is the very first
|
||||
// statement of the block body and therefore IS inside the block, so it must be
|
||||
// morphed into a JumpComplexCase to unwind the block's lexical environment.
|
||||
// Using strictly greater-than (`>`) here would skip such a jump and leave the
|
||||
// block environment un-popped (Issue #1571), so the comparison must be `>=`.
|
||||
for (unsigned i = 0; i < m_breakStatementPositions.size(); i++) {
|
||||
if (m_breakStatementPositions[i] >= (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_breakStatementPositions[i]) == m_complexCaseStatementPositions.end()) {
|
||||
if (m_breakStatementPositions[i] > (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_breakStatementPositions[i]) == m_complexCaseStatementPositions.end()) {
|
||||
m_complexCaseStatementPositions.insert(std::make_pair(m_breakStatementPositions[i], tryCatchWithBlockStatementCount()));
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < m_continueStatementPositions.size(); i++) {
|
||||
if (m_continueStatementPositions[i] >= (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_continueStatementPositions[i]) == m_complexCaseStatementPositions.end()) {
|
||||
if (m_continueStatementPositions[i] > (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_continueStatementPositions[i]) == m_complexCaseStatementPositions.end()) {
|
||||
m_complexCaseStatementPositions.insert(std::make_pair(m_continueStatementPositions[i], tryCatchWithBlockStatementCount()));
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < m_labelledBreakStatmentPositions.size(); i++) {
|
||||
if (m_labelledBreakStatmentPositions[i].second >= (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_labelledBreakStatmentPositions[i].second) == m_complexCaseStatementPositions.end()) {
|
||||
if (m_labelledBreakStatmentPositions[i].second > (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_labelledBreakStatmentPositions[i].second) == m_complexCaseStatementPositions.end()) {
|
||||
m_complexCaseStatementPositions.insert(std::make_pair(m_labelledBreakStatmentPositions[i].second, tryCatchWithBlockStatementCount()));
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < m_labelledContinueStatmentPositions.size(); i++) {
|
||||
if (m_labelledContinueStatmentPositions[i].second >= (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_labelledContinueStatmentPositions[i].second) == m_complexCaseStatementPositions.end()) {
|
||||
if (m_labelledContinueStatmentPositions[i].second > (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_labelledContinueStatmentPositions[i].second) == m_complexCaseStatementPositions.end()) {
|
||||
m_complexCaseStatementPositions.insert(std::make_pair(m_labelledContinueStatmentPositions[i].second, tryCatchWithBlockStatementCount()));
|
||||
}
|
||||
}
|
||||
|
|
@ -356,8 +341,6 @@ struct ByteCodeGenerateContext {
|
|||
bool m_isFunctionDeclarationBindingInitialization : 1;
|
||||
bool m_isVarDeclaredBindingInitialization : 1;
|
||||
bool m_isLexicallyDeclaredBindingInitialization : 1;
|
||||
bool m_isUsingBindingInitialization : 1;
|
||||
bool m_isAwaitUsingBindingInitialization : 1;
|
||||
bool m_canSkipCopyToRegister : 1;
|
||||
bool m_keepNumberalLiteralsInRegisterFile : 1;
|
||||
bool m_inCallingExpressionScope : 1;
|
||||
|
|
@ -372,7 +355,6 @@ struct ByteCodeGenerateContext {
|
|||
bool m_needsExtendedExecutionState : 1;
|
||||
|
||||
std::shared_ptr<std::vector<ByteCodeRegisterIndex>> m_registerStack;
|
||||
std::shared_ptr<std::vector<ByteCodeRegisterIndex>> m_disposableRecordRegisterStack;
|
||||
std::shared_ptr<std::vector<std::pair<size_t, AtomicString>>> m_lexicallyDeclaredNames;
|
||||
std::vector<AtomicString> m_initializedParameterNames;
|
||||
std::vector<size_t> m_breakStatementPositions;
|
||||
|
|
@ -398,10 +380,6 @@ struct ByteCodeGenerateContext {
|
|||
ClassContextInformation m_classInfo;
|
||||
std::map<size_t, size_t> m_complexCaseStatementPositions;
|
||||
void* m_numeralLiteralData; // should be NumeralLiteralVector
|
||||
// Labels directly targeting the loop that is about to be generated (a loop may
|
||||
// carry more than one label, e.g. `L1: L2: for (...)`). A loop clears this list
|
||||
// before generating its body so that nested loops do not inherit it. - Issue #1571/#1577
|
||||
std::vector<String*> m_currentLoopLabels;
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
size_t m_returnRegister; // for tail call optimizaiton (TCO)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -40,6 +40,12 @@ public:
|
|||
};
|
||||
|
||||
static Value interpret(ExecutionState* state, ByteCodeBlock* byteCodeBlock, size_t programCounter, Value* registerFile);
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
static void initTCOBuffer();
|
||||
|
||||
static MAY_THREAD_LOCAL Value* tcoBuffer;
|
||||
#endif
|
||||
};
|
||||
} // namespace Escargot
|
||||
|
||||
|
|
|
|||
1957
src/intl/Intl.cpp
1957
src/intl/Intl.cpp
File diff suppressed because it is too large
Load diff
205
src/intl/Intl.h
205
src/intl/Intl.h
|
|
@ -21,120 +21,8 @@
|
|||
#ifndef __EscargotIntlObject__
|
||||
#define __EscargotIntlObject__
|
||||
|
||||
#include "util/ISO8601.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
struct MonthCode {
|
||||
unsigned monthNumber = 0;
|
||||
bool isLeapMonth = false;
|
||||
bool operator==(const MonthCode& src) const
|
||||
{
|
||||
return monthNumber == src.monthNumber && isLeapMonth == src.isLeapMonth;
|
||||
}
|
||||
|
||||
bool operator!=(const MonthCode& src) const
|
||||
{
|
||||
return !operator==(src);
|
||||
}
|
||||
};
|
||||
|
||||
// https://github.com/tc39/proposal-intl-era-monthcode
|
||||
class Calendar {
|
||||
public:
|
||||
#define CALENDAR_ID_RECORDS(F) \
|
||||
F(ISO8601, "iso8601", "iso8601", "iso8601", false) \
|
||||
F(Buddhist, "buddhist", "buddhist", "buddhist", false) \
|
||||
F(Chinese, "chinese", "chinese", "chinese", false) \
|
||||
F(Coptic, "coptic", "coptic", "coptic", false) \
|
||||
F(Dangi, "dangi", "dangi", "dangi", false) \
|
||||
F(Ethiopian, "ethiopic", "ethiopic", "ethiopic", false) \
|
||||
F(EthiopianAmeteAlem, "ethioaa", "ethiopic-amete-alem", "ethioaa", false) \
|
||||
F(EthiopianAmeteAlemAlias, "ethiopic-amete-alem", "ethiopic-amete-alem", "ethioaa", true) \
|
||||
F(Gregorian, "gregory", "gregorian", "gregory", false) \
|
||||
F(Hebrew, "hebrew", "hebrew", "hebrew", false) \
|
||||
F(Indian, "indian", "indian", "indian", false) \
|
||||
F(Islamic, "islamic", "islamic", "islamic", false) \
|
||||
F(IslamicCivil, "islamic-civil", "islamic-civil", "islamic-civil", false) \
|
||||
F(IslamicCivilAlias, "islamicc", "islamic-civil", "islamic-civil", true) \
|
||||
F(IslamicRGSA, "islamic-rgsa", "islamic-rgsa", "islamic-rgsa", false) \
|
||||
F(IslamicTabular, "islamic-tbla", "islamic-tbla", "islamic-tbla", false) \
|
||||
F(IslamicUmmAlQura, "islamic-umalqura", "islamic-umalqura", "islamic-umalqura", false) \
|
||||
F(Japanese, "japanese", "japanese", "japanese", false) \
|
||||
F(Persian, "persian", "persian", "persian", false) \
|
||||
F(ROC, "roc", "roc", "roc", false)
|
||||
|
||||
enum class ID : int32_t {
|
||||
#define DEFINE_FIELD(name, string, icuString, fullName, alias) name,
|
||||
CALENDAR_ID_RECORDS(DEFINE_FIELD)
|
||||
#undef DEFINE_FIELD
|
||||
};
|
||||
|
||||
Calendar(ID id = ID::ISO8601)
|
||||
: m_id(id)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const Calendar& c) const
|
||||
{
|
||||
return toICUString() == c.toICUString();
|
||||
}
|
||||
|
||||
bool operator!=(const Calendar& c) const
|
||||
{
|
||||
return !operator==(c);
|
||||
}
|
||||
|
||||
bool isISO8601() const
|
||||
{
|
||||
return m_id == ID::ISO8601;
|
||||
}
|
||||
|
||||
ID id() const
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
bool isEraRelated() const;
|
||||
bool shouldUseICUExtendedYear() const;
|
||||
bool hasLeapMonths() const;
|
||||
bool hasEpagomenalMonths() const;
|
||||
bool sameAsGregoryExceptHandlingEraAndYear() const;
|
||||
|
||||
// https://tc39.es/proposal-intl-era-monthcode/#table-epoch-years
|
||||
int32_t epochISOYear() const;
|
||||
|
||||
// icu4c base year of chinese, dangi, roc are differ with icu4x
|
||||
int diffYearDueToICU4CAndSpecDiffer() const;
|
||||
|
||||
static Optional<Calendar> fromString(const std::string&, bool shouldAllowIslamicAndIslamicRGSA = false);
|
||||
static Optional<Calendar> fromString(String* str, bool shouldAllowIslamicAndIslamicRGSA = false);
|
||||
String* toString() const;
|
||||
std::string toICUString() const;
|
||||
UCalendar* createICUCalendar(ExecutionState& state);
|
||||
void lookupICUEra(ExecutionState& state, const std::function<bool(size_t idx, const std::string& icuEra)>& fn) const;
|
||||
|
||||
void setYear(ExecutionState& state, UCalendar* calendar, int32_t year);
|
||||
void setYear(ExecutionState& state, UCalendar* calendar, const String* era, int32_t year);
|
||||
|
||||
int32_t year(ExecutionState& state, UCalendar* calendar);
|
||||
int32_t eraYear(ExecutionState& state, UCalendar* calendar);
|
||||
String* era(ExecutionState& state, UCalendar* calendar);
|
||||
|
||||
void setOrdinalMonth(ExecutionState& state, UCalendar* calendar, int32_t month);
|
||||
void setMonth(UCalendar* calendar, MonthCode mc);
|
||||
|
||||
int32_t ordinalMonth(ExecutionState& state, UCalendar* calendar);
|
||||
MonthCode monthCode(ExecutionState& state, UCalendar* calendar);
|
||||
bool inLeapMonth(ExecutionState& state, UCalendar* calendar);
|
||||
|
||||
static ISO8601::PlainDate computeISODate(ExecutionState& state, UCalendar* ucal);
|
||||
|
||||
private:
|
||||
UCalendar* createICUCalendar(ExecutionState& state, const std::string& name);
|
||||
ID m_id;
|
||||
};
|
||||
|
||||
class Intl {
|
||||
public:
|
||||
typedef std::vector<std::string> (*LocaleDataImplFunction)(String* locale, size_t keyIndex);
|
||||
|
|
@ -142,7 +30,7 @@ public:
|
|||
struct IntlMatcherResult {
|
||||
IntlMatcherResult()
|
||||
{
|
||||
extension = locale = String::emptyString();
|
||||
extension = locale = String::emptyString;
|
||||
extensionIndex = SIZE_MAX;
|
||||
}
|
||||
|
||||
|
|
@ -151,41 +39,24 @@ public:
|
|||
size_t extensionIndex;
|
||||
};
|
||||
|
||||
static void availableTimeZones(const std::function<void(const char* buf, size_t len)>& callback, bool includeNonCanonical = false);
|
||||
static UTF16StringDataNonGCStd canonicalTimeZoneID(String* timezoneId);
|
||||
static ValueVector canonicalizeLocaleList(ExecutionState& state, Value locales);
|
||||
static StringMap resolveLocale(ExecutionState& state, const Vector<String*, GCUtil::gc_malloc_allocator<String*>>& availableLocales, const ValueVector& requestedLocales, const StringMap& options, const char* const relevantExtensionKeys[], size_t relevantExtensionKeyCount, LocaleDataImplFunction localeData);
|
||||
static Value supportedLocales(ExecutionState& state, const Vector<String*, GCUtil::gc_malloc_allocator<String*>>& availableLocales, const ValueVector& requestedLocales, Value options);
|
||||
static Optional<Object*> getOptionsObject(ExecutionState& state, const Value& options); // https://tc39.es/proposal-temporal/#sec-getoptionsobject
|
||||
enum OptionValueType {
|
||||
StringValue,
|
||||
BooleanValue,
|
||||
#if defined(ENABLE_TEMPORAL)
|
||||
NumberValue
|
||||
#endif
|
||||
};
|
||||
static Value getOption(ExecutionState& state, Object* options, Value property, OptionValueType type, Value* values, size_t valuesLength, Optional<Value> fallback);
|
||||
static Value getOption(ExecutionState& state, Object* options, Value property, OptionValueType type, Value* values, size_t valuesLength, Value fallback)
|
||||
{
|
||||
return getOption(state, options, property, type, values, valuesLength, Optional<Value>(fallback));
|
||||
}
|
||||
static Value getOption(ExecutionState& state, Object* options, Value property, OptionValueType type, Value* values, size_t valuesLength, const Value& fallback);
|
||||
template <typename T>
|
||||
static T getNumberOption(ExecutionState& state, Optional<Object*> options, String* property, double minimum, double maximum, const T& fallback);
|
||||
static std::string preferredLanguage(const std::string& language);
|
||||
static String* icuLocaleToBCP47Tag(String* string);
|
||||
static std::string convertICUCalendarKeywordToBCP47KeywordIfNeeds(const std::string& icuCalendar);
|
||||
static std::string convertBCP47KeywordToICUCalendarKeywordIfNeeds(const std::string& icuCalendar);
|
||||
static std::string convertICUCollationKeywordToBCP47KeywordIfNeeds(const std::string& icuCollation);
|
||||
static std::string localeIDBufferForLanguageTagWithNullTerminator(const std::string& tag);
|
||||
static std::string canonicalizeUnicodeExtensionsAfterICULocaleCanonicalization(std::string&& buffer);
|
||||
static Optional<std::string> canonicalizeLocaleID(const char* localeID);
|
||||
static bool isUnicodeLocaleIdentifierType(const std::string& src);
|
||||
static bool isUnicodeLanguageSubtag(const std::string& src);
|
||||
static bool isUnicodeScriptSubtag(const std::string& src);
|
||||
static bool isUnicodeRegionSubtag(const std::string& src);
|
||||
static bool isUnicodeVariantSubtag(const std::string& src);
|
||||
static bool isUnicodeExtensionAttribute(const std::string& src);
|
||||
static bool isUnicodeExtensionKey(const std::string& src);
|
||||
static Optional<std::string> languageTagForLocaleID(const char* localeID);
|
||||
|
||||
static std::vector<std::string> calendarsForLocale(String* locale, bool includeAlias);
|
||||
static std::vector<std::string> calendarsForLocale(String* locale);
|
||||
static std::vector<std::string> numberingSystemsForLocale(String* locale);
|
||||
struct CanonicalizedLangunageTag {
|
||||
Optional<String*> canonicalizedTag;
|
||||
|
|
@ -203,10 +74,8 @@ public:
|
|||
std::string privateUse;
|
||||
};
|
||||
static CanonicalizedLangunageTag canonicalizeLanguageTag(const std::string& locale, const std::string& unicodeExtensionNameShouldIgnored = "");
|
||||
static bool isStructurallyValidLanguageTag(const std::string& string);
|
||||
static CanonicalizedLangunageTag isStructurallyValidLanguageTagAndCanonicalizeLanguageTag(const std::string& locale);
|
||||
static String* getLocaleForStringLocaleConvertCase(ExecutionState& state, Value locales);
|
||||
static std::string canonicalizeCalendarTag(const std::string& s);
|
||||
|
||||
static bool isValidUnicodeLocaleIdentifier(String* value);
|
||||
// test string is `(3*8alphanum) *("-" (3*8alphanum))` sequence
|
||||
|
|
@ -218,40 +87,7 @@ public:
|
|||
int32_t type;
|
||||
};
|
||||
static void convertICUNumberFieldToEcmaNumberField(std::vector<NumberFieldItem>& fields, double x, const UTF16StringDataNonGCStd& resultString);
|
||||
static String* icuNumberFieldToString(ExecutionState& state, int32_t fieldName, double d, String* style);
|
||||
|
||||
enum class RoundingType {
|
||||
MorePrecision,
|
||||
LessPrecision,
|
||||
SignificantDigits,
|
||||
FractionDigits,
|
||||
};
|
||||
|
||||
enum class RoundingPriority {
|
||||
Auto,
|
||||
LessPrecision,
|
||||
MorePrecision
|
||||
};
|
||||
struct SetNumberFormatDigitOptionsResult {
|
||||
double minimumIntegerDigits;
|
||||
double roundingIncrement;
|
||||
String* roundingMode;
|
||||
String* trailingZeroDisplay;
|
||||
Value minimumSignificantDigits;
|
||||
Value maximumSignificantDigits;
|
||||
Value minimumFractionDigits;
|
||||
Value maximumFractionDigits;
|
||||
RoundingType roundingType;
|
||||
RoundingPriority computedRoundingPriority;
|
||||
};
|
||||
// https://402.ecma-international.org/12.0/index.html#sec-setnumberformatdigitoptions
|
||||
static SetNumberFormatDigitOptionsResult setNumberFormatDigitOptions(ExecutionState& state, Object* options,
|
||||
double mnfdDefault, double mxfdDefault, String* notation);
|
||||
static void initNumberFormatSkeleton(ExecutionState& state, const Intl::SetNumberFormatDigitOptionsResult& formatResult, String* notation, String* compactDisplay, UTF16StringDataNonGCStd& skeleton);
|
||||
|
||||
// https://tc39.es/ecma402/#sec-defaultnumberoption
|
||||
static Value defaultNumberOption(ExecutionState& state, Value value, double minimum, double maximum, double fallback);
|
||||
static Value defaultNumberOption(ExecutionState& state, Value value, double minimum, double maximum, Value fallback);
|
||||
static String* icuNumberFieldToString(ExecutionState& state, int32_t fieldName, double d);
|
||||
|
||||
#define INTL_ICU_STRING_BUFFER_OPERATION(icuFnName, ...) \
|
||||
([&]() -> std::pair<UErrorCode, UTF16StringDataNonGCStd> { \
|
||||
|
|
@ -288,34 +124,7 @@ public:
|
|||
} \
|
||||
return std::make_pair(status, output); \
|
||||
})()
|
||||
|
||||
#define INTL_ICU_STD_STRING_BUFFER_OPERATION(icuFnName, ...) \
|
||||
([&]() -> std::pair<UErrorCode, std::string> { \
|
||||
std::string output; \
|
||||
UErrorCode status = U_ZERO_ERROR; \
|
||||
auto resultLength = icuFnName(__VA_ARGS__, nullptr, 0, &status); \
|
||||
if (status == U_BUFFER_OVERFLOW_ERROR) { \
|
||||
status = U_ZERO_ERROR; \
|
||||
output.resize(resultLength); \
|
||||
icuFnName(__VA_ARGS__, (char*)output.data(), resultLength, &status); \
|
||||
} \
|
||||
return std::make_pair(status, output); \
|
||||
})()
|
||||
|
||||
#define INTL_ICU_STD_STRING_BUFFER_OPERATION_COMPLEX(icuFnName, extra, ...) \
|
||||
([&]() -> std::pair<UErrorCode, std::string> { \
|
||||
std::string output; \
|
||||
UErrorCode status = U_ZERO_ERROR; \
|
||||
auto resultLength = icuFnName(__VA_ARGS__, nullptr, 0, extra, &status); \
|
||||
if (status == U_BUFFER_OVERFLOW_ERROR) { \
|
||||
status = U_ZERO_ERROR; \
|
||||
output.resize(resultLength); \
|
||||
icuFnName(__VA_ARGS__, (char*)output.data(), resultLength, extra, &status); \
|
||||
} \
|
||||
return std::make_pair(status, output); \
|
||||
})()
|
||||
};
|
||||
|
||||
} // namespace Escargot
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -138,10 +138,10 @@ IntlCollator::CollatorResolvedOptions IntlCollator::resolvedOptions(ExecutionSta
|
|||
opt.locale = internalSlot->get(state, ObjectPropertyName(state.context()->staticStrings().lazySmallLetterLocale())).value(state, internalSlot).toString(state);
|
||||
opt.sensitivity = internalSlot->get(state, ObjectPropertyName(state.context()->staticStrings().lazySensitivity())).value(state, internalSlot).toString(state);
|
||||
opt.usage = internalSlot->get(state, ObjectPropertyName(state.context()->staticStrings().lazyUsage())).value(state, internalSlot).toString(state);
|
||||
opt.collation = internalSlot->get(state, ObjectPropertyName(state.context()->staticStrings().lazyCollation())).value(state, internalSlot).toString(state);
|
||||
opt.numeric = internalSlot->get(state, ObjectPropertyName(state.context()->staticStrings().lazyNumeric())).value(state, internalSlot).toBoolean();
|
||||
opt.collation = internalSlot->get(state, ObjectPropertyName(state.context()->staticStrings().collation)).value(state, internalSlot).toString(state);
|
||||
opt.numeric = internalSlot->get(state, ObjectPropertyName(state.context()->staticStrings().numeric)).value(state, internalSlot).toBoolean();
|
||||
opt.ignorePunctuation = internalSlot->get(state, ObjectPropertyName(state.context()->staticStrings().lazyIgnorePunctuation())).value(state, internalSlot).toBoolean();
|
||||
opt.caseFirst = internalSlot->get(state, ObjectPropertyName(state.context()->staticStrings().lazyCaseFirst())).value(state, internalSlot).toString(state);
|
||||
opt.caseFirst = internalSlot->get(state, ObjectPropertyName(state.context()->staticStrings().caseFirst)).value(state, internalSlot).toString(state);
|
||||
return opt;
|
||||
}
|
||||
|
||||
|
|
@ -203,14 +203,6 @@ void IntlCollator::initialize(ExecutionState& state, Object* collator, Context*
|
|||
// Set opt.[[localeMatcher]] to matcher.
|
||||
opt.insert(std::make_pair("localeMatcher", matcher.toString(state)));
|
||||
|
||||
Value collation = Intl::getOption(state, options.asObject(), state.context()->staticStrings().lazyCollation().string(), Intl::StringValue, nullptr, 0, Value());
|
||||
if (!collation.isUndefined()) {
|
||||
if (!Intl::isValidUnicodeLocaleIdentifierTypeNonterminalOrTypeSequence(collation.asString())) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "collation is not a well-formed");
|
||||
}
|
||||
opt.insert(std::make_pair("co", collation.asString()));
|
||||
}
|
||||
|
||||
// Table 1 – Collator options settable through both extension keys and options properties
|
||||
// Key Property Type Values
|
||||
// kn numeric "boolean"
|
||||
|
|
@ -233,9 +225,9 @@ void IntlCollator::initialize(ExecutionState& state, Object* collator, Context*
|
|||
opt.insert(std::make_pair(keyColumn->toNonGCUTF8StringData(), value.toString(state)));
|
||||
};
|
||||
|
||||
doTable1(state.context()->staticStrings().lazyKn().string(), state.context()->staticStrings().lazyNumeric().string(), Intl::BooleanValue, nullptr, 0);
|
||||
doTable1(state.context()->staticStrings().lazyKn().string(), state.context()->staticStrings().numeric.string(), Intl::BooleanValue, nullptr, 0);
|
||||
Value caseFirstValue[3] = { state.context()->staticStrings().lazyUpper().string(), state.context()->staticStrings().lazyLower().string(), state.context()->staticStrings().stringFalse.string() };
|
||||
doTable1(state.context()->staticStrings().lazyKf().string(), state.context()->staticStrings().lazyCaseFirst().string(), Intl::StringValue, caseFirstValue, 3);
|
||||
doTable1(state.context()->staticStrings().lazyKf().string(), state.context()->staticStrings().caseFirst.string(), Intl::StringValue, caseFirstValue, 3);
|
||||
|
||||
// Let relevantExtensionKeys be the value of the [[relevantExtensionKeys]] internal property of Collator.
|
||||
// Let r be the result of calling the ResolveLocale abstract operation (defined in 9.2.5) with the [[availableLocales]]
|
||||
|
|
@ -258,7 +250,7 @@ void IntlCollator::initialize(ExecutionState& state, Object* collator, Context*
|
|||
Value value;
|
||||
if (strcmp(key, "co") == 0) {
|
||||
// Let property be "collation".
|
||||
property = state.context()->staticStrings().lazyCollation();
|
||||
property = state.context()->staticStrings().collation;
|
||||
// Let value be the value of r.[[co]].
|
||||
auto iter = r.find("co");
|
||||
// If value is null, then let value be "default".
|
||||
|
|
@ -275,7 +267,7 @@ void IntlCollator::initialize(ExecutionState& state, Object* collator, Context*
|
|||
|
||||
// Else use the row of Table 1 that contains the value of key in the Key column:
|
||||
// Let property be the name given in the Property column of the row.
|
||||
property = state.context()->staticStrings().lazyNumeric();
|
||||
property = state.context()->staticStrings().numeric;
|
||||
// Let value be the value of r.[[<key>]].
|
||||
value = r.at("kn");
|
||||
// If the name given in the Type column of the row is "boolean",
|
||||
|
|
@ -290,7 +282,7 @@ void IntlCollator::initialize(ExecutionState& state, Object* collator, Context*
|
|||
|
||||
// Else use the row of Table 1 that contains the value of key in the Key column:
|
||||
// Let property be the name given in the Property column of the row.
|
||||
property = state.context()->staticStrings().lazyCaseFirst();
|
||||
property = state.context()->staticStrings().caseFirst;
|
||||
// Let value be the value of r.[[<key>]].
|
||||
value = r.at("kf");
|
||||
// If the name given in the Type column of the row is "boolean",
|
||||
|
|
@ -325,12 +317,13 @@ void IntlCollator::initialize(ExecutionState& state, Object* collator, Context*
|
|||
}
|
||||
|
||||
// Let ip be the result of calling the GetOption abstract operation with arguments options, "ignorePunctuation", "boolean", undefined, and false.
|
||||
Value ip = Intl::getOption(state, options.asObject(), state.context()->staticStrings().lazyIgnorePunctuation().string(), Intl::BooleanValue, nullptr, 0, Value());
|
||||
Value ip = Intl::getOption(state, options.asObject(), state.context()->staticStrings().lazyIgnorePunctuation().string(), Intl::BooleanValue, nullptr, 0, Value(false));
|
||||
// Set the [[ignorePunctuation]] internal property of collator to ip.
|
||||
collator->ensureInternalSlot(state)->defineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().lazyIgnorePunctuation()), ObjectPropertyDescriptor(ip.isUndefined() ? Value(false) : ip, ObjectPropertyDescriptor::WritablePresent));
|
||||
collator->ensureInternalSlot(state)->defineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().lazyIgnorePunctuation()), ObjectPropertyDescriptor(ip));
|
||||
// Set the [[boundCompare]] internal property of collator to undefined.
|
||||
// Set the [[initializedCollator]] internal property of collator to true.
|
||||
collator->ensureInternalSlot(state)->defineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().lazyInitializedCollator()), ObjectPropertyDescriptor(Value(true)));
|
||||
// Return collator.
|
||||
|
||||
{
|
||||
Object* internalSlot = collator->internalSlot();
|
||||
|
|
@ -339,7 +332,7 @@ void IntlCollator::initialize(ExecutionState& state, Object* collator, Context*
|
|||
String* locale = opt.locale;
|
||||
UColAttributeValue strength = UCOL_DEFAULT;
|
||||
UColAttributeValue caseLevel = UCOL_OFF;
|
||||
UColAttributeValue alternate = ip.isUndefined() ? UCOL_DEFAULT : UCOL_NON_IGNORABLE;
|
||||
UColAttributeValue alternate = UCOL_DEFAULT;
|
||||
UColAttributeValue numeric = UCOL_OFF;
|
||||
UColAttributeValue normalization = UCOL_ON; // normalization is always on. ecma-402 needs this
|
||||
UColAttributeValue caseFirst = UCOL_DEFAULT;
|
||||
|
|
@ -420,15 +413,6 @@ void IntlCollator::initialize(ExecutionState& state, Object* collator, Context*
|
|||
},
|
||||
nullptr);
|
||||
}
|
||||
|
||||
if (ip.isUndefined()) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
auto result = ucol_getAttribute((UCollator*)collator->internalSlot()->extraData(), UCOL_ALTERNATE_HANDLING, &status);
|
||||
ASSERT(U_SUCCESS(status));
|
||||
ip = Value(result == UCOL_SHIFTED);
|
||||
collator->ensureInternalSlot(state)->defineOwnProperty(state, ObjectPropertyName(state.context()->staticStrings().lazyIgnorePunctuation()), ObjectPropertyDescriptor(ip));
|
||||
}
|
||||
// Return collator.
|
||||
}
|
||||
|
||||
int IntlCollator::compare(ExecutionState& state, Object* collator, String* a, String* b)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -28,20 +28,17 @@ namespace Escargot {
|
|||
|
||||
class IntlDateTimeFormatObject : public DerivedObject {
|
||||
public:
|
||||
IntlDateTimeFormatObject(ExecutionState& state, Value locales, Value options, Optional<String*> toLocaleStringTimeZone = NullOption);
|
||||
IntlDateTimeFormatObject(ExecutionState& state, Object* proto, Value locales, Value options, Optional<String*> toLocaleStringTimeZone = NullOption);
|
||||
IntlDateTimeFormatObject(ExecutionState& state, Value locales, Value options);
|
||||
IntlDateTimeFormatObject(ExecutionState& state, Object* proto, Value locales, Value options);
|
||||
|
||||
virtual bool isIntlDateTimeFormatObject() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
UTF16StringDataNonGCStd format(ExecutionState& state, Value x, bool allowZonedDateTime = false);
|
||||
UTF16StringDataNonGCStd format(ExecutionState& state, double x);
|
||||
ArrayObject* formatToParts(ExecutionState& state, Value x);
|
||||
UTF16StringDataNonGCStd formatRange(ExecutionState& state, Value startDate, Value endDate);
|
||||
ArrayObject* formatRangeToParts(ExecutionState& state, Value startDate, Value endDate);
|
||||
static std::pair<Value, bool> toDateTimeOptions(ExecutionState& state, Value options, Value required, Value defaults);
|
||||
ArrayObject* formatToParts(ExecutionState& state, double x);
|
||||
static Value toDateTimeOptions(ExecutionState& state, Value options, Value required, Value defaults);
|
||||
static std::string readHourCycleFromPattern(const UTF16StringDataNonGCStd& patternString);
|
||||
String* locale() const
|
||||
{
|
||||
|
|
@ -138,35 +135,15 @@ public:
|
|||
return m_timeStyle;
|
||||
}
|
||||
|
||||
UDateFormat* icuDateFormat()
|
||||
{
|
||||
return m_icuDateFormat;
|
||||
}
|
||||
|
||||
bool allOptionsUndefined();
|
||||
|
||||
protected:
|
||||
std::tuple<double, LocalResourcePointer<UDateFormat>> icuFormatTemporalHelper(ExecutionState& state, Value value, bool allowZonedDateTime);
|
||||
static String* initDateTimeFormatMainHelper(ExecutionState& state, StringMap& opt, Object* options, const Value& hour12, StringBuilder& skeletonBuilder);
|
||||
struct DateTimeFormatOtherHelperResult {
|
||||
Optional<UDateFormat*> icuDateFormat;
|
||||
Value newHourCycle;
|
||||
Optional<String*> timeZoneICU;
|
||||
};
|
||||
static DateTimeFormatOtherHelperResult initDateTimeFormatOtherHelper(ExecutionState& state, Optional<IntlDateTimeFormatObject*> dateObject, const Value& dataLocale, String* timeZone, const Value& dateStyle, const Value& timeStyle, const Value& computedHourCycle, const Value& hourCycle, const Value& hour12, String* hour, const StringMap& opt, StringBuilder& skeletonBuilder, bool ignoreDay = false, bool ignoreYear = false, bool ignoreTimeZone = false);
|
||||
String* initDateTimeFormatMainHelper(ExecutionState& state, StringMap& opt, const Value& options, const Value& hour12, std::function<void(String* prop, Value* values, size_t valuesSize)>& doTable4, StringBuilder& skeletonBuilder);
|
||||
void initDateTimeFormatOtherHelper(ExecutionState& state, const Value& dataLocale, const Value& dateStyle, const Value& timeStyle, const Value& hourCycle, const Value& hour12, String* hour, const StringMap& opt, std::string& dataLocaleWithExtensions, StringBuilder& skeletonBuilder);
|
||||
void setDateFromPattern(ExecutionState& state, UTF16StringDataNonGCStd& patternBuffer, bool hasHourOption);
|
||||
void initICUIntervalFormatIfNecessary(ExecutionState& state);
|
||||
std::tuple<double, double, UCalendar*, UDateIntervalFormat*, LocalResourcePointer<UCalendar>, LocalResourcePointer<UDateIntervalFormat>> prepareFormatRangeArguments(ExecutionState& state, Value startDateInput, Value endDateInput);
|
||||
UTF16StringDataNonGCStd format(ExecutionState& state, UDateFormat* dateFormat, double x);
|
||||
|
||||
bool m_wasThereNoFormatOption;
|
||||
|
||||
String* m_locale;
|
||||
String* m_dataLocale;
|
||||
String* m_calendar;
|
||||
String* m_numberingSystem;
|
||||
String* m_timeZone;
|
||||
String* m_timeZoneICU;
|
||||
|
||||
EncodedValue m_hour12;
|
||||
EncodedValue m_era;
|
||||
|
|
@ -175,7 +152,6 @@ protected:
|
|||
EncodedValue m_weekday;
|
||||
EncodedValue m_day;
|
||||
EncodedValue m_dayPeriod;
|
||||
EncodedValue m_dayPeriodInput;
|
||||
EncodedValue m_hour;
|
||||
EncodedValue m_hourCycle;
|
||||
EncodedValue m_minute;
|
||||
|
|
@ -186,7 +162,6 @@ protected:
|
|||
EncodedValue m_timeStyle;
|
||||
|
||||
UDateFormat* m_icuDateFormat;
|
||||
Optional<UDateIntervalFormat*> m_icuDateIntervalFormat;
|
||||
};
|
||||
|
||||
} // namespace Escargot
|
||||
|
|
|
|||
|
|
@ -77,8 +77,8 @@ IntlDisplayNamesObject::IntlDisplayNamesObject(ExecutionState& state, Object* pr
|
|||
// Set displayNames.[[Style]] to style.
|
||||
m_style = style.asString();
|
||||
// Let type be ? GetOption(options, "type", "string", « "language", "region", "script", "currency", "calendar", "dateTimeField" », undefined).
|
||||
Value typeValues[] = { staticStrings.lazyLanguage().string(), staticStrings.lazyRegion().string(),
|
||||
staticStrings.lazyScript().string(), staticStrings.lazyCurrency().string(), staticStrings.lazyCalendar().string(), staticStrings.lazyDateTimeField().string() };
|
||||
Value typeValues[] = { staticStrings.language.string(), staticStrings.region.string(),
|
||||
staticStrings.script.string(), staticStrings.lazyCurrency().string(), staticStrings.calendar.string(), staticStrings.lazyDateTimeField().string() };
|
||||
Value type = Intl::getOption(state, optionsObject, staticStrings.lazyType().string(), Intl::StringValue, typeValues, 6, Value());
|
||||
// If type is undefined, throw a TypeError exception.
|
||||
if (type.isUndefined()) {
|
||||
|
|
@ -137,11 +137,8 @@ IntlDisplayNamesObject::IntlDisplayNamesObject(ExecutionState& state, Object* pr
|
|||
// https://tc39.es/intl-displaynames-v2/#sec-isvaliddatetimefieldcode
|
||||
static bool isValidDatetimeFieldCode(String* code)
|
||||
{
|
||||
#define TEST_ONCE(name) \
|
||||
else if (code->equals(#name)) \
|
||||
{ \
|
||||
return true; \
|
||||
}
|
||||
#define TEST_ONCE(name) \
|
||||
else if (code->equals(#name)) { return true; }
|
||||
if (false) {}
|
||||
TEST_ONCE(era)
|
||||
TEST_ONCE(year)
|
||||
|
|
@ -219,17 +216,20 @@ static String* canonicalCodeForDisplayNames(ExecutionState& state, String* type,
|
|||
// a. If code does not match the Unicode Locale Identifier type nonterminal, throw a RangeError exception.
|
||||
// b. Let code be the result of mapping code to lower case as described in 6.1.
|
||||
// c. Return code.
|
||||
std::string s = code->toNonGCUTF8StringData();
|
||||
|
||||
auto mayID = Calendar::fromString(s);
|
||||
if (mayID) {
|
||||
s = mayID.value().toString()->toNonGCUTF8StringData();
|
||||
if (code->equals("gregory")) {
|
||||
code = String::fromASCII("gregorian", sizeof("gregorian") - 1);
|
||||
} else if (code->equals("islamicc")) {
|
||||
code = String::fromASCII("islamic-civil", sizeof("islamic-civil") - 1);
|
||||
} else if (code->equals("ethioaa")) {
|
||||
code = String::fromASCII("ethiopic-amete-alem", sizeof("ethiopic-amete-alem") - 1);
|
||||
}
|
||||
|
||||
if (!Intl::isValidUnicodeLocaleIdentifier(code)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Invalid calendar code");
|
||||
}
|
||||
|
||||
std::string s = code->toNonGCUTF8StringData();
|
||||
for (size_t i = 0; i < s.length(); i++) {
|
||||
s[i] = tolower(s[i]);
|
||||
}
|
||||
|
|
@ -288,14 +288,7 @@ Value IntlDisplayNamesObject::of(ExecutionState& state, const Value& codeInput)
|
|||
} else if (m_type->equals("script")) {
|
||||
result = INTL_ICU_STRING_BUFFER_OPERATION(uldn_scriptDisplayName, m_icuLocaleDisplayNames, code->toNonGCUTF8StringData().data());
|
||||
} else if (m_type->equals("calendar")) {
|
||||
auto icuKey = Intl::convertBCP47KeywordToICUCalendarKeywordIfNeeds(code->toNonGCUTF8StringData());
|
||||
result = INTL_ICU_STRING_BUFFER_OPERATION(uldn_keyValueDisplayName, m_icuLocaleDisplayNames, "calendar", icuKey.data());
|
||||
if (U_SUCCESS(result.first)) {
|
||||
auto mayID = Calendar::fromString(icuKey);
|
||||
if (!mayID) {
|
||||
result.first = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
}
|
||||
}
|
||||
result = INTL_ICU_STRING_BUFFER_OPERATION(uldn_keyValueDisplayName, m_icuLocaleDisplayNames, "calendar", code->toNonGCUTF8StringData().data());
|
||||
} else if (m_type->equals("currency")) {
|
||||
UCurrNameStyle style = UCURR_LONG_NAME;
|
||||
if (m_style->equals("long")) {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,99 +0,0 @@
|
|||
#if defined(ENABLE_ICU) && defined(ENABLE_INTL) && defined(ENABLE_INTL_DURATIONFORMAT)
|
||||
/*
|
||||
* Copyright (c) 2025-present Samsung Electronics Co., Ltd
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __EscargotIntlDurationFormat__
|
||||
#define __EscargotIntlDurationFormat__
|
||||
|
||||
#include "runtime/Object.h"
|
||||
#include "runtime/BigInt.h"
|
||||
#include "util/ISO8601.h"
|
||||
|
||||
namespace Escargot {
|
||||
|
||||
// https://tc39.es/ecma402/#sec-todurationrecord
|
||||
using DurationRecord = ISO8601::Duration;
|
||||
|
||||
class IntlDurationFormatObject : public DerivedObject {
|
||||
public:
|
||||
IntlDurationFormatObject(ExecutionState& state, Value locales, Value options);
|
||||
IntlDurationFormatObject(ExecutionState& state, Object* proto, Value locales, Value options);
|
||||
|
||||
virtual bool isIntlDurationFormatObject() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::pair<String*, String*> data(size_t index);
|
||||
|
||||
Object* resolvedOptions(ExecutionState& state);
|
||||
String* format(ExecutionState& state, const Value& duration);
|
||||
ArrayObject* formatToParts(ExecutionState& state, const Value& duration);
|
||||
|
||||
protected:
|
||||
enum class ElementType : uint8_t {
|
||||
Literal,
|
||||
Element,
|
||||
};
|
||||
|
||||
struct Element {
|
||||
ElementType m_type;
|
||||
bool m_valueSignBit;
|
||||
ISO8601::DateTimeUnit m_unit;
|
||||
UTF16StringDataNonGCStd m_string;
|
||||
LocalResourcePointer<UFormattedNumber> m_formattedNumber;
|
||||
};
|
||||
static std::vector<Element> collectElements(ExecutionState& state, IntlDurationFormatObject* durationFormat, const DurationRecord& duration);
|
||||
|
||||
String* m_locale;
|
||||
String* m_dataLocale;
|
||||
String* m_dataLocaleWithExtensions;
|
||||
String* m_numberingSystem;
|
||||
String* m_style;
|
||||
|
||||
String* m_yearsStyle;
|
||||
String* m_yearsDisplay;
|
||||
String* m_monthsStyle;
|
||||
String* m_monthsDisplay;
|
||||
String* m_weeksStyle;
|
||||
String* m_weeksDisplay;
|
||||
String* m_daysStyle;
|
||||
String* m_daysDisplay;
|
||||
String* m_hoursStyle;
|
||||
String* m_hoursDisplay;
|
||||
String* m_minutesStyle;
|
||||
String* m_minutesDisplay;
|
||||
String* m_secondsStyle;
|
||||
String* m_secondsDisplay;
|
||||
String* m_millisecondsStyle;
|
||||
String* m_millisecondsDisplay;
|
||||
String* m_microsecondsStyle;
|
||||
String* m_microsecondsDisplay;
|
||||
String* m_nanosecondsStyle;
|
||||
String* m_nanosecondsDisplay;
|
||||
|
||||
Value m_fractionalDigits;
|
||||
|
||||
UListFormatter* m_icuListFormatter;
|
||||
};
|
||||
|
||||
} // namespace Escargot
|
||||
#endif
|
||||
#endif
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Sony Interactive Entertainment Inc.
|
||||
* Copyright (C) 2021-2023 Apple Inc. All rights reserved.
|
||||
* Copyright (C) 2021 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
|
|
@ -55,432 +55,361 @@
|
|||
|
||||
namespace Escargot {
|
||||
|
||||
class LocaleIDBuilder {
|
||||
public:
|
||||
bool initialize(const std::string&);
|
||||
std::string toCanonical();
|
||||
|
||||
void overrideLanguageScriptRegionVariants(Optional<String*> language, Optional<String*> script,
|
||||
Optional<String*> region, Optional<String*> variants);
|
||||
bool setKeywordValue(const char* key, const std::string& value);
|
||||
|
||||
private:
|
||||
std::string m_buffer;
|
||||
};
|
||||
|
||||
|
||||
bool LocaleIDBuilder::initialize(const std::string& tag)
|
||||
static Intl::CanonicalizedLangunageTag applyOptionsToTag(ExecutionState& state, String* tag, Optional<Object*> options)
|
||||
{
|
||||
if (!Intl::isStructurallyValidLanguageTag(tag)) {
|
||||
// we should not allow grandfathered tag like `i-default`(invalid lang), `no-bok`(have extlang)
|
||||
auto u8Tag = tag->toNonGCUTF8StringData();
|
||||
auto tagPart = split(u8Tag, '-');
|
||||
if ((tagPart.size() > 0 && tagPart[0].length() == 1) || (tagPart.size() > 1 && tagPart[1].length() == 3 && isAllSpecialCharacters(tagPart[1], isASCIIAlpha))) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Incorrect locale information provided");
|
||||
}
|
||||
|
||||
// If IsStructurallyValidLanguageTag(tag) is false, throw a RangeError exception.
|
||||
auto parsedResult = Intl::isStructurallyValidLanguageTagAndCanonicalizeLanguageTag(u8Tag);
|
||||
if (!parsedResult.canonicalizedTag) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Incorrect locale information provided");
|
||||
}
|
||||
|
||||
// We should not allow extlang here
|
||||
if (parsedResult.extLang.size()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Incorrect locale information provided");
|
||||
}
|
||||
|
||||
String* languageString = state.context()->staticStrings().language.string();
|
||||
// Let language be ? GetOption(options, "language", "string", undefined, undefined).
|
||||
Value language = options ? Intl::getOption(state, options.value(), languageString, Intl::StringValue, nullptr, 0, Value()) : Value();
|
||||
// If language is not undefined, then
|
||||
if (!language.isUndefined()) {
|
||||
// If language does not match the unicode_language_subtag production, throw a RangeError exception.
|
||||
auto u8Lang = language.asString()->toNonGCUTF8StringData();
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
int32_t len = uloc_forLanguageTag(u8Lang.data(), nullptr, 0, nullptr, &status);
|
||||
UNUSED_VARIABLE(len);
|
||||
if (status != U_BUFFER_OVERFLOW_ERROR || u8Lang.length() != 2 || !isAllSpecialCharacters(u8Lang, isASCIIAlpha)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "lanuage tag you give into Intl.Locale is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
String* scriptString = state.context()->staticStrings().script.string();
|
||||
// Let script be ? GetOption(options, "script", "string", undefined, undefined).
|
||||
Value script = options ? Intl::getOption(state, options.value(), scriptString, Intl::StringValue, nullptr, 0, Value()) : Value();
|
||||
// If script is not undefined, then
|
||||
if (!script.isUndefined()) {
|
||||
// If script does not match the unicode_script_subtag, throw a RangeError exception.
|
||||
String* scriptString = script.asString();
|
||||
if (scriptString->length() != 4 || !scriptString->isAllSpecialCharacters(isASCIIAlpha)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "script you give into Intl.Locale is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
String* regionString = state.context()->staticStrings().region.string();
|
||||
// Let region be ? GetOption(options, "region", "string", undefined, undefined).
|
||||
Value region = options ? Intl::getOption(state, options.value(), regionString, Intl::StringValue, nullptr, 0, Value()) : Value();
|
||||
// If region is not undefined, then
|
||||
if (!region.isUndefined()) {
|
||||
// If region does not match the unicode_region_subtag, throw a RangeError exception.
|
||||
String* regionString = region.asString();
|
||||
if ((!(regionString->length() == 2 && regionString->isAllSpecialCharacters(isASCIIAlpha)) && !(regionString->length() == 3 && regionString->isAllSpecialCharacters(isASCIIDigit)))) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "region you give into Intl.Locale is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
// Let tag to CanonicalizeUnicodeLocaleId(tag).
|
||||
// If language is not undefined,
|
||||
// Assert: tag matches the unicode_locale_id production.
|
||||
// Set tag to tag with the substring corresponding to the unicode_language_subtag production of the unicode_language_id replaced by the string language.
|
||||
// If script is not undefined, then
|
||||
// If tag does not contain a unicode_script_subtag production of the unicode_language_id, then
|
||||
// Set tag to the concatenation of the unicode_language_subtag production of the unicode_languge_id of tag, "-", script, and the rest of tag.
|
||||
// Else,
|
||||
// Set tag to tag with the substring corresponding to the unicode_script_subtag production of the unicode_language_id replaced by the string script.
|
||||
// If region is not undefined, then
|
||||
// If tag does not contain a unicode_region_subtag production of the unicode_language_id, then
|
||||
// Set tag to the concatenation of the unicode_language_subtag production of the unicode_language_id of tag, the substring corresponding to the "-" unicode_script_subtag production of the unicode_language_id if present, "-", region, and the rest of tag.
|
||||
// Else,
|
||||
// Set tag to tag with the substring corresponding to the unicode_region_subtag production of the unicode_language_id replaced by the string region.
|
||||
// Return CanonicalizeUnicodeLocaleId(tag).
|
||||
std::string localeStr;
|
||||
|
||||
if (!language.isUndefined()) {
|
||||
localeStr = language.asString()->toNonGCUTF8StringData();
|
||||
} else {
|
||||
localeStr = parsedResult.language;
|
||||
}
|
||||
|
||||
if (!script.isUndefined()) {
|
||||
localeStr += "-";
|
||||
localeStr += script.asString()->toNonGCUTF8StringData();
|
||||
} else if (parsedResult.script.length()) {
|
||||
localeStr += "-";
|
||||
localeStr += parsedResult.script;
|
||||
}
|
||||
|
||||
if (!region.isUndefined()) {
|
||||
localeStr += "-";
|
||||
localeStr += region.asString()->toNonGCUTF8StringData();
|
||||
} else if (parsedResult.region.length()) {
|
||||
localeStr += "-";
|
||||
localeStr += parsedResult.region;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < parsedResult.variant.size(); i++) {
|
||||
localeStr += "-";
|
||||
localeStr += parsedResult.variant[i];
|
||||
}
|
||||
|
||||
auto newParsedResult = Intl::isStructurallyValidLanguageTagAndCanonicalizeLanguageTag(localeStr);
|
||||
if (!newParsedResult.canonicalizedTag) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "Incorrect locale information provided");
|
||||
}
|
||||
|
||||
// We should not allow extlang here
|
||||
if (newParsedResult.extLang.size()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "First argument of Intl.Locale should have valid tag");
|
||||
}
|
||||
|
||||
newParsedResult.privateUse = parsedResult.privateUse;
|
||||
newParsedResult.extensions = parsedResult.extensions;
|
||||
newParsedResult.unicodeExtension = parsedResult.unicodeExtension;
|
||||
|
||||
return newParsedResult;
|
||||
}
|
||||
|
||||
static bool checkOptionValueIsAlphaNum38DashAlphaNum38(const std::string& value)
|
||||
{
|
||||
if (value.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
ASSERT(String::fromUTF8(tag.data(), tag.size())->is8Bit());
|
||||
m_buffer = Intl::localeIDBufferForLanguageTagWithNullTerminator(tag);
|
||||
return m_buffer.size();
|
||||
|
||||
auto parts = split(value, '-');
|
||||
if (parts.size() > 2) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < parts.size(); i++) {
|
||||
const auto& s = parts[i];
|
||||
if (s.length() < 3 || s.length() > 8 || !isAllSpecialCharacters(s, isASCIIAlphanumeric)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::string LocaleIDBuilder::toCanonical()
|
||||
{
|
||||
ASSERT(m_buffer.size());
|
||||
|
||||
auto buffer = Intl::canonicalizeLocaleID(m_buffer.data());
|
||||
if (!buffer)
|
||||
return {};
|
||||
|
||||
return Intl::canonicalizeUnicodeExtensionsAfterICULocaleCanonicalization(std::move(buffer.value()));
|
||||
}
|
||||
|
||||
// Because ICU's C API doesn't have set[Language|Script|Region|Variants] functions...
|
||||
void LocaleIDBuilder::overrideLanguageScriptRegionVariants(Optional<String*> language, Optional<String*> script,
|
||||
Optional<String*> region, Optional<String*> variants)
|
||||
{
|
||||
unsigned length = m_buffer.length();
|
||||
const std::string& localeIDView = m_buffer;
|
||||
|
||||
auto endOfLanguageScriptRegionVariant = localeIDView.find(ULOC_KEYWORD_SEPARATOR);
|
||||
if (endOfLanguageScriptRegionVariant == std::string::npos) {
|
||||
endOfLanguageScriptRegionVariant = length;
|
||||
}
|
||||
|
||||
std::vector<std::string> subtags;
|
||||
|
||||
for (auto subtag : split(localeIDView.substr(0, endOfLanguageScriptRegionVariant), '_')) {
|
||||
subtags.push_back(subtag);
|
||||
}
|
||||
|
||||
if (language) {
|
||||
subtags[0] = language->toNonGCUTF8StringData();
|
||||
}
|
||||
|
||||
bool hasScript = subtags.size() > 1 && subtags[1].length() == 4;
|
||||
if (script) {
|
||||
if (hasScript) {
|
||||
subtags[1] = script.value()->toNonGCUTF8StringData();
|
||||
} else {
|
||||
subtags.insert(subtags.begin() + 1, script.value()->toNonGCUTF8StringData());
|
||||
hasScript = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (region) {
|
||||
size_t index = hasScript ? 2 : 1;
|
||||
bool hasRegion = subtags.size() > index && subtags[index].length() < 4;
|
||||
if (hasRegion) {
|
||||
subtags[index] = region.value()->toNonGCUTF8StringData();
|
||||
} else {
|
||||
subtags.insert(index + subtags.begin(), region.value()->toNonGCUTF8StringData());
|
||||
}
|
||||
}
|
||||
|
||||
if (variants) {
|
||||
while (subtags.size() > 1) {
|
||||
auto& lastSubtag = subtags.back();
|
||||
bool isVariant = (lastSubtag.length() >= 5 && lastSubtag.length() <= 8) || (lastSubtag.length() == 4 && isASCIIDigit(lastSubtag[0]));
|
||||
if (!isVariant)
|
||||
break;
|
||||
subtags.pop_back();
|
||||
}
|
||||
if (variants->length()) {
|
||||
for (auto variant : split(variants->toNonGCUTF8StringData(), '-')) {
|
||||
subtags.push_back(variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string buffer;
|
||||
bool hasAppended = false;
|
||||
for (auto subtag : subtags) {
|
||||
if (hasAppended) {
|
||||
buffer.push_back('_');
|
||||
} else {
|
||||
hasAppended = true;
|
||||
}
|
||||
|
||||
buffer += subtag;
|
||||
}
|
||||
|
||||
if (endOfLanguageScriptRegionVariant != length) {
|
||||
auto rest = localeIDView.substr(endOfLanguageScriptRegionVariant, length - endOfLanguageScriptRegionVariant);
|
||||
buffer += rest;
|
||||
}
|
||||
|
||||
m_buffer.swap(buffer);
|
||||
}
|
||||
|
||||
bool LocaleIDBuilder::setKeywordValue(const char* key, const std::string& value)
|
||||
{
|
||||
ASSERT(m_buffer.size());
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
auto length = uloc_setKeywordValue(key, value.data(), (char*)m_buffer.data(), m_buffer.size(), &status);
|
||||
// uloc_setKeywordValue does not set U_STRING_NOT_TERMINATED_WARNING.
|
||||
if (status == U_BUFFER_OVERFLOW_ERROR) {
|
||||
m_buffer.resize(length);
|
||||
status = U_ZERO_ERROR;
|
||||
uloc_setKeywordValue(key, value.data(), (char*)m_buffer.data(), length + 1, &status);
|
||||
} else if (U_SUCCESS(status)) {
|
||||
m_buffer.resize(length);
|
||||
}
|
||||
return U_SUCCESS(status);
|
||||
}
|
||||
|
||||
IntlLocaleObject::IntlLocaleObject(ExecutionState& state, String* tag, Object* options)
|
||||
IntlLocaleObject::IntlLocaleObject(ExecutionState& state, String* tag, Optional<Object*> options)
|
||||
: IntlLocaleObject(state, state.context()->globalObject()->intlLocalePrototype(), tag, options)
|
||||
{
|
||||
}
|
||||
|
||||
IntlLocaleObject::IntlLocaleObject(ExecutionState& state, Object* proto, String* tag, Object* options)
|
||||
IntlLocaleObject::IntlLocaleObject(ExecutionState& state, Object* proto, String* tag, Optional<Object*> options)
|
||||
: DerivedObject(state, proto)
|
||||
, m_language(nullptr)
|
||||
, m_script(nullptr)
|
||||
, m_region(nullptr)
|
||||
, m_baseName(nullptr)
|
||||
, m_locale(nullptr)
|
||||
{
|
||||
auto u8Tag = tag->toNonGCUTF8StringData();
|
||||
LocaleIDBuilder localeID;
|
||||
if (!localeID.initialize(u8Tag)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "invalid language tag");
|
||||
}
|
||||
// Set tag to ? ApplyOptionsToTag(tag, options).
|
||||
Intl::CanonicalizedLangunageTag buildResult = applyOptionsToTag(state, tag, options);
|
||||
m_language = String::fromUTF8(buildResult.language.data(), buildResult.language.length());
|
||||
m_script = String::fromUTF8(buildResult.script.data(), buildResult.script.length());
|
||||
m_region = String::fromUTF8(buildResult.region.data(), buildResult.region.length());
|
||||
m_baseName = buildResult.canonicalizedTag.value();
|
||||
|
||||
Value language = Intl::getOption(state, options, state.context()->staticStrings().lazyLanguage().string(), Intl::StringValue, nullptr, 0, Value());
|
||||
if (!language.isUndefined() && !Intl::isUnicodeLanguageSubtag(language.asString()->toNonGCUTF8StringData())) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "invalid language tag");
|
||||
}
|
||||
// Let opt be a new Record.
|
||||
Optional<String*> ca;
|
||||
Optional<String*> co;
|
||||
Optional<String*> hc;
|
||||
Optional<String*> nu;
|
||||
Optional<String*> kf;
|
||||
Optional<String*> kn;
|
||||
|
||||
Value script = Intl::getOption(state, options, state.context()->staticStrings().lazyScript().string(), Intl::StringValue, nullptr, 0, Value());
|
||||
if (!script.isUndefined() && !Intl::isUnicodeScriptSubtag(script.asString()->toNonGCUTF8StringData())) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "script is not a well-formed script value");
|
||||
}
|
||||
|
||||
Value region = Intl::getOption(state, options, state.context()->staticStrings().lazyRegion().string(), Intl::StringValue, nullptr, 0, Value());
|
||||
if (!region.isUndefined() && !Intl::isUnicodeRegionSubtag(region.asString()->toNonGCUTF8StringData())) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "region is not a well-formed region value");
|
||||
}
|
||||
|
||||
Value variantsValue = Intl::getOption(state, options, state.context()->staticStrings().lazyVariants().string(), Intl::StringValue, nullptr, 0, Value());
|
||||
if (!variantsValue.isUndefined()) {
|
||||
const char* variantsErrorMessage = "variants is not a well-formed variants value";
|
||||
std::string variants = variantsValue.asString()->toNonGCUTF8StringData();
|
||||
if (variants.empty() || variants[0] == '-' || variants.back() == '-' || variants.find("--") != std::string::npos) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, variantsErrorMessage);
|
||||
}
|
||||
auto variantList = split(variants, '-');
|
||||
if (variantList.size() == 1) {
|
||||
if (!Intl::isUnicodeVariantSubtag(variantList[0])) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, variantsErrorMessage);
|
||||
}
|
||||
} else {
|
||||
std::unordered_set<std::string> seenVariants;
|
||||
for (const auto& variant : variantList) {
|
||||
if (!Intl::isUnicodeVariantSubtag(variant)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, variantsErrorMessage);
|
||||
}
|
||||
std::string lowerVariant = variant;
|
||||
std::transform(lowerVariant.begin(), lowerVariant.end(), lowerVariant.begin(),
|
||||
[](char c) { return std::tolower(c); });
|
||||
if (seenVariants.find(lowerVariant) != seenVariants.end()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, variantsErrorMessage);
|
||||
}
|
||||
seenVariants.insert(lowerVariant);
|
||||
}
|
||||
auto& parsedUnicodeExtension = buildResult.unicodeExtension;
|
||||
for (size_t i = 0; i < parsedUnicodeExtension.size(); i++) {
|
||||
if (!ca && parsedUnicodeExtension[i].first == "ca") {
|
||||
ca = String::fromUTF8(parsedUnicodeExtension[i].second.data(), parsedUnicodeExtension[i].second.length());
|
||||
} else if (!co && parsedUnicodeExtension[i].first == "co") {
|
||||
co = String::fromUTF8(parsedUnicodeExtension[i].second.data(), parsedUnicodeExtension[i].second.length());
|
||||
} else if (!hc && parsedUnicodeExtension[i].first == "hc") {
|
||||
hc = String::fromUTF8(parsedUnicodeExtension[i].second.data(), parsedUnicodeExtension[i].second.length());
|
||||
} else if (!kf && parsedUnicodeExtension[i].first == "kf") {
|
||||
kf = String::fromUTF8(parsedUnicodeExtension[i].second.data(), parsedUnicodeExtension[i].second.length());
|
||||
} else if (!kn && parsedUnicodeExtension[i].first == "kn") {
|
||||
kn = String::fromUTF8(parsedUnicodeExtension[i].second.data(), parsedUnicodeExtension[i].second.length());
|
||||
} else if (!nu && parsedUnicodeExtension[i].first == "nu") {
|
||||
nu = String::fromUTF8(parsedUnicodeExtension[i].second.data(), parsedUnicodeExtension[i].second.length());
|
||||
}
|
||||
}
|
||||
|
||||
if (!language.isUndefined() || !script.isUndefined() || !region.isUndefined() || !variantsValue.isUndefined()) {
|
||||
localeID.overrideLanguageScriptRegionVariants(language.isUndefined() ? nullptr : language.asString(),
|
||||
script.isUndefined() ? nullptr : script.asString(),
|
||||
region.isUndefined() ? nullptr : region.asString(),
|
||||
variantsValue.isUndefined() ? nullptr : variantsValue.asString());
|
||||
}
|
||||
|
||||
|
||||
Value calendar = Intl::getOption(state, options, state.context()->staticStrings().lazyCalendar().string(), Intl::StringValue, nullptr, 0, Value());
|
||||
// Let calendar be ? GetOption(options, "calendar", "string", undefined, undefined).
|
||||
String* calendarString = state.context()->staticStrings().calendar.string();
|
||||
Value calendar = options ? Intl::getOption(state, options.value(), calendarString, Intl::StringValue, nullptr, 0, Value()) : Value();
|
||||
// If calendar is not undefined, then
|
||||
if (!calendar.isUndefined()) {
|
||||
std::string s = calendar.asString()->toNonGCUTF8StringData();
|
||||
if (!Intl::isUnicodeLocaleIdentifierType(s) || !localeID.setKeywordValue("calendar", Intl::canonicalizeCalendarTag(s))) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "calendar is not a well-formed calendar value");
|
||||
// If calendar does not match the type sequence (from UTS 35 Unicode Locale Identifier, section 3.2), throw a RangeError exception.
|
||||
// If calendar does not match the [(3*8alphanum) *("-" (3*8alphanum))] sequence, throw a RangeError exception.
|
||||
std::string value = calendar.asString()->toNonGCUTF8StringData();
|
||||
if (!uloc_toLegacyType("calendar", value.data()) || !checkOptionValueIsAlphaNum38DashAlphaNum38(value)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "`calendar` option you input is not valid");
|
||||
}
|
||||
// Set opt.[[ca]] to calendar.
|
||||
ca = calendar.asString();
|
||||
}
|
||||
|
||||
Value collation = Intl::getOption(state, options, state.context()->staticStrings().lazyCollation().string(), Intl::StringValue, nullptr, 0, Value());
|
||||
// Let collation be ? GetOption(options, "collation", "string", undefined, undefined).
|
||||
Value collation = options ? Intl::getOption(state, options.value(), state.context()->staticStrings().collation.string(), Intl::StringValue, nullptr, 0, Value()) : Value();
|
||||
// If collation is not undefined, then
|
||||
if (!collation.isUndefined()) {
|
||||
std::string s = collation.asString()->toNonGCUTF8StringData();
|
||||
if (!Intl::isUnicodeLocaleIdentifierType(s) || !localeID.setKeywordValue("collation", s)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "collation is not a well-formed collation value");
|
||||
// If collation does not match the type sequence (from UTS 35 Unicode Locale Identifier, section 3.2), throw a RangeError exception.
|
||||
// If collation does not match the [(3*8alphanum) *("-" (3*8alphanum))] sequence, throw a RangeError exception.
|
||||
std::string value = collation.asString()->toNonGCUTF8StringData();
|
||||
if (!uloc_toLegacyType("collation", value.data()) || !checkOptionValueIsAlphaNum38DashAlphaNum38(value)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "`collation` option you input is not valid");
|
||||
}
|
||||
// Set opt.[[co]] to collation.
|
||||
co = collation.asString();
|
||||
}
|
||||
|
||||
Value firstDayOfWeek = Intl::getOption(state, options, state.context()->staticStrings().lazyFirstDayOfWeek().string(), Intl::StringValue, nullptr, 0, Value());
|
||||
if (!firstDayOfWeek.isUndefined()) {
|
||||
std::string s = firstDayOfWeek.asString()->toNonGCUTF8StringData();
|
||||
|
||||
if (s.length() == 1) {
|
||||
if (s == "0") {
|
||||
s = "sun";
|
||||
} else if (s == "1") {
|
||||
s = "mon";
|
||||
} else if (s == "2") {
|
||||
s = "tue";
|
||||
} else if (s == "3") {
|
||||
s = "wed";
|
||||
} else if (s == "4") {
|
||||
s = "thu";
|
||||
} else if (s == "5") {
|
||||
s = "fri";
|
||||
} else if (s == "6") {
|
||||
s = "sat";
|
||||
} else if (s == "7") {
|
||||
s = "sun";
|
||||
}
|
||||
}
|
||||
|
||||
if (!Intl::isUnicodeLocaleIdentifierType(s) || !localeID.setKeywordValue("fw", s)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "firstDayOfWeek is not a well-formed firstDayOfWeek value");
|
||||
}
|
||||
}
|
||||
|
||||
// Let hc be ? GetOption(options, "hourCycle", "string", « "h11", "h12", "h23", "h24" », undefined).
|
||||
Value hourCycleValues[4] = { state.context()->staticStrings().lazyH11().string(), state.context()->staticStrings().lazyH12().string(), state.context()->staticStrings().lazyH23().string(), state.context()->staticStrings().lazyH24().string() };
|
||||
Value hourCycle = options ? Intl::getOption(state, options, state.context()->staticStrings().lazyHourCycle().string(), Intl::StringValue, hourCycleValues, 4, Value()) : Value();
|
||||
Value hourCycle = options ? Intl::getOption(state, options.value(), state.context()->staticStrings().hourCycle.string(), Intl::StringValue, hourCycleValues, 4, Value()) : Value();
|
||||
// Set opt.[[hc]] to hc.
|
||||
if (!hourCycle.isUndefined()) {
|
||||
localeID.setKeywordValue("hours", hourCycle.asString()->toNonGCUTF8StringData());
|
||||
hc = hourCycle.asString();
|
||||
}
|
||||
|
||||
// Let kf be ? GetOption(options, "caseFirst", "string", « "upper", "lower", "false" », undefined).
|
||||
Value caseFirstValues[3] = { state.context()->staticStrings().lazyUpper().string(), state.context()->staticStrings().lazyLower().string(), state.context()->staticStrings().stringFalse.string() };
|
||||
Value caseFirst = options ? Intl::getOption(state, options, state.context()->staticStrings().lazyCaseFirst().string(), Intl::StringValue, caseFirstValues, 3, Value()) : Value();
|
||||
// Set opt.[[kf]] to kf.
|
||||
Value caseFirst = options ? Intl::getOption(state, options.value(), state.context()->staticStrings().caseFirst.string(), Intl::StringValue, caseFirstValues, 3, Value()) : Value();
|
||||
if (!caseFirst.isUndefined()) {
|
||||
localeID.setKeywordValue("colcasefirst", caseFirst.asString()->toNonGCUTF8StringData());
|
||||
kf = caseFirst.asString();
|
||||
}
|
||||
|
||||
Value numeric = Intl::getOption(state, options, state.context()->staticStrings().lazyNumeric().string(), Intl::BooleanValue, nullptr, 0, Value());
|
||||
// Let kn be ? GetOption(options, "numeric", "boolean", undefined, undefined).
|
||||
Value numeric = options ? Intl::getOption(state, options.value(), state.context()->staticStrings().numeric.string(), Intl::BooleanValue, nullptr, 0, Value()) : Value();
|
||||
// If kn is not undefined, set kn to ! ToString(kn).
|
||||
if (!numeric.isUndefined()) {
|
||||
localeID.setKeywordValue("colnumeric", numeric.toBoolean() ? "yes" : "no");
|
||||
kn = numeric.toString(state);
|
||||
}
|
||||
// Set opt.[[kn]] to kn.
|
||||
|
||||
|
||||
Value numberingSystem = Intl::getOption(state, options, state.context()->staticStrings().lazyNumberingSystem().string(), Intl::StringValue, nullptr, 0, Value());
|
||||
// Let numberingSystem be ? GetOption(options, "numberingSystem", "string", undefined, undefined).
|
||||
Value numberingSystem = options ? Intl::getOption(state, options.value(), state.context()->staticStrings().numberingSystem.string(), Intl::StringValue, nullptr, 0, Value()) : Value();
|
||||
// If numberingSystem is not undefined, then
|
||||
if (!numberingSystem.isUndefined()) {
|
||||
std::string s = numberingSystem.asString()->toNonGCUTF8StringData();
|
||||
if (!Intl::isUnicodeLocaleIdentifierType(s) || !localeID.setKeywordValue("numbers", s)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "numberingSystem is not a well-formed numberingSystem value");
|
||||
// If numberingSystem does not match the type sequence (from UTS 35 Unicode Locale Identifier, section 3.2), throw a RangeError exception.
|
||||
// If numberingSystem does not match the [(3*8alphanum) *("-" (3*8alphanum))] sequence, throw a RangeError exception.
|
||||
std::string value = numberingSystem.asString()->toNonGCUTF8StringData();
|
||||
if (!uloc_toLegacyType(uloc_toLegacyKey("nu"), value.data()) || !checkOptionValueIsAlphaNum38DashAlphaNum38(value)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::RangeError, "`collation` option you input is not valid");
|
||||
}
|
||||
// Set opt.[[nu]] to numberingSystem.
|
||||
nu = numberingSystem.asString();
|
||||
}
|
||||
|
||||
// Let r be ! ApplyUnicodeExtensionToTag(tag, opt, relevantExtensionKeys).
|
||||
m_calendar = ca;
|
||||
m_caseFirst = kf;
|
||||
m_collation = co;
|
||||
m_hourCycle = hc;
|
||||
m_numberingSystem = nu;
|
||||
m_numeric = kn;
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> newUnicodeExtension;
|
||||
for (size_t i = 0; i < parsedUnicodeExtension.size(); i++) {
|
||||
if (parsedUnicodeExtension[i].first != "ca" && parsedUnicodeExtension[i].first != "co" && parsedUnicodeExtension[i].first != "hc" && parsedUnicodeExtension[i].first != "kf" && parsedUnicodeExtension[i].first != "kn" && parsedUnicodeExtension[i].first != "nu") {
|
||||
newUnicodeExtension.push_back(parsedUnicodeExtension[i]);
|
||||
}
|
||||
}
|
||||
|
||||
std::string cLocale = localeID.toCanonical();
|
||||
m_localeID = String::fromUTF8(cLocale.data(), cLocale.length());
|
||||
if (!m_localeID->length()) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "failed to initialize Locale");
|
||||
if (m_calendar) {
|
||||
newUnicodeExtension.push_back(std::make_pair(std::string("ca"), m_calendar->asString()->toNonGCUTF8StringData()));
|
||||
}
|
||||
}
|
||||
|
||||
String* IntlLocaleObject::locale() const
|
||||
{
|
||||
auto langTag = Intl::languageTagForLocaleID(m_localeID->toNonGCUTF8StringData().data());
|
||||
if (langTag) {
|
||||
return String::fromUTF8(langTag.value().data(), langTag.value().size());
|
||||
} else {
|
||||
return String::emptyString();
|
||||
if (m_caseFirst) {
|
||||
newUnicodeExtension.push_back(std::make_pair(std::string("kf"), m_caseFirst->asString()->equals("true") ? std::string() : m_caseFirst->asString()->toNonGCUTF8StringData()));
|
||||
}
|
||||
}
|
||||
|
||||
String* IntlLocaleObject::language() const
|
||||
{
|
||||
auto result = INTL_ICU_STD_STRING_BUFFER_OPERATION(uloc_getLanguage, m_localeID->toNonGCUTF8StringData().data());
|
||||
if (result.second == "") {
|
||||
result.second = "und";
|
||||
if (m_collation) {
|
||||
newUnicodeExtension.push_back(std::make_pair(std::string("co"), m_collation->asString()->toNonGCUTF8StringData()));
|
||||
}
|
||||
return String::fromUTF8(result.second.data(), result.second.length());
|
||||
}
|
||||
|
||||
String* IntlLocaleObject::region() const
|
||||
{
|
||||
auto result = INTL_ICU_STD_STRING_BUFFER_OPERATION(uloc_getCountry, m_localeID->toNonGCUTF8StringData().data());
|
||||
return String::fromUTF8(result.second.data(), result.second.length());
|
||||
}
|
||||
if (m_hourCycle) {
|
||||
newUnicodeExtension.push_back(std::make_pair(std::string("hc"), m_hourCycle->asString()->toNonGCUTF8StringData()));
|
||||
}
|
||||
|
||||
String* IntlLocaleObject::script() const
|
||||
{
|
||||
auto result = INTL_ICU_STD_STRING_BUFFER_OPERATION(uloc_getScript, m_localeID->toNonGCUTF8StringData().data());
|
||||
return String::fromUTF8(result.second.data(), result.second.length());
|
||||
}
|
||||
if (m_numberingSystem) {
|
||||
newUnicodeExtension.push_back(std::make_pair(std::string("nu"), m_numberingSystem->asString()->toNonGCUTF8StringData()));
|
||||
}
|
||||
|
||||
String* IntlLocaleObject::variants() const
|
||||
{
|
||||
std::string baseNameValue = baseName()->toNonGCUTF8StringData();
|
||||
std::vector<std::string> variantParts;
|
||||
if (baseNameValue.length()) {
|
||||
auto parts = split(baseNameValue, '-');
|
||||
for (size_t i = 1; i < parts.size(); i++) {
|
||||
const auto& part = parts[i];
|
||||
if (Intl::isUnicodeVariantSubtag(part)) {
|
||||
auto p = part;
|
||||
std::transform(p.begin(), p.end(), p.begin(),
|
||||
[](char c) { return std::tolower(c); });
|
||||
variantParts.push_back(p);
|
||||
if (m_numeric) {
|
||||
newUnicodeExtension.push_back(std::make_pair(std::string("kn"), m_numeric->asString()->equals("true") ? std::string() : m_numeric->asString()->toNonGCUTF8StringData()));
|
||||
}
|
||||
|
||||
std::sort(newUnicodeExtension.begin(), newUnicodeExtension.end(),
|
||||
[](const std::pair<std::string, std::string>& a, const std::pair<std::string, std::string>& b) -> bool {
|
||||
return a.first < b.first;
|
||||
});
|
||||
|
||||
StringBuilder localeBuilder;
|
||||
|
||||
localeBuilder.appendString(m_baseName);
|
||||
|
||||
size_t extensionIndex;
|
||||
for (extensionIndex = 0; extensionIndex < buildResult.extensions.size(); extensionIndex++) {
|
||||
if (buildResult.extensions[extensionIndex].key == 'u') {
|
||||
extensionIndex++;
|
||||
break;
|
||||
}
|
||||
|
||||
localeBuilder.appendChar('-');
|
||||
localeBuilder.appendChar(buildResult.extensions[extensionIndex].key);
|
||||
localeBuilder.appendChar('-');
|
||||
localeBuilder.appendString(buildResult.extensions[extensionIndex].value.data());
|
||||
}
|
||||
|
||||
if (newUnicodeExtension.size()) {
|
||||
localeBuilder.appendChar('-');
|
||||
localeBuilder.appendChar('u');
|
||||
|
||||
for (size_t i = 0; i < newUnicodeExtension.size(); i++) {
|
||||
if (newUnicodeExtension[i].first.length()) {
|
||||
localeBuilder.appendChar('-');
|
||||
localeBuilder.appendString(newUnicodeExtension[i].first.data());
|
||||
}
|
||||
|
||||
if (newUnicodeExtension[i].second.length()) {
|
||||
localeBuilder.appendChar('-');
|
||||
localeBuilder.appendString(newUnicodeExtension[i].second.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (variantParts.size() > 0) {
|
||||
std::string builder;
|
||||
bool first = true;
|
||||
for (const auto& variant : variantParts) {
|
||||
if (!first) {
|
||||
builder += "-";
|
||||
}
|
||||
builder.append(variant);
|
||||
first = false;
|
||||
}
|
||||
return String::fromUTF8(builder.data(), builder.length());
|
||||
} else {
|
||||
return String::emptyString();
|
||||
}
|
||||
}
|
||||
|
||||
String* IntlLocaleObject::baseName() const
|
||||
{
|
||||
std::string buffer;
|
||||
buffer.resize(32);
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
auto bufferLength = uloc_getBaseName(m_localeID->toNonGCUTF8StringData().data(), (char*)buffer.data(), buffer.size(), &status);
|
||||
// uloc_setKeywordValue does not set U_STRING_NOT_TERMINATED_WARNING.
|
||||
if (status == U_BUFFER_OVERFLOW_ERROR) {
|
||||
buffer.resize(bufferLength);
|
||||
status = U_ZERO_ERROR;
|
||||
uloc_getBaseName(m_localeID->toNonGCUTF8StringData().data(), (char*)buffer.data(), bufferLength, &status);
|
||||
} else if (U_SUCCESS(status)) {
|
||||
buffer.resize(bufferLength);
|
||||
for (; extensionIndex < buildResult.extensions.size(); extensionIndex++) {
|
||||
localeBuilder.appendChar('-');
|
||||
localeBuilder.appendChar(buildResult.extensions[extensionIndex].key);
|
||||
localeBuilder.appendChar('-');
|
||||
localeBuilder.appendString(buildResult.extensions[extensionIndex].value.data());
|
||||
}
|
||||
|
||||
auto langTag = Intl::languageTagForLocaleID(buffer.data());
|
||||
if (langTag) {
|
||||
return String::fromUTF8(langTag.value().data(), langTag.value().size());
|
||||
} else {
|
||||
return String::emptyString();
|
||||
if (buildResult.privateUse.length()) {
|
||||
localeBuilder.appendChar('-');
|
||||
localeBuilder.appendString(buildResult.privateUse.data());
|
||||
}
|
||||
}
|
||||
|
||||
static Optional<String*> keywordValue(String* localeID, const char* key, bool isBoolean = false)
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
std::string cLocaleID = localeID->toNonGCUTF8StringData();
|
||||
auto result = INTL_ICU_STD_STRING_BUFFER_OPERATION(uloc_getKeywordValue, cLocaleID.data(), key);
|
||||
ASSERT(U_SUCCESS(result.first));
|
||||
if (isBoolean) {
|
||||
return String::fromUTF8(result.second.data(), result.second.length());
|
||||
}
|
||||
const char* value = uloc_toUnicodeLocaleType(key, result.second.data());
|
||||
if (!value) {
|
||||
return nullptr;
|
||||
}
|
||||
auto r = String::fromUTF8(value, strlen(value));
|
||||
if (r->equals("true")) {
|
||||
return String::emptyString();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
Optional<String*> IntlLocaleObject::calendar() const
|
||||
{
|
||||
return keywordValue(m_localeID, "calendar");
|
||||
}
|
||||
|
||||
Optional<String*> IntlLocaleObject::caseFirst() const
|
||||
{
|
||||
return keywordValue(m_localeID, "colcasefirst");
|
||||
}
|
||||
|
||||
Optional<String*> IntlLocaleObject::collation() const
|
||||
{
|
||||
return keywordValue(m_localeID, "collation");
|
||||
}
|
||||
|
||||
Optional<String*> IntlLocaleObject::firstDayOfWeek() const
|
||||
{
|
||||
return keywordValue(m_localeID, "fw");
|
||||
}
|
||||
|
||||
Optional<String*> IntlLocaleObject::hourCycle() const
|
||||
{
|
||||
return keywordValue(m_localeID, "hours");
|
||||
}
|
||||
|
||||
Optional<String*> IntlLocaleObject::numeric() const
|
||||
{
|
||||
return keywordValue(m_localeID, "colnumeric", true);
|
||||
}
|
||||
|
||||
Optional<String*> IntlLocaleObject::numberingSystem() const
|
||||
{
|
||||
return keywordValue(m_localeID, "numbers");
|
||||
m_locale = localeBuilder.finalize();
|
||||
}
|
||||
|
||||
Value IntlLocaleObject::calendars(ExecutionState& state)
|
||||
{
|
||||
ValueVector resultVector;
|
||||
auto calendar = this->calendar();
|
||||
if (calendar) {
|
||||
resultVector.pushBack(calendar.value());
|
||||
if (m_calendar) {
|
||||
resultVector.pushBack(m_calendar.value());
|
||||
} else {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
LocalResourcePointer<UEnumeration> calendars(
|
||||
ucal_getKeywordValuesForLocale("calendar", locale()->toNonGCUTF8StringData().data(), true, &status),
|
||||
ucal_getKeywordValuesForLocale("calendar", m_locale->toNonGCUTF8StringData().data(), true, &status),
|
||||
[](UEnumeration* fmt) { uenum_close(fmt); });
|
||||
|
||||
if (!U_SUCCESS(status)) {
|
||||
|
|
@ -508,13 +437,12 @@ Value IntlLocaleObject::calendars(ExecutionState& state)
|
|||
Value IntlLocaleObject::collations(ExecutionState& state)
|
||||
{
|
||||
ValueVector resultVector;
|
||||
auto collation = this->collation();
|
||||
if (collation) {
|
||||
resultVector.pushBack(collation.value());
|
||||
if (m_collation) {
|
||||
resultVector.pushBack(m_collation.value());
|
||||
} else {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
LocalResourcePointer<UEnumeration> collations(
|
||||
ucol_getKeywordValuesForLocale("collation", locale()->toNonGCUTF8StringData().data(), true, &status),
|
||||
ucol_getKeywordValuesForLocale("collation", m_locale->toNonGCUTF8StringData().data(), true, &status),
|
||||
[](UEnumeration* f) { uenum_close(f); });
|
||||
|
||||
if (!U_SUCCESS(status)) {
|
||||
|
|
@ -550,12 +478,11 @@ Value IntlLocaleObject::collations(ExecutionState& state)
|
|||
Value IntlLocaleObject::hourCycles(ExecutionState& state)
|
||||
{
|
||||
ValueVector resultVector;
|
||||
auto hourCycle = this->hourCycle();
|
||||
if (hourCycle) {
|
||||
resultVector.pushBack(hourCycle.value());
|
||||
if (m_hourCycle) {
|
||||
resultVector.pushBack(m_hourCycle.value());
|
||||
} else {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
LocalResourcePointer<UDateTimePatternGenerator> generator(udatpg_open(locale()->toNonGCUTF8StringData().data(), &status),
|
||||
LocalResourcePointer<UDateTimePatternGenerator> generator(udatpg_open(m_locale->toNonGCUTF8StringData().data(), &status),
|
||||
[](UDateTimePatternGenerator* d) { udatpg_close(d); });
|
||||
if (!U_SUCCESS(status)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Invalid locale");
|
||||
|
|
@ -581,13 +508,12 @@ Value IntlLocaleObject::hourCycles(ExecutionState& state)
|
|||
Value IntlLocaleObject::numberingSystems(ExecutionState& state)
|
||||
{
|
||||
ValueVector resultVector;
|
||||
auto numberingSystem = this->numberingSystem();
|
||||
if (numberingSystem) {
|
||||
resultVector.pushBack(numberingSystem.value());
|
||||
if (m_numberingSystem) {
|
||||
resultVector.pushBack(m_numberingSystem.value());
|
||||
} else {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
LocalResourcePointer<UNumberingSystem> numberingSystem(
|
||||
unumsys_open(locale()->toNonGCUTF8StringData().data(), &status),
|
||||
unumsys_open(m_locale->toNonGCUTF8StringData().data(), &status),
|
||||
[](UNumberingSystem* p) { unumsys_close(p); });
|
||||
|
||||
if (!U_SUCCESS(status)) {
|
||||
|
|
@ -605,7 +531,7 @@ Value IntlLocaleObject::numberingSystems(ExecutionState& state)
|
|||
Value IntlLocaleObject::textInfo(ExecutionState& state)
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
ULayoutType layout = uloc_getCharacterOrientation(locale()->toNonGCUTF8StringData().data(), &status);
|
||||
ULayoutType layout = uloc_getCharacterOrientation(m_locale->toNonGCUTF8StringData().data(), &status);
|
||||
if (!U_SUCCESS(status)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Invalid locale");
|
||||
return Value();
|
||||
|
|
@ -631,7 +557,7 @@ Value IntlLocaleObject::textInfo(ExecutionState& state)
|
|||
Value IntlLocaleObject::weekInfo(ExecutionState& state)
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
LocalResourcePointer<UCalendar> calendar(ucal_open(nullptr, 0, locale()->toNonGCUTF8StringData().data(), UCAL_DEFAULT, &status),
|
||||
LocalResourcePointer<UCalendar> calendar(ucal_open(nullptr, 0, m_locale->toNonGCUTF8StringData().data(), UCAL_DEFAULT, &status),
|
||||
[](UCalendar* d) { ucal_close(d); });
|
||||
if (!U_SUCCESS(status)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Invalid locale");
|
||||
|
|
@ -662,6 +588,33 @@ Value IntlLocaleObject::weekInfo(ExecutionState& state)
|
|||
return Value();
|
||||
}
|
||||
|
||||
int32_t weekendStart = 0;
|
||||
int32_t weekendEnd = 0;
|
||||
for (int32_t day = UCAL_SUNDAY; day <= UCAL_SATURDAY; ++day) {
|
||||
UCalendarWeekdayType type = canonicalizeDayOfWeekType(ucal_getDayOfWeekType(calendar.get(), static_cast<UCalendarDaysOfWeek>(day), &status));
|
||||
if (!U_SUCCESS(status)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Invalid locale");
|
||||
return Value();
|
||||
}
|
||||
if (previous != type) {
|
||||
switch (type) {
|
||||
case UCAL_WEEKDAY: // WeekEnd => WeekDay
|
||||
if (day == UCAL_SUNDAY)
|
||||
weekendEnd = UCAL_SATURDAY;
|
||||
else
|
||||
weekendEnd = day - 1;
|
||||
break;
|
||||
case UCAL_WEEKEND: // WeekDay => WeekEnd
|
||||
weekendStart = day;
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
previous = type;
|
||||
}
|
||||
|
||||
auto convertUCalendarDaysOfWeekToMondayBasedDay = [](int32_t day) -> int32_t {
|
||||
// Convert from
|
||||
// Sunday => 1
|
||||
|
|
@ -674,42 +627,22 @@ Value IntlLocaleObject::weekInfo(ExecutionState& state)
|
|||
return day - 1;
|
||||
};
|
||||
|
||||
ArrayObject* weekendArray = new ArrayObject(state);
|
||||
size_t index = 0;
|
||||
for (int32_t day = 1; day <= 7; ++day) {
|
||||
UCalendarWeekdayType type = canonicalizeDayOfWeekType(ucal_getDayOfWeekType(calendar.get(), static_cast<UCalendarDaysOfWeek>(convertUCalendarDaysOfWeekToMondayBasedDay(day)), &status));
|
||||
if (!U_SUCCESS(status)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Invalid locale");
|
||||
return Value();
|
||||
}
|
||||
switch (type) {
|
||||
case UCAL_WEEKDAY:
|
||||
break;
|
||||
case UCAL_WEEKEND:
|
||||
weekendArray->setIndexedProperty(state, Value(index++), Value(day), weekendArray);
|
||||
break;
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Object* result = new Object(state);
|
||||
result->set(state, ObjectPropertyName(state, String::fromASCII("firstDay")), Value(convertUCalendarDaysOfWeekToMondayBasedDay(firstDayOfWeek)), result);
|
||||
result->set(state, ObjectPropertyName(state, String::fromASCII("weekend")), weekendArray, result);
|
||||
result->set(state, ObjectPropertyName(state, String::fromASCII("weekendStart")), Value(convertUCalendarDaysOfWeekToMondayBasedDay(weekendStart)), result);
|
||||
result->set(state, ObjectPropertyName(state, String::fromASCII("weekendEnd")), Value(convertUCalendarDaysOfWeekToMondayBasedDay(weekendEnd)), result);
|
||||
result->set(state, ObjectPropertyName(state, String::fromASCII("minimalDays")), Value(minimalDays), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
Value IntlLocaleObject::timeZones(ExecutionState& state)
|
||||
{
|
||||
auto region = this->region();
|
||||
if (!region->length()) {
|
||||
if (!m_region->length()) {
|
||||
return Value();
|
||||
}
|
||||
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
LocalResourcePointer<UEnumeration> enumeration(ucal_openTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, region->toNonGCUTF8StringData().data(), nullptr, &status),
|
||||
LocalResourcePointer<UEnumeration> enumeration(ucal_openTimeZoneIDEnumeration(UCAL_ZONE_TYPE_CANONICAL, m_region->toNonGCUTF8StringData().data(), nullptr, &status),
|
||||
[](UEnumeration* d) { uenum_close(d); });
|
||||
if (!U_SUCCESS(status)) {
|
||||
ErrorObject::throwBuiltinError(state, ErrorCode::TypeError, "Invalid locale");
|
||||
|
|
|
|||
|
|
@ -28,22 +28,63 @@ namespace Escargot {
|
|||
|
||||
class IntlLocaleObject : public DerivedObject {
|
||||
public:
|
||||
IntlLocaleObject(ExecutionState& state, String* tag, Object* options);
|
||||
IntlLocaleObject(ExecutionState& state, Object* proto, String* tag, Object* options);
|
||||
IntlLocaleObject(ExecutionState& state, String* tag, Optional<Object*> options);
|
||||
IntlLocaleObject(ExecutionState& state, Object* proto, String* tag, Optional<Object*> options);
|
||||
|
||||
String* locale() const;
|
||||
String* language() const;
|
||||
String* region() const;
|
||||
String* script() const;
|
||||
String* variants() const;
|
||||
String* baseName() const;
|
||||
Optional<String*> calendar() const;
|
||||
Optional<String*> caseFirst() const;
|
||||
Optional<String*> collation() const;
|
||||
Optional<String*> firstDayOfWeek() const;
|
||||
Optional<String*> hourCycle() const;
|
||||
Optional<String*> numeric() const;
|
||||
Optional<String*> numberingSystem() const;
|
||||
String* locale() const
|
||||
{
|
||||
return m_locale;
|
||||
}
|
||||
|
||||
String* language() const
|
||||
{
|
||||
return m_language;
|
||||
}
|
||||
|
||||
Value region() const
|
||||
{
|
||||
return m_region->length() ? Value(m_region) : Value();
|
||||
}
|
||||
|
||||
Value script() const
|
||||
{
|
||||
return m_script->length() ? Value(m_script) : Value();
|
||||
}
|
||||
|
||||
String* baseName() const
|
||||
{
|
||||
return m_baseName;
|
||||
}
|
||||
|
||||
Optional<String*> calendar() const
|
||||
{
|
||||
return m_calendar;
|
||||
}
|
||||
|
||||
Optional<String*> caseFirst() const
|
||||
{
|
||||
return m_caseFirst;
|
||||
}
|
||||
|
||||
Optional<String*> collation() const
|
||||
{
|
||||
return m_collation;
|
||||
}
|
||||
|
||||
Optional<String*> hourCycle() const
|
||||
{
|
||||
return m_hourCycle;
|
||||
}
|
||||
|
||||
Optional<String*> numeric() const
|
||||
{
|
||||
return m_numeric;
|
||||
}
|
||||
|
||||
Optional<String*> numberingSystem() const
|
||||
{
|
||||
return m_numberingSystem;
|
||||
}
|
||||
|
||||
virtual bool isIntlLocaleObject() const override
|
||||
{
|
||||
|
|
@ -59,7 +100,17 @@ public:
|
|||
Value timeZones(ExecutionState& state);
|
||||
|
||||
protected:
|
||||
String* m_localeID;
|
||||
String* m_language;
|
||||
String* m_script;
|
||||
String* m_region;
|
||||
String* m_baseName;
|
||||
String* m_locale;
|
||||
Optional<String*> m_calendar; // ca
|
||||
Optional<String*> m_caseFirst; // kf
|
||||
Optional<String*> m_collation; // co
|
||||
Optional<String*> m_hourCycle; // hc
|
||||
Optional<String*> m_numeric; // kn
|
||||
Optional<String*> m_numberingSystem; // nu
|
||||
};
|
||||
|
||||
} // namespace Escargot
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue