├── .github ├── splash.png └── workflows │ ├── android-release.yml │ ├── build-aar.yml │ ├── ci.yml │ └── publish-pod.yml ├── .gitignore ├── .gn ├── .habitat ├── Android ├── .gitignore ├── app │ ├── .gitignore │ ├── CMakeLists.txt │ ├── build.gradle │ ├── gradle.properties │ ├── proguard-rules.pro │ └── publish.gradle ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── BUILD.gn ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── DEPS ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── LICENSE.nodejs ├── LICENSE.v8 ├── NOTICE ├── PRIMJS_VERSION ├── PrimJS.podspec ├── Primjs.gni ├── README.md ├── SECURITY.md ├── config.gni ├── docs ├── benchmark.md ├── debugger.md ├── gc.md ├── jpg │ ├── flowchart.jpg │ ├── single-step1.jpg │ ├── single-step2.jpg │ ├── states.png │ └── template.png ├── template_interpreter.md └── unsupported_sepcifications.md ├── include ├── allocator.h ├── base_export.h ├── cutils.h ├── global-handles.h ├── libregexp.h ├── libunicode.h ├── list.h ├── persistent-handle.h ├── quickjs-libc.h ├── quickjs-tag.h ├── quickjs.h └── trace-gc.h ├── patches └── test262 │ └── 0001-Avoid-excessive-U-in-error-messages.patch ├── src ├── BUILD.gn ├── basic │ ├── BUILD.gn │ └── log │ │ ├── logging.cc │ │ ├── logging.h │ │ └── primjs_logging.cc ├── gc │ ├── BUILD.gn │ ├── allocator.cc │ ├── allocator.h │ ├── base-global-handles.h │ ├── collector.cc │ ├── collector.h │ ├── global-handles.cc │ ├── global-handles.h │ ├── persistent-handle.h │ ├── qjsvaluevalue-space.cc │ ├── qjsvaluevalue-space.h │ ├── sweeper.cc │ ├── sweeper.h │ ├── thread_pool.cc │ ├── thread_pool.h │ └── trace-gc.h ├── inspector │ ├── BUILD.gn │ ├── cpuprofiler │ │ ├── cpu_profiler.cc │ │ ├── cpu_profiler.h │ │ ├── profile_generator.cc │ │ ├── profile_generator.h │ │ ├── profile_tree.cc │ │ ├── profile_tree.h │ │ ├── profiler_sampling.cc │ │ ├── profiler_sampling.h │ │ ├── profiler_time.h │ │ ├── tracing_cpu_profiler.cc │ │ └── tracing_cpu_profiler.h │ ├── debugger │ │ ├── debugger.cc │ │ ├── debugger.h │ │ ├── debugger_breakpoint.cc │ │ ├── debugger_breakpoint.h │ │ ├── debugger_callframe.cc │ │ ├── debugger_callframe.h │ │ ├── debugger_properties.cc │ │ ├── debugger_properties.h │ │ ├── debugger_queue.cc │ │ └── debugger_queue.h │ ├── debugger_inner.h │ ├── debugger_struct.h │ ├── heapprofiler │ │ ├── edge.cc │ │ ├── edge.h │ │ ├── entry.cc │ │ ├── entry.h │ │ ├── gen.cc │ │ ├── gen.h │ │ ├── heapexplorer.cc │ │ ├── heapexplorer.h │ │ ├── heapprofiler.cc │ │ ├── heapprofiler.h │ │ ├── serialize.cc │ │ ├── serialize.h │ │ ├── snapshot.cc │ │ └── snapshot.h │ ├── interface.h │ ├── protocols.cc │ ├── protocols.h │ ├── runtime │ │ ├── runtime.cc │ │ └── runtime.h │ └── string_tools.cc ├── interpreter │ ├── BUILD.gn │ ├── primjs │ │ ├── BUILD.gn │ │ ├── android │ │ │ ├── embedded-inspector.S │ │ │ └── embedded.S │ │ ├── ios │ │ │ ├── embedded-inspector.S │ │ │ └── embedded.S │ │ ├── mac │ │ │ ├── embedded-inspector.S │ │ │ └── embedded.S │ │ └── snapshot_toolchain.gni │ └── quickjs │ │ ├── BUILD.gn │ │ ├── include │ │ ├── base_export.h │ │ ├── cutils.h │ │ ├── libbf.h │ │ ├── libregexp-opcode.h │ │ ├── libregexp.h │ │ ├── libunicode-table.h │ │ ├── libunicode.h │ │ ├── list.h │ │ ├── primjs_monitor.h │ │ ├── quickjs-atom.h │ │ ├── quickjs-inner.h │ │ ├── quickjs-libc.h │ │ ├── quickjs-opcode.h │ │ ├── quickjs-tag.h │ │ ├── quickjs.h │ │ ├── quickjs_queue.h │ │ └── quickjs_version.h │ │ └── source │ │ ├── cutils.cc │ │ ├── libbf.cc │ │ ├── libregexp.cc │ │ ├── libunicode.cc │ │ ├── primjs_monitor.cc │ │ ├── quickjs-libc.cc │ │ ├── quickjs.cc │ │ ├── quickjs_gc.cc │ │ ├── quickjs_queue.cc │ │ └── quickjs_version.cc └── napi │ ├── BUILD.gn │ ├── adapter │ ├── BUILD.gn │ ├── js_native_api_adapter.cc │ └── js_native_api_adapter.h │ ├── common │ ├── BUILD.gn │ ├── code_cache.cc │ ├── code_cache.h │ └── napi_state.h │ ├── env │ ├── BUILD.gn │ ├── napi_env.cc │ ├── napi_env.h │ ├── napi_runtime.cc │ └── napi_runtime.h │ ├── internal │ ├── primjs_napi_defines.h │ └── primjs_napi_undefs.h │ ├── js_native_api.h │ ├── js_native_api_types.h │ ├── jsc │ ├── BUILD.gn │ ├── js_native_api_JavaScriptCore.cc │ ├── js_native_api_JavaScriptCore.h │ └── napi_env_jsc.h │ ├── napi.cc │ ├── napi.h │ ├── napi_module.cc │ ├── napi_module.h │ ├── quickjs │ ├── BUILD.gn │ ├── js_native_api_QuickJS.cc │ ├── js_native_api_QuickJS.h │ └── napi_env_quickjs.h │ └── v8 │ ├── BUILD.gn │ ├── js_native_api_v8.cc │ ├── js_native_api_v8.cc.h │ ├── js_native_api_v8.h │ ├── js_native_api_v8_internals.h │ └── napi_env_v8.h ├── testing ├── BUILD.gn ├── napi │ ├── BUILD.gn │ ├── LICENSE.md │ ├── benchmark.cc │ ├── codecache_test.cc │ ├── codecache_unittests.cc │ ├── testlib.cc │ └── testlib.h ├── quickjs │ ├── BUILD.gn │ ├── compiler │ │ ├── BUILD.gn │ │ ├── common_test │ │ │ ├── structuredCloneTest.js │ │ │ └── wchar_rejection.js │ │ ├── finalization_registry_test │ │ │ └── wrong_param_fg_test.js │ │ ├── qjs.cc │ │ ├── qjs_debug_test │ │ │ └── qjs_debug_test1.js │ │ ├── structuredClone │ │ │ └── structuredClone.js │ │ ├── test-heap-profiler.cc │ │ ├── test_common.cc │ │ ├── test_debug_base.cc │ │ ├── test_debug_base.h │ │ ├── test_debug_common.cc │ │ ├── test_debug_complex_properties.cc │ │ ├── test_debug_parse_script_flag.cc │ │ ├── test_debug_pause.cc │ │ ├── test_debug_step.cc │ │ ├── test_finalization_registry.cc │ │ ├── test_parse_program.cc │ │ ├── test_primjs_version.cc │ │ ├── test_promise_rejection.cc │ │ ├── test_quickjs_cpu_profiler.cc │ │ ├── test_shared_context_debug.cc │ │ ├── test_weak_ref.cc │ │ ├── unit_test │ │ │ ├── async_closure_gc.js │ │ │ ├── async_stack_trace_test.js │ │ │ ├── debugger.js │ │ │ ├── demo_test.js │ │ │ ├── es6.js │ │ │ ├── finalization_registry_test.js │ │ │ ├── force_gc.js │ │ │ ├── json_parse.js │ │ │ ├── local_variables.js │ │ │ ├── object_get_set_property_test.js │ │ │ ├── prototype_test.js │ │ │ ├── rejection_reason_object.js │ │ │ ├── unhandled_register_test.js │ │ │ └── weak_ref_test.js │ │ └── weak_ref_test │ │ │ ├── wrong_param_weakref_test.js │ │ │ └── zero_param_weakref_test.js │ ├── run-test262.cc │ ├── run_test262.py │ ├── test-build.sh │ ├── test262.conf │ └── test262_errors.txt └── test.gni └── tools ├── ci ├── check_test_build.py ├── check_test_run.py └── run_quickjs_unittests.py ├── envsetup.sh └── hab /.github/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynx-family/primjs/e7301b9bbddbc39a981b091be1509ca1c76fa0a3/.github/splash.png -------------------------------------------------------------------------------- /.github/workflows/android-release.yml: -------------------------------------------------------------------------------- 1 | name: android sdk release 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | android-release: 13 | runs-on: lynx-ubuntu-22.04-medium 14 | 15 | steps: 16 | - name: Download Source 17 | uses: actions/checkout@v4.2.2 18 | 19 | - name: Set up JDK 11 20 | uses: actions/setup-java@v1 21 | with: 22 | java-version: 11 23 | 24 | - name: Get Tag Information 25 | run: |- 26 | version=$(echo ${{ github.ref }} | awk -F "/" '{print $3}') 27 | echo "VERSION=$version" >> $GITHUB_OUTPUT; 28 | id: get_tag 29 | 30 | - name: Cache Gradle packages 31 | uses: actions/cache@v4 32 | with: 33 | path: | 34 | ~/.gradle/caches 35 | ~/.gradle/wrapper 36 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} 37 | restore-keys: | 38 | ${{ runner.os }}-gradle- 39 | 40 | - name: Build artifact 41 | run: |- 42 | pushd Android 43 | chmod +x ./gradlew 44 | ./gradlew :app:assemblerelease 45 | ./gradlew :app:publish \ 46 | -Pversion=${{ steps.get_tag.outputs.VERSION }} \ 47 | -Psigning.keyId=${{ secrets.SIGNING_KEY_ID }} \ 48 | -Psigning.password=${{ secrets.SIGNING_PASSWORD }} \ 49 | -Psigning.secretKey=${{ secrets.SIGNING_SECRET_KEY }} 50 | ./gradlew :app:zipArtifacts -Pversion=${{ steps.get_tag.outputs.VERSION }} :app:getArtifactList 51 | popd 52 | pushd Android/app/build/ 53 | artifact_list=$(> $GITHUB_OUTPUT; 55 | popd 56 | id: build_artifact 57 | 58 | - name: Push to release 59 | uses: ncipollo/release-action@v1 60 | with: 61 | tag: ${{ steps.get_tag.outputs.VERSION }} 62 | token: ${{ secrets.GITHUB_TOKEN }} 63 | replacesArtifacts: true 64 | allowUpdates: true 65 | artifacts: "Android/app/build/outputs/aar/primjs-release.aar" 66 | 67 | - name: Publish artifact to maven 68 | uses: lynx-infra/maven-publish-action@2a55bab5273ffd84ebbb600e783286fc6251c239 69 | with: 70 | portal_api_token: ${{ secrets.PORTAL_API_TOKEN }} 71 | artifact_path_list: ${{ steps.build_artifact.outputs.artifact_list }} 72 | -------------------------------------------------------------------------------- /.github/workflows/build-aar.yml: -------------------------------------------------------------------------------- 1 | name: Build AAR 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | tag: 7 | description: 'release tag' 8 | required: true 9 | type: string 10 | 11 | jobs: 12 | build: 13 | runs-on: lynx-ubuntu-22.04-medium 14 | 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v2 18 | - name: Set up JDK 11 19 | uses: actions/setup-java@v1 20 | with: 21 | java-version: 11 22 | 23 | - name: Cache Gradle packages 24 | uses: actions/cache@v4 25 | with: 26 | path: | 27 | ~/.gradle/caches 28 | ~/.gradle/wrapper 29 | key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} 30 | restore-keys: | 31 | ${{ runner.os }}-gradle- 32 | 33 | - name: Grant execute permission for gradlew 34 | run: cd Android && chmod +x ./gradlew 35 | 36 | - name: Build the AAR 37 | run: cd Android && ./gradlew :app:assemblerelease 38 | 39 | - name: push to release 40 | uses: ncipollo/release-action@v1 41 | with: 42 | tag: ${{ github.event.inputs.tag }} 43 | token: ${{ secrets.GITHUB_TOKEN }} 44 | artifacts: "Android/app/build/outputs/aar/primjs-release.aar" -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | pull_request: 8 | branches: 9 | - develop 10 | 11 | jobs: 12 | coding-style: 13 | runs-on: lynx-ubuntu-22.04-medium 14 | steps: 15 | - uses: actions/checkout@v4 16 | - run: | 17 | echo "to be done" 18 | lint-pod: 19 | runs-on: lynx-darwin-14-medium 20 | steps: 21 | - name: Download Source 22 | uses: actions/checkout@v4.2.2 23 | - name: Install libyaml 24 | run: brew install libyaml 25 | - uses: ruby/setup-ruby@v1 26 | with: 27 | ruby-version: '2.6' 28 | - name: Bundle Install 29 | run: |- 30 | SDKROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk bundle install --path .bundle 31 | - name: Lint 32 | run: |- 33 | POD_VERSION=${{ github.event.pull_request.head.sha }} bundle exec pod spec lint PrimJS.podspec --verbose --skip-import-validation --allow-warnings 34 | 35 | check-unittests-linux: 36 | runs-on: lynx-ubuntu-22.04-medium 37 | steps: 38 | - name: Download Source 39 | uses: actions/checkout@v4.2.2 40 | - name: Set up Python 3.9 41 | uses: actions/setup-python@v5 42 | with: 43 | python-version: "3.9" 44 | - name: Install deps 45 | run: | 46 | source tools/envsetup.sh 47 | hab sync . -f 48 | - name: Build unittests 49 | run: | 50 | source tools/envsetup.sh 51 | python3 tools/ci/check_test_build.py 52 | - name: Run unittests 53 | run: 54 | python3 tools/ci/check_test_run.py 55 | 56 | check-unittests-darwin: 57 | runs-on: lynx-darwin-14-medium 58 | steps: 59 | - name: Download Source 60 | uses: actions/checkout@v4.2.2 61 | - name: Install deps 62 | run: | 63 | source tools/envsetup.sh 64 | hab sync . -f 65 | - name: Build unittests 66 | run: | 67 | source tools/envsetup.sh 68 | python3 tools/ci/check_test_build.py 69 | - name: Run unittests 70 | run: 71 | python3 tools/ci/check_test_run.py 72 | -------------------------------------------------------------------------------- /.github/workflows/publish-pod.yml: -------------------------------------------------------------------------------- 1 | name: Publish Pod 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | 8 | jobs: 9 | publish-pod: 10 | runs-on: macos-13 11 | steps: 12 | - name: Download Source 13 | uses: actions/checkout@v4.2.2 14 | - name: Bundle Install 15 | run: |- 16 | SDKROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk bundle install --path .bundle 17 | - name: Get Tag Information 18 | run: |- 19 | version=$(echo ${{ github.ref }} | awk -F "/" '{print $3}') 20 | echo "VERSION=$version" >> $GITHUB_OUTPUT; 21 | id: get_tag 22 | - name: Publish to CocoaPods Repo 23 | env: 24 | COCOAPODS_TRUNK_TOKEN: ${{ secrets.REPO_PRIMJS_COCOAPODS_TRUNK_TOKEN }} 25 | POD_VERSION: ${{ steps.get_tag.outputs.VERSION }} 26 | run: |- 27 | pod repo add-cdn trunk https://cdn.cocoapods.org/ 28 | COCOAPODS_TRUNK_TOKEN=$COCOAPODS_TRUNK_TOKEN POD_VERSION=$POD_VERSION pod trunk push PrimJS.podspec --skip-import-validation --allow-warnings 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out/ -------------------------------------------------------------------------------- /.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | buildconfig = "//build/config/BUILDCONFIG.gn" 5 | script_executable = "python3" 6 | 7 | secondary_source = "//build/secondary/" 8 | 9 | default_args = { 10 | use_xcode = true 11 | is_debug = false 12 | use_flutter_cxx = false 13 | enable_optimize_with_O2 = true 14 | } 15 | -------------------------------------------------------------------------------- /.habitat: -------------------------------------------------------------------------------- 1 | solutions = [{'name': '.', 'deps_file': 'DEPS', 'url': 'https://github.com/lynx-family/primjs'}] -------------------------------------------------------------------------------- /Android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /Android/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /Android/app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5.1) 2 | project("primjs aar") 3 | enable_language(C CXX ASM) 4 | set(CMAKE_ASM_COMPILER_TARGET "${CMAKE_CXX_COMPILER_TARGET}") 5 | add_subdirectory(${CMAKE_SOURCE_DIR}/../../ binary) 6 | -------------------------------------------------------------------------------- /Android/app/gradle.properties: -------------------------------------------------------------------------------- 1 | ARTIFACT_GROUP=org.lynxsdk.lynx 2 | ARTIFACT_NAME=primjs 3 | DESCRIPTION=Primjs 4 | REPOSITORY_URL=https://github.com/lynx-family/primjs 5 | REPOSITORY_SSH_URL=github.com/lynx-family/primjs.git 6 | DEVELOPER_NAME=lynx 7 | DEVELOPER_EMAIL=lynx.authors@gmail.com -------------------------------------------------------------------------------- /Android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /Android/build.gradle: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | // Project-level build.gradle 6 | 7 | buildscript { 8 | repositories { 9 | google() 10 | mavenCentral() 11 | } 12 | dependencies { 13 | classpath 'com.android.tools.build:gradle:7.0.2' 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | google() 22 | mavenCentral() 23 | } 24 | } -------------------------------------------------------------------------------- /Android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Enables namespacing of each library's R class so that its R class includes only the 19 | # resources declared in the library itself and none from the library's dependencies, 20 | # thereby reducing the size of the R class for that library 21 | android.nonTransitiveRClass=true 22 | 23 | enable_quickjs_debugger=true 24 | enable_codecache=true 25 | cache_profile=false 26 | enable_lite=false 27 | enable_mem=false 28 | force_gc=false 29 | enable_asan=false 30 | enable_primjs_snapshot=true 31 | 32 | disable_nanbox=false 33 | enable_compatible_mm=true 34 | enable_lepusng=true 35 | 36 | use_cpp_shared=false -------------------------------------------------------------------------------- /Android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynx-family/primjs/e7301b9bbddbc39a981b091be1509ca1c76fa0a3/Android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /Android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Nov 28 20:20:25 CST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /Android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /Android/settings.gradle: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | pluginManagement { 6 | repositories { 7 | google() 8 | mavenCentral() 9 | gradlePluginPortal() 10 | } 11 | } 12 | 13 | 14 | rootProject.name = "primjs" 15 | include ':app' 16 | -------------------------------------------------------------------------------- /BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | import("//Primjs.gni") 5 | import("//testing/test.gni") 6 | 7 | config("quickjs_public_config") { 8 | include_dirs = [ 9 | ".", 10 | "./src/", 11 | "./src/interpreter", 12 | ] 13 | 14 | defines = [ 15 | "PRIMJS_MIN_LOG_LEVEL=5", # disable alog in unittests 16 | "ENABLE_BUILTIN_SERIALIZE=1", 17 | "CONFIG_VERSION=\"2019-09-10\"", 18 | "LYNX_SIMPLIFY=1", 19 | ] 20 | 21 | if (enable_quickjs_debugger) { 22 | defines += [ "ENABLE_QUICKJS_DEBUGGER=1" ] 23 | include_dirs += [ 24 | "./src", 25 | "./src/inspector", 26 | ] 27 | } 28 | 29 | if (use_lepusng) { 30 | defines += [ "ENABLE_LEPUSNG" ] 31 | } 32 | 33 | if (use_bignum) { 34 | defines += [ "CONFIG_BIGNUM" ] 35 | } 36 | 37 | if (force_gc) { 38 | defines += [ "FORCE_GC_AT_MALLOC" ] 39 | } 40 | 41 | if (dump_bytecode) { 42 | defines += [ "DUMP_BYTECODE" ] 43 | } 44 | 45 | if (enable_mem) { 46 | defines += [ 47 | "DUMP_LEAKS", 48 | "DEBUG_MEMORY", 49 | "DUMP_LEAKS", 50 | ] 51 | } 52 | 53 | if (enable_primjs_snapshot) { 54 | defines += [ "ENABLE_PRIMJS_SNAPSHOT" ] 55 | } 56 | 57 | if (enable_compatible_mm) { 58 | defines += [ "ENABLE_COMPATIBLE_MM" ] 59 | } 60 | 61 | if (enable_force_gc) { 62 | defines += [ "ENABLE_FORCE_GC" ] 63 | } 64 | 65 | if (enable_tracing_gc_log) { 66 | defines += [ "ENABLE_TRACING_GC_LOG" ] 67 | } 68 | 69 | if (gen_android_embedded) { 70 | defines += [ "GEN_ANDROID_EMBEDDED" ] 71 | } 72 | 73 | if (enable_gc_debug_tools) { 74 | defines += [ "ENABLE_GC_DEBUG_TOOLS" ] 75 | } 76 | 77 | if (enable_primjs_trace) { 78 | defines += [ "ENABLE_PRIMJS_TRACE" ] 79 | } 80 | 81 | if (enable_unittests) { 82 | defines += [ 83 | "QJS_UNITTEST", 84 | "HEAPPROFILER_UNITTEST", 85 | ] 86 | } 87 | 88 | if (enable_tracing_gc) { 89 | defines += [ "ENABLE_TRACING_GC" ] 90 | } 91 | 92 | cflags_cc = [ 93 | "-Wno-unused-private-field", 94 | "-Wno-unused-variable", 95 | "-Wno-unused-local-typedef", 96 | "-Wno-sometimes-uninitialized", 97 | "-Wno-uninitialized", 98 | "-Wno-unused-function", 99 | "-Wno-format", 100 | "-Wno-unused-but-set-variable", 101 | "-Wno-unknown-warning-option", 102 | "-Wno-sign-compare", 103 | ] 104 | if (target_os != "android" || current_toolchain == snapshot_toolchain) { 105 | cflags_cc += [ 106 | "-Wno-c99-designator", 107 | "-Wno-reorder-init-list", 108 | ] 109 | } 110 | } 111 | 112 | config("napi_public_config") { 113 | include_dirs = [ 114 | ".", 115 | "./src", 116 | "./src/napi", 117 | "./src/napi/common", 118 | "./src/napi/env", 119 | "./src/napi/internal", 120 | "./src/napi/jsc/", 121 | "./src/napi/quickjs", 122 | "./src/napi/v8", 123 | "./src/interpreter", 124 | ] 125 | 126 | defines = [ 127 | "PRIMJS_MIN_LOG_LEVEL=5", # disable alog in unittests 128 | ] 129 | if (use_rtti) { 130 | defines += [ "NAPI_CPP_RTTI" ] 131 | } else { 132 | defines += [ "NAPI_DISABLE_CPP_RTTI" ] 133 | } 134 | } 135 | 136 | group("all") { 137 | deps = [ "src:src" ] 138 | 139 | if (enable_unittests) { 140 | testonly = true 141 | deps += [ "./testing" ] 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 5 | 6 | ## Our Standards 7 | Examples of behavior that contributes to creating a positive environment include: 8 | - Using welcoming and inclusive language 9 | - Being respectful of differing viewpoints and experiences 10 | - Gracefully accepting constructive criticism 11 | - Focusing on what is best for the community 12 | - Showing empathy towards other community members 13 | 14 | Examples of unacceptable behavior by participants include: 15 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 16 | - Trolling, insulting/derogatory comments, and personal or political attacks 17 | - Public or private harassment 18 | - Publishing others’ private information, such as a physical or electronic address, without explicit permission 19 | - Other conduct which could reasonably be considered inappropriate in a professional setting 20 | 21 | ## Our Responsibilities 22 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 23 | 24 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 25 | 26 | ## Scope 27 | This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 28 | 29 | ## Enforcement 30 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at opensource-conduct@tiktok.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 31 | 32 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project’s leadership. 33 | 34 | ## Attribution 35 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 36 | 37 | For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq 38 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'cocoapods', '~> 1.16', '>= 1.16.2' -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.7) 5 | base64 6 | nkf 7 | rexml 8 | activesupport (6.1.7.10) 9 | concurrent-ruby (~> 1.0, >= 1.0.2) 10 | i18n (>= 1.6, < 2) 11 | minitest (>= 5.1) 12 | tzinfo (~> 2.0) 13 | zeitwerk (~> 2.3) 14 | addressable (2.8.7) 15 | public_suffix (>= 2.0.2, < 7.0) 16 | algoliasearch (1.27.5) 17 | httpclient (~> 2.8, >= 2.8.3) 18 | json (>= 1.5.1) 19 | atomos (0.1.3) 20 | base64 (0.2.0) 21 | claide (1.1.0) 22 | cocoapods (1.16.2) 23 | addressable (~> 2.8) 24 | claide (>= 1.0.2, < 2.0) 25 | cocoapods-core (= 1.16.2) 26 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 27 | cocoapods-downloader (>= 2.1, < 3.0) 28 | cocoapods-plugins (>= 1.0.0, < 2.0) 29 | cocoapods-search (>= 1.0.0, < 2.0) 30 | cocoapods-trunk (>= 1.6.0, < 2.0) 31 | cocoapods-try (>= 1.1.0, < 2.0) 32 | colored2 (~> 3.1) 33 | escape (~> 0.0.4) 34 | fourflusher (>= 2.3.0, < 3.0) 35 | gh_inspector (~> 1.0) 36 | molinillo (~> 0.8.0) 37 | nap (~> 1.0) 38 | ruby-macho (>= 2.3.0, < 3.0) 39 | xcodeproj (>= 1.27.0, < 2.0) 40 | cocoapods-core (1.16.2) 41 | activesupport (>= 5.0, < 8) 42 | addressable (~> 2.8) 43 | algoliasearch (~> 1.0) 44 | concurrent-ruby (~> 1.1) 45 | fuzzy_match (~> 2.0.4) 46 | nap (~> 1.0) 47 | netrc (~> 0.11) 48 | public_suffix (~> 4.0) 49 | typhoeus (~> 1.0) 50 | cocoapods-deintegrate (1.0.5) 51 | cocoapods-downloader (2.1) 52 | cocoapods-plugins (1.0.0) 53 | nap 54 | cocoapods-search (1.0.1) 55 | cocoapods-trunk (1.6.0) 56 | nap (>= 0.8, < 2.0) 57 | netrc (~> 0.11) 58 | cocoapods-try (1.2.0) 59 | colored2 (3.1.2) 60 | concurrent-ruby (1.3.4) 61 | escape (0.0.4) 62 | ethon (0.16.0) 63 | ffi (>= 1.15.0) 64 | ffi (1.17.0-x86_64-darwin) 65 | fourflusher (2.3.1) 66 | fuzzy_match (2.0.4) 67 | gh_inspector (1.1.3) 68 | httpclient (2.8.3) 69 | i18n (1.14.6) 70 | concurrent-ruby (~> 1.0) 71 | json (2.7.6) 72 | minitest (5.25.2) 73 | molinillo (0.8.0) 74 | nanaimo (0.4.0) 75 | nap (1.1.0) 76 | netrc (0.11.0) 77 | nkf (0.2.0) 78 | public_suffix (4.0.7) 79 | rexml (3.3.9) 80 | ruby-macho (2.5.1) 81 | typhoeus (1.4.1) 82 | ethon (>= 0.9.0) 83 | tzinfo (2.0.6) 84 | concurrent-ruby (~> 1.0) 85 | xcodeproj (1.27.0) 86 | CFPropertyList (>= 2.3.3, < 4.0) 87 | atomos (~> 0.1.3) 88 | claide (>= 1.0.2, < 2.0) 89 | colored2 (~> 3.1) 90 | nanaimo (~> 0.4.0) 91 | rexml (>= 3.3.6, < 4.0) 92 | zeitwerk (2.6.18) 93 | 94 | PLATFORMS 95 | x86_64-darwin-17 96 | 97 | DEPENDENCIES 98 | cocoapods (~> 1.16, >= 1.16.2) 99 | 100 | BUNDLED WITH 101 | 2.4.20 102 | -------------------------------------------------------------------------------- /LICENSE.nodejs: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 [Node.js API collaborators](https://github.com/nodejs/node-addon-api#collaborators) 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. -------------------------------------------------------------------------------- /LICENSE.v8: -------------------------------------------------------------------------------- 1 | Copyright 2006-2011, the V8 project authors. All rights reserved. 2 | Redistribution and use in source and binary forms, with or without 3 | modification, are permitted provided that the following conditions are 4 | met: 5 | 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above 9 | copyright notice, this list of conditions and the following 10 | disclaimer in the documentation and/or other materials provided 11 | with the distribution. 12 | * Neither the name of Google Inc. nor the names of its 13 | contributors may be used to endorse or promote products derived 14 | from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | PrimJS JavaScript Engine 2 | Copyright (c) 2018-2024 ByteDance Inc. 3 | Copyright (c) 2024 TikTok Inc. 4 | All rights reserved. 5 | 6 | -------------------------------------------------------------------------------- /PRIMJS_VERSION: -------------------------------------------------------------------------------- 1 | 2.11.1-rc.1 -------------------------------------------------------------------------------- /Primjs.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | 5 | import("//config.gni") 6 | 7 | template("primjs_source_set") { 8 | source_set(target_name) { 9 | forward_variables_from(invoker, 10 | "*", 11 | [ 12 | "configs", 13 | "public_configs", 14 | ]) 15 | 16 | if (!defined(deps)) { 17 | deps = [] 18 | } 19 | 20 | if (!defined(configs)) { 21 | configs = [] 22 | } 23 | if (defined(invoker.configs)) { 24 | configs += invoker.configs 25 | } 26 | 27 | if (!defined(public_configs)) { 28 | public_configs = [] 29 | } 30 | if (defined(invoker.public_configs)) { 31 | public_configs += invoker.public_configs 32 | } 33 | public_configs += [ "//:quickjs_public_config" ] 34 | 35 | if (defined(exclude_configs)) { 36 | configs -= exclude_configs 37 | } 38 | 39 | if (defined(exclude_deps)) { 40 | deps -= exclude_deps 41 | } 42 | if (use_rtti) { 43 | configs -= [ "//build/config/compiler:no_rtti" ] 44 | } 45 | } 46 | } 47 | 48 | template("napi_source_set") { 49 | source_set(target_name) { 50 | forward_variables_from(invoker, 51 | "*", 52 | [ 53 | "configs", 54 | "public_configs", 55 | ]) 56 | 57 | if (!defined(deps)) { 58 | deps = [] 59 | } 60 | 61 | if (!defined(configs)) { 62 | configs = [] 63 | } 64 | if (defined(invoker.configs)) { 65 | configs += invoker.configs 66 | } 67 | 68 | if (!defined(public_configs)) { 69 | public_configs = [] 70 | } 71 | if (defined(invoker.public_configs)) { 72 | public_configs += invoker.public_configs 73 | } 74 | public_configs += [ "//:napi_public_config" ] 75 | 76 | if (defined(exclude_configs)) { 77 | configs -= exclude_configs 78 | } 79 | 80 | if (defined(exclude_deps)) { 81 | deps -= exclude_deps 82 | } 83 | if (use_rtti) { 84 | configs -= [ "//build/config/compiler:no_rtti" ] 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting a Vulnerability 2 | 3 | If you happen to discover a potential security issue in this project, please do **not** create a public GitHub issue. 4 | 5 | We kindly ask you to notify TikTok Security via email at [security@tiktok.com]. 6 | 7 | Valid vulnerability reports can be eligible for monetary rewards under our Bug Bounty program. 8 | For eligible reports, we will provide additional instructions on how to claim the reward over email. 9 | 10 | We greatly appreciate community contributions to the security of this project. 11 | -------------------------------------------------------------------------------- /config.gni: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | 5 | if (is_android) { 6 | import("//build/config/android/config.gni") 7 | } 8 | 9 | if (target_cpu == "arm" || target_cpu == "arm64") { 10 | import("//build/config/arm.gni") 11 | } 12 | 13 | # global configs 14 | declare_args() { 15 | # Compile for test cases. 16 | # Lynx 17 | enable_unittests = false 18 | 19 | # Whether to enable unit tests 20 | 21 | # Compile lepusng 22 | use_lepusng = true 23 | just_lepusng = true 24 | 25 | # Open support of bignum 26 | # Conflict with lepusng! 27 | use_bignum = false 28 | 29 | # Force qjs run gc at malloc 30 | force_gc = false 31 | 32 | # dump memory leaks 33 | enable_mem = false 34 | 35 | # dump bytecode 36 | dump_bytecode = false 37 | 38 | enable_quickjs_debugger = false 39 | 40 | # Enable primjs snapshot version (with pre-generated embedded.S) 41 | enable_primjs_snapshot = false 42 | 43 | # Enable primjs compatible memory management 44 | enable_compatible_mm = false 45 | 46 | # Enable tracing gc debug tools 47 | enable_gc_debug_tools = false 48 | 49 | # Enable force tracing gc 50 | enable_force_gc = false 51 | 52 | # Enable tracing gc log 53 | enable_tracing_gc_log = false 54 | 55 | gen_android_embedded = false 56 | 57 | use_rtti = false 58 | 59 | # Enable primjs trace 60 | enable_primjs_trace = false 61 | 62 | enable_tracing_gc = false 63 | napi_src_dir = "//src/napi/" 64 | } 65 | 66 | if (target_cpu != "arm64") { 67 | enable_primjs_snapshot = false 68 | enable_compatible_mm = false 69 | } 70 | 71 | if (!enable_primjs_snapshot) { 72 | enable_compatible_mm = false 73 | enable_tracing_gc = false 74 | enable_primjs_trace = false 75 | enable_gc_debug_tools = false 76 | enable_gc_debug_tools = false 77 | } 78 | -------------------------------------------------------------------------------- /docs/benchmark.md: -------------------------------------------------------------------------------- 1 | # Results of performance comparison between Primjs and QuickJS 2 | 3 | This document documents the performance of PrimJS and QuickJS on [Octane Benchmark](https://chromium.github.io/octane/). 4 | 5 | ## Testing environment 6 | - CPU: Apple M1 Max 7 | - Memory: 64GB 8 | - OS: Sonoma 14.0 9 | 10 | ## Testing build args 11 | ``` 12 | gn gen out/Default --args=' 13 | enable_quickjs_debugger=false 14 | enable_tracing_gc = true 15 | enable_compatbile_mm = true 16 | enable_primjs_snapshot = true 17 | target_cpu = "arm64" 18 | target_os = "mac" 19 | is_debug=false 20 | enable_optimize_with_O2=true 21 | ' 22 | ``` 23 | 24 | ## Testing result 25 | 26 | | BenchMark | QuickJS
(6e2e68) | PrimJS | 27 | |------------------|-----------------------|----------------------| 28 | | Richards | 1163 | 1247 | 29 | | DeltaBlue | 1093 | 1353 | 30 | | Crypto | 1349 | 1844 | 31 | | RayTrace | 1273 | 2751 | 32 | | NavierStokes | 2640 | 4166 | 33 | | Mandreel | 1350 | 1372 | 34 | | MandreelLatency | 9680 | 9587 | 35 | | Gameboy | 9265 | 10463 | 36 | | CodeLoad | 18137 | 16992 | 37 | | Box2D | 4544 | 5670 | 38 | | zlib | 3097 | 3864 | 39 | | Typescript | 18158 | 22855 | 40 | | EarleyBoyer | 2284 | 4270 | 41 | | RegExp | 282 | 324 | 42 | | PdfJS | 4236 | 6642 | 43 | | **Score (version 9)** | **2904** | **3735** | -------------------------------------------------------------------------------- /docs/jpg/flowchart.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynx-family/primjs/e7301b9bbddbc39a981b091be1509ca1c76fa0a3/docs/jpg/flowchart.jpg -------------------------------------------------------------------------------- /docs/jpg/single-step1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynx-family/primjs/e7301b9bbddbc39a981b091be1509ca1c76fa0a3/docs/jpg/single-step1.jpg -------------------------------------------------------------------------------- /docs/jpg/single-step2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynx-family/primjs/e7301b9bbddbc39a981b091be1509ca1c76fa0a3/docs/jpg/single-step2.jpg -------------------------------------------------------------------------------- /docs/jpg/states.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynx-family/primjs/e7301b9bbddbc39a981b091be1509ca1c76fa0a3/docs/jpg/states.png -------------------------------------------------------------------------------- /docs/jpg/template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lynx-family/primjs/e7301b9bbddbc39a981b091be1509ca1c76fa0a3/docs/jpg/template.png -------------------------------------------------------------------------------- /include/allocator.h: -------------------------------------------------------------------------------- 1 | ../src/gc/allocator.h -------------------------------------------------------------------------------- /include/base_export.h: -------------------------------------------------------------------------------- 1 | ../src/interpreter/quickjs/include/base_export.h -------------------------------------------------------------------------------- /include/cutils.h: -------------------------------------------------------------------------------- 1 | ../src/interpreter/quickjs/include/cutils.h -------------------------------------------------------------------------------- /include/global-handles.h: -------------------------------------------------------------------------------- 1 | ../src/gc/global-handles.h -------------------------------------------------------------------------------- /include/libregexp.h: -------------------------------------------------------------------------------- 1 | ../src/interpreter/quickjs/include/libregexp.h -------------------------------------------------------------------------------- /include/libunicode.h: -------------------------------------------------------------------------------- 1 | ../src/interpreter/quickjs/include/libunicode.h -------------------------------------------------------------------------------- /include/list.h: -------------------------------------------------------------------------------- 1 | ../src/interpreter/quickjs/include/list.h -------------------------------------------------------------------------------- /include/persistent-handle.h: -------------------------------------------------------------------------------- 1 | ../src/gc/persistent-handle.h -------------------------------------------------------------------------------- /include/quickjs-libc.h: -------------------------------------------------------------------------------- 1 | ../src/interpreter/quickjs/include/quickjs-libc.h -------------------------------------------------------------------------------- /include/quickjs-tag.h: -------------------------------------------------------------------------------- 1 | ../src/interpreter/quickjs/include/quickjs-tag.h -------------------------------------------------------------------------------- /include/quickjs.h: -------------------------------------------------------------------------------- 1 | ../src/interpreter/quickjs/include/quickjs.h -------------------------------------------------------------------------------- /include/trace-gc.h: -------------------------------------------------------------------------------- 1 | ../src/gc/trace-gc.h -------------------------------------------------------------------------------- /patches/test262/0001-Avoid-excessive-U-in-error-messages.patch: -------------------------------------------------------------------------------- 1 | From 36be6b75a3b9623b8a23102b312eac9f526aca5d Mon Sep 17 00:00:00 2001 2 | From: zhangyuping 3 | Date: Wed, 18 Dec 2024 16:15:31 +0800 4 | Subject: [PATCH] Avoid excessive U\+ in error messages 5 | 6 | --- 7 | harness/regExpUtils.js | 2 +- 8 | 1 file changed, 1 insertion(+), 1 deletion(-) 9 | 10 | diff --git a/harness/regExpUtils.js b/harness/regExpUtils.js 11 | index 9b4c58ae1d..194f610aa7 100644 12 | --- a/harness/regExpUtils.js 13 | +++ b/harness/regExpUtils.js 14 | @@ -54,7 +54,7 @@ function testPropertyEscapes(regExp, string, expression) { 15 | const hex = printCodePoint(symbol.codePointAt(0)); 16 | assert( 17 | regExp.test(symbol), 18 | - `\`${ expression }\` should match U+${ hex } (\`${ symbol }\`)` 19 | + `\`${ expression }\` should match ${ hex } (\`${ symbol }\`)` 20 | ); 21 | } 22 | } 23 | -- 24 | 2.45.2 25 | 26 | -------------------------------------------------------------------------------- /src/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | 5 | import("//Primjs.gni") 6 | 7 | static_library("quickjs_lib") { 8 | public_deps = [ 9 | "basic", 10 | "gc", 11 | "interpreter", 12 | ] 13 | if (enable_quickjs_debugger) { 14 | public_deps += [ "inspector" ] 15 | } 16 | 17 | complete_static_lib = true 18 | output_dir = "$root_out_dir" 19 | output_name = "quickjs" 20 | } 21 | 22 | static_library("napi_lib") { 23 | output_name = "napi" 24 | output_dir = "$root_out_dir" 25 | deps = [ 26 | ":quickjs_lib", 27 | "napi:napi", 28 | "napi:napi_env", 29 | "napi:napi_quickjs", 30 | "napi:napi_runtime", 31 | ] 32 | 33 | if (is_ios || is_mac) { 34 | deps += [ "napi:napi_jsc" ] 35 | } 36 | complete_static_lib = true 37 | } 38 | 39 | executable("qjs_exe") { 40 | testonly = true 41 | sources = [ "../testing/quickjs/compiler/qjs.cc" ] 42 | public_deps = [ 43 | "gc", 44 | "interpreter", 45 | ] 46 | if (enable_quickjs_debugger) { 47 | public_deps += [ "inspector" ] 48 | } 49 | output_name = "qjs" 50 | } 51 | 52 | group("src") { 53 | deps = [ 54 | ":napi_lib", 55 | ":quickjs_lib", 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /src/basic/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | 5 | import("//Primjs.gni") 6 | 7 | primjs_source_set("basic") { 8 | sources = [ 9 | "log/logging.cc", 10 | "log/primjs_logging.cc", 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /src/basic/log/logging.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | #include "basic/log/logging.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace primjs { 15 | namespace general { 16 | namespace logging { 17 | namespace { 18 | 19 | const char *const log_severity_names[LOG_NUM_SEVERITIES] = {"INFO", "WARNING", 20 | "ERROR", "FATAL"}; 21 | 22 | const char *log_severity_name(int severity) { 23 | if (severity >= 0 && severity < LOG_NUM_SEVERITIES) 24 | return log_severity_names[severity]; 25 | return "UNKNOWN"; 26 | } 27 | 28 | int g_min_log_level = 0; 29 | } // namespace 30 | 31 | void SetMinLogLevel(int level) { g_min_log_level = std::min(LOG_FATAL, level); } 32 | 33 | int GetMinAllLogLevel() { return std::min(g_min_log_level, LOG_INFO); } 34 | 35 | LogMessage::LogMessage(const char *file, int line, LogSeverity severity) 36 | : severity_(severity), file_(file), line_(line) { 37 | Init(file_, line_); 38 | } 39 | 40 | std::ostringstream &LogMessage::stream() { return stream_; } 41 | 42 | LogMessage::~LogMessage() { 43 | // on Windows, use spdlog which add newline at the end of each line. 44 | #if !defined(_WIN32) 45 | stream_ << std::endl; 46 | primjs::general::logging::Log(this); 47 | #else 48 | std::string str_newline(stream_.str()); 49 | printf("primjs: %s\n", str_newline.c_str()); 50 | #endif 51 | 52 | if (severity_ == LOG_FATAL) { 53 | abort(); 54 | } 55 | } 56 | 57 | // writes the common header info to the stream 58 | void LogMessage::Init(const char *file, int line) { 59 | std::string filename(file); 60 | size_t last_slash_pos = filename.find_last_of("\\/"); 61 | if (last_slash_pos != std::string::npos) { 62 | size_t size = last_slash_pos + 1; 63 | filename = filename.substr(size, filename.length() - size); 64 | } 65 | 66 | stream_ << '['; 67 | time_t t = time(NULL); 68 | struct tm local_time = {}; 69 | 70 | #if !defined(_WIN32) 71 | localtime_r(&t, &local_time); 72 | #else 73 | localtime_s(&local_time, &t); 74 | #endif 75 | 76 | struct tm *tm_time = &local_time; 77 | stream_ << std::setfill('0') << std::setw(2) << 1 + tm_time->tm_mon 78 | << std::setw(2) << tm_time->tm_mday << '/' << std::setw(2) 79 | << tm_time->tm_hour << std::setw(2) << tm_time->tm_min << std::setw(2) 80 | << tm_time->tm_sec << ':'; 81 | 82 | if (severity_ >= 0) 83 | stream_ << log_severity_name(severity_); 84 | else 85 | stream_ << "VERBOSE" << -severity_; 86 | 87 | stream_ << ":" << filename << "(" << line << ")] "; 88 | } 89 | 90 | } // namespace logging 91 | } // namespace general 92 | } // namespace primjs 93 | -------------------------------------------------------------------------------- /src/basic/log/primjs_logging.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | #include 6 | 7 | #include "basic/log/logging.h" 8 | 9 | namespace primjs { 10 | namespace general { 11 | namespace logging { 12 | void Log(LogMessage *msg) { 13 | std::cout << msg->stream().str() << std::endl; 14 | return; 15 | } 16 | } // namespace logging 17 | } // namespace general 18 | 19 | } // namespace primjs 20 | -------------------------------------------------------------------------------- /src/gc/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | 5 | import("//Primjs.gni") 6 | primjs_source_set("gc") { 7 | sources = [ 8 | "allocator.cc", 9 | "collector.cc", 10 | "global-handles.cc", 11 | "qjsvaluevalue-space.cc", 12 | "sweeper.cc", 13 | "thread_pool.cc", 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/gc/global-handles.h: -------------------------------------------------------------------------------- 1 | // Copyright 2009 the V8 project authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Copyright 2024 The Lynx Authors. All rights reserved. 6 | // Licensed under the Apache License Version 2.0 that can be found in the 7 | // LICENSE file in the root directory of this source tree. 8 | 9 | #ifndef SRC_GC_GLOBAL_HANDLES_H_ 10 | #define SRC_GC_GLOBAL_HANDLES_H_ 11 | 12 | // #include 13 | 14 | #include "gc/persistent-handle.h" 15 | 16 | typedef uintptr_t Addr; 17 | 18 | // Global handles hold handles that are independent of stack-state and can have 19 | // callbacks and finalizers attached to them. 20 | class GlobalHandles final { 21 | public: 22 | static void EnableMarkingBarrier(LEPUSRuntime* runtime); 23 | static void DisableMarkingBarrier(LEPUSRuntime* runtime); 24 | 25 | GlobalHandles(const GlobalHandles&) = delete; 26 | GlobalHandles& operator=(const GlobalHandles&) = delete; 27 | 28 | class NodeBlock; 29 | 30 | static void Destroy(LEPUSValue* location); 31 | 32 | explicit GlobalHandles(LEPUSRuntime* runtime); 33 | ~GlobalHandles(); 34 | 35 | // Creates a new global handle that is alive until Destroy is called. 36 | LEPUSValue* Create(LEPUSValue value, bool is_weak); 37 | 38 | void IterateAllRoots(int local_idx, int offset); 39 | void GlobalRootsFinalizer(); 40 | bool IsMarkedLEPUSValue(LEPUSValue* val); 41 | LEPUSRuntime* runtime() const { return runtime_; } 42 | 43 | size_t TotalSize() const; 44 | size_t UsedSize() const; 45 | // Number of global handles. 46 | size_t handles_count() const; 47 | void SetWeak(LEPUSValue* location, void* data, void (*cb)(void*)); 48 | void ClearWeak(LEPUSValue* location); 49 | void SetWeakState(LEPUSValue* location); 50 | 51 | private: 52 | // Internal node structures. 53 | class NodeIterator; 54 | class NodeSpace; 55 | 56 | LEPUSRuntime* const runtime_; 57 | bool is_marking_ = false; 58 | 59 | NodeSpace* regular_nodes_; 60 | }; 61 | 62 | #endif // SRC_GC_GLOBAL_HANDLES_H_ 63 | -------------------------------------------------------------------------------- /src/gc/qjsvaluevalue-space.h: -------------------------------------------------------------------------------- 1 | // Copyright 2009 the V8 project authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Copyright 2024 The Lynx Authors. All rights reserved. 6 | // Licensed under the Apache License Version 2.0 that can be found in the 7 | // LICENSE file in the root directory of this source tree. 8 | 9 | #ifndef SRC_GC_QJSVALUEVALUE_SPACE_H_ 10 | #define SRC_GC_QJSVALUEVALUE_SPACE_H_ 11 | 12 | #include "gc/persistent-handle.h" 13 | class QJSValueValueSpace final { 14 | public: 15 | QJSValueValueSpace(const QJSValueValueSpace&) = delete; 16 | QJSValueValueSpace& operator=(const QJSValueValueSpace&) = delete; 17 | 18 | class NodeBlock; 19 | class NodeIterator; 20 | class NodeSpace; 21 | 22 | static void Destroy(void* location); 23 | explicit QJSValueValueSpace(LEPUSRuntime* runtime); 24 | ~QJSValueValueSpace(); 25 | void* Create(); 26 | void IterateAllRoots(int local_idx); 27 | LEPUSRuntime* runtime() const { return runtime_; } 28 | 29 | private: 30 | LEPUSRuntime* runtime_; 31 | NodeSpace* regular_nodes_; 32 | }; 33 | 34 | #endif // SRC_GC_QJSVALUEVALUE_SPACE_H_ 35 | -------------------------------------------------------------------------------- /src/gc/sweeper.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | #ifndef SRC_GC_SWEEPER_H_ 6 | #define SRC_GC_SWEEPER_H_ 7 | #include 8 | 9 | #include "quickjs/include/quickjs-inner.h" 10 | 11 | class Sweeper { 12 | public: 13 | Sweeper(mstate state) : m(state) {} 14 | void sweep_finalizer(); 15 | void sweep_free(); 16 | void traverse_finalizer(bool is_only, int64_t begin_time); 17 | void traverse_chunk_for_finalizer(bool is_only = false); 18 | void free_mmap_objects(); 19 | void traverse_chunk_for_free(); 20 | void reinit_freelist(); 21 | int calculate_task_granularity(); 22 | static void generate_freelist(mstate m, msegmentptr sp_begin, 23 | msegmentptr sp_end); 24 | 25 | private: 26 | mstate m; 27 | }; 28 | 29 | void do_finalizer(void* runtime, void* ptr, bool is_only); 30 | 31 | void do_global_finalizer(void* rt); 32 | 33 | void merge_dead_objs(mstate m, msegmentptr sp_begin, msegmentptr sp_end); 34 | 35 | class AcquireIdxScope { 36 | private: 37 | int local_idx; 38 | mstate m_; 39 | 40 | public: 41 | AcquireIdxScope(mstate m) { 42 | local_idx = atomic_acqurie_local_idx(m); 43 | m_ = m; 44 | while (local_idx == -1) { 45 | #ifndef _WIN32 46 | sched_yield(); 47 | #endif 48 | local_idx = atomic_acqurie_local_idx(m); 49 | } 50 | } 51 | ~AcquireIdxScope() { atomic_release_local_idx(m_, local_idx); } 52 | operator int() { return local_idx; } 53 | }; 54 | void parallel_traverse_heap_segment( 55 | mstate m, size_t segs_in_thread, ByteThreadPool* workerThreadPool, 56 | std::function func); 57 | 58 | #endif // SRC_GC_SWEEPER_H_ 59 | -------------------------------------------------------------------------------- /src/gc/trace-gc.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | #ifndef SRC_GC_TRACE_GC_H_ 6 | #define SRC_GC_TRACE_GC_H_ 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | #include "quickjs/include/quickjs.h" 11 | #ifdef __cplusplus 12 | } 13 | #endif 14 | 15 | struct LEPUSRuntime; 16 | 17 | #ifdef USE_PRIMJS_NAPI 18 | #include "primjs_napi_defines.h" 19 | #endif 20 | typedef struct napi_env__ *napi_env; 21 | typedef struct napi_value__ *napi_value; 22 | class NAPIHandleScope; 23 | typedef void napi_func(napi_env env, NAPIHandleScope *scope); 24 | 25 | enum HandleType { 26 | HANDLE_TYPE_UNDEFINED, 27 | HANDLE_TYPE_HEAP_OBJ, 28 | HANDLE_TYPE_DIR_HEAP_OBJ, 29 | HANDLE_TYPE_LEPUS_VALUE, 30 | HANDLE_TYPE_CSTRING, 31 | HANDLE_TYPE_LEPUS_TOKEN, 32 | HANDLE_TYPE_BC_READER_STATE, 33 | HANDLE_TYPE_VALUE_BUFFER 34 | }; 35 | 36 | typedef struct { 37 | void *ptr; 38 | HandleType type; 39 | } HeapStruct; 40 | 41 | class PtrHandles { 42 | public: 43 | PtrHandles(LEPUSRuntime *rt); 44 | ~PtrHandles(); 45 | // handles 46 | void PushHandle(void *ptr, HandleType type); 47 | void ResetHandle(void *val, HandleType type); 48 | void PushLEPUSValueArrayHandle(LEPUSValue *array, int size, 49 | bool need_init = true); 50 | // special type 51 | void PushLEPUSAtom(uint32_t atom); 52 | void PushLEPUSPropertyDescriptor(LEPUSPropertyDescriptor *desc); 53 | void PushLEPUSValuePtr(LEPUSValue val); 54 | // tools 55 | HeapStruct *GetHandles() const { return handles; } 56 | void SetHeapObjIdx(int idx) { handle_idx = idx; } 57 | int GetHeapObjIdx() const { return handle_idx; } 58 | 59 | private: 60 | int handle_idx; 61 | int handle_size; 62 | HeapStruct *handles; 63 | LEPUSRuntime *rt_; 64 | void InitialHandles(); 65 | void ResizeHandles(); 66 | }; 67 | 68 | class HandleScope { 69 | public: 70 | HandleScope(LEPUSRuntime *rt); 71 | HandleScope(LEPUSContext *ctx); 72 | HandleScope(LEPUSContext *ctx, void *ptr, HandleType type); 73 | ~HandleScope(); 74 | void PushHandle(void *ptr, HandleType type); 75 | void PushLEPUSAtom(JSAtom atom); 76 | void PushLEPUSValueArrayHandle(LEPUSValue *array, int size, 77 | bool need_init = true); 78 | void ResetHandle(void *ptr, HandleType type); 79 | void PushLEPUSPropertyDescriptor(LEPUSPropertyDescriptor *desc); 80 | 81 | private: 82 | PtrHandles *ptr_handles; 83 | int handle_prev_idx; 84 | }; 85 | 86 | class NAPIHandleScope { 87 | public: 88 | NAPIHandleScope(napi_env env, LEPUSContext *ctx, napi_func *func = nullptr); 89 | explicit NAPIHandleScope(LEPUSContext *ctx) 90 | : env_(nullptr), 91 | ctx_(ctx), 92 | handle_tail_(nullptr), 93 | reset_napi_env(nullptr) { 94 | is_gc = ctx_ == nullptr ? false : LEPUS_IsGCMode(ctx_); 95 | if (is_gc) { 96 | prev_ = reinterpret_cast(GetNapiScope(ctx_)); 97 | SetNapiScope(ctx_, this); 98 | } 99 | } 100 | 101 | inline ~NAPIHandleScope() { 102 | Handle *curr = handle_tail_; 103 | while (curr) { 104 | Handle *temp = curr; 105 | if (!is_gc) { 106 | LEPUS_FreeValue(ctx_, curr->value); 107 | } 108 | curr = curr->prev; 109 | delete temp; 110 | } 111 | if (is_gc) { 112 | SetNapiScope(ctx_, prev_); 113 | } else { 114 | reset_napi_env(env_, prev_); 115 | } 116 | } 117 | 118 | NAPIHandleScope(const NAPIHandleScope &) = delete; 119 | void operator=(const NAPIHandleScope &) = delete; 120 | 121 | napi_value Escape(napi_value v); 122 | 123 | napi_value CreateHandle(LEPUSValue v); 124 | struct Handle { 125 | LEPUSValue value; 126 | Handle *prev; 127 | }; 128 | Handle *GetHandle() { return handle_tail_; } 129 | NAPIHandleScope *GetPrevScope() { return prev_; } 130 | 131 | napi_env env_; 132 | LEPUSContext *ctx_; 133 | bool is_gc; 134 | NAPIHandleScope *prev_; 135 | Handle *handle_tail_; 136 | napi_func *reset_napi_env; 137 | }; 138 | 139 | #ifdef USE_PRIMJS_NAPI 140 | #include "primjs_napi_undefs.h" 141 | #endif 142 | 143 | #endif // SRC_GC_TRACE_GC_H_ 144 | -------------------------------------------------------------------------------- /src/inspector/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | 5 | import("//Primjs.gni") 6 | primjs_source_set("inspector") { 7 | sources = [ 8 | "cpuprofiler/cpu_profiler.cc", 9 | "cpuprofiler/profile_generator.cc", 10 | "cpuprofiler/profile_tree.cc", 11 | "cpuprofiler/profiler_sampling.cc", 12 | "cpuprofiler/tracing_cpu_profiler.cc", 13 | "debugger/debugger.cc", 14 | "debugger/debugger_breakpoint.cc", 15 | "debugger/debugger_callframe.cc", 16 | "debugger/debugger_properties.cc", 17 | "debugger/debugger_queue.cc", 18 | "heapprofiler/edge.cc", 19 | "heapprofiler/entry.cc", 20 | "heapprofiler/gen.cc", 21 | "heapprofiler/heapexplorer.cc", 22 | "heapprofiler/heapprofiler.cc", 23 | "heapprofiler/serialize.cc", 24 | "heapprofiler/snapshot.cc", 25 | "protocols.cc", 26 | "runtime/runtime.cc", 27 | "string_tools.cc", 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /src/inspector/cpuprofiler/cpu_profiler.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2012 the V8 project authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Copyright 2024 The Lynx Authors. All rights reserved. 6 | // Licensed under the Apache License Version 2.0 that can be found in the 7 | // LICENSE file in the root directory of this source tree. 8 | 9 | #if !defined(_WIN32) 10 | #include "inspector/cpuprofiler/cpu_profiler.h" 11 | 12 | #include 13 | 14 | #include "inspector/cpuprofiler/profile_generator.h" 15 | #include "inspector/cpuprofiler/profiler_sampling.h" 16 | #include "inspector/interface.h" 17 | #include "quickjs/include/quickjs-inner.h" 18 | 19 | namespace primjs { 20 | namespace CpuProfiler { 21 | 22 | // CpuProfiler 23 | // set sampling interval 24 | // ref: 25 | // https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-setSamplingInterval 26 | void CpuProfiler::set_sampling_interval(uint32_t value) { 27 | assert(!is_profiling_); 28 | sampling_interval_ = value; 29 | } 30 | 31 | void CpuProfiler::StartProfiling(const char* title) { 32 | if (is_profiling_) return; 33 | ctx_->debugger_info->is_profiling_started = true; 34 | profile_ = std::make_shared(this, title); 35 | StartProcessorIfNotStarted(); 36 | } 37 | 38 | LEPUSContext* CpuProfiler::context() const { return ctx_; } 39 | void CpuProfiler::StartProcessorIfNotStarted() { 40 | if (processor_) { 41 | processor_->AddCurrentStack(); 42 | return; 43 | } 44 | 45 | if (!generator_) { 46 | generator_ = std::make_unique(profile_); 47 | } 48 | 49 | processor_ = std::make_unique(ctx_, generator_.get(), 50 | sampling_interval_); 51 | is_profiling_ = true; 52 | 53 | // profiler thread begin to run 54 | processor_->Run(); 55 | } 56 | 57 | std::shared_ptr CpuProfiler::StopProfiling( 58 | const std::string& title) { 59 | ctx_->debugger_info->is_profiling_started = false; 60 | if (!is_profiling_) return nullptr; 61 | StopProcessorIfLastProfile(title); 62 | profile_->FinishProfile(); 63 | return profile_; 64 | } 65 | 66 | void CpuProfiler::StopProcessorIfLastProfile(const std::string& title) { 67 | StopProcessor(); 68 | } 69 | 70 | void CpuProfiler::StopProcessor() { 71 | is_profiling_ = false; 72 | processor_->StopSynchronously(); 73 | processor_.reset(); 74 | } 75 | 76 | ProfileGenerator* CpuProfiler::Generator() const { return generator_.get(); } 77 | ProfilerSampling* CpuProfiler::Processor() const { return processor_.get(); } 78 | } // namespace CpuProfiler 79 | } // namespace primjs 80 | #endif 81 | -------------------------------------------------------------------------------- /src/inspector/cpuprofiler/cpu_profiler.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 the V8 project authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Copyright 2024 The Lynx Authors. All rights reserved. 6 | // Licensed under the Apache License Version 2.0 that can be found in the 7 | // LICENSE file in the root directory of this source tree. 8 | 9 | #if !defined(_WIN32) 10 | #ifndef SRC_INSPECTOR_CPUPROFILER_CPU_PROFILER_H_ 11 | #define SRC_INSPECTOR_CPUPROFILER_CPU_PROFILER_H_ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | #include "quickjs/include/quickjs.h" 23 | #ifdef __cplusplus 24 | } 25 | #endif 26 | #include "inspector/cpuprofiler/profile_generator.h" 27 | #include "inspector/cpuprofiler/profiler_sampling.h" 28 | #include "quickjs/include/quickjs-inner.h" 29 | 30 | namespace primjs { 31 | namespace CpuProfiler { 32 | 33 | class ProfilerSampling; 34 | class CpuSampler; 35 | class SamplerManager; 36 | class CpuProfile; 37 | class ProfileGenerator; 38 | 39 | typedef struct CpuProfileMetaInfo { 40 | explicit CpuProfileMetaInfo() = default; 41 | CpuProfileMetaInfo(CpuProfileMetaInfo& right) 42 | : pc_{right.pc_}, 43 | script_{right.script_}, 44 | func_name_{right.func_name_}, 45 | file_name_{right.file_name_}, 46 | line_{right.line_}, 47 | col_{right.col_} {} 48 | 49 | const uint8_t* pc_ = nullptr; 50 | LEPUSScriptSource* script_ = nullptr; 51 | JSString* func_name_{nullptr}; 52 | JSString* file_name_{nullptr}; 53 | GCPersistent func_name_handle_{}; 54 | GCPersistent file_name_handle_{}; 55 | int32_t line_ = 0; 56 | int32_t col_ = 0; 57 | } CpuProfileMetaInfo; 58 | 59 | class TickSampleEventRecord { 60 | public: 61 | static const unsigned kMaxFramesCountLog2 = 8; 62 | static const unsigned kMaxFramesCount = (1 << kMaxFramesCountLog2) - 1; 63 | CpuProfileMetaInfo stack_meta_info_[kMaxFramesCount]; 64 | LEPUSContext* ctx_; 65 | uint64_t timestamp_{0}; 66 | int32_t frames_count_{0}; 67 | }; 68 | 69 | class CpuProfiler { 70 | public: 71 | explicit CpuProfiler(LEPUSContext* ctx) : ctx_{ctx} {} 72 | ~CpuProfiler() = default; 73 | 74 | void set_sampling_interval(uint32_t); 75 | void StartProfiling(const char*); 76 | std::shared_ptr StopProfiling(const std::string&); 77 | 78 | ProfileGenerator* Generator() const; 79 | ProfilerSampling* Processor() const; 80 | LEPUSContext* context() const; 81 | auto& IsProfiling() const { return is_profiling_; } 82 | 83 | private: 84 | void StartProcessorIfNotStarted(); 85 | void StopProcessorIfLastProfile(const std::string&); 86 | void StopProcessor(); 87 | 88 | LEPUSContext* ctx_{nullptr}; 89 | std::unique_ptr generator_; 90 | std::unique_ptr processor_; 91 | std::shared_ptr profile_; 92 | uint32_t sampling_interval_{100}; 93 | bool is_profiling_{false}; 94 | }; 95 | } // namespace CpuProfiler 96 | } // namespace primjs 97 | #endif 98 | #endif // SRC_INSPECTOR_CPUPROFILER_CPU_PROFILER_H_ 99 | -------------------------------------------------------------------------------- /src/inspector/cpuprofiler/profile_generator.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 the V8 project authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Copyright 2024 The Lynx Authors. All rights reserved. 6 | // Licensed under the Apache License Version 2.0 that can be found in the 7 | // LICENSE file in the root directory of this source tree. 8 | 9 | #if !defined(_WIN32) 10 | #ifndef SRC_INSPECTOR_CPUPROFILER_PROFILE_GENERATOR_H_ 11 | #define SRC_INSPECTOR_CPUPROFILER_PROFILE_GENERATOR_H_ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | #include "quickjs/include/quickjs.h" 23 | #ifdef __cplusplus 24 | } 25 | #endif 26 | #include "inspector/cpuprofiler/profile_tree.h" 27 | 28 | namespace primjs { 29 | namespace CpuProfiler { 30 | 31 | class TickSampleEventRecord; 32 | class CpuProfiler; 33 | 34 | uint64_t HashString(const char*); 35 | uint64_t ComputedHashUint64(uint64_t); 36 | struct SampleInfo { 37 | uint64_t time_stample_; 38 | uint32_t node_id_; 39 | }; 40 | 41 | class CpuProfile { 42 | public: 43 | CpuProfile(CpuProfiler*, std::string); 44 | ~CpuProfile() = default; 45 | void AddPath(const TickSampleEventRecord&); 46 | void FinishProfile(); 47 | const std::string& title() const { return title_; } 48 | LEPUSValue GetCpuProfileContent(LEPUSContext*) const&; 49 | 50 | uint64_t start_time() const& { return start_time_; } 51 | uint64_t end_time() const& { return end_time_; } 52 | auto& TopDown() const& { return top_down_; } 53 | auto& SampleInfos() const& { return sample_info_; } 54 | 55 | private: 56 | std::string title_; 57 | std::vector sample_info_; 58 | CpuProfiler* profiler_; 59 | std::unique_ptr top_down_; 60 | LEPUSContext* ctx_{nullptr}; 61 | uint64_t start_time_{0}; 62 | uint64_t end_time_{0}; 63 | }; 64 | 65 | class ProfileGenerator { 66 | public: 67 | explicit ProfileGenerator(std::shared_ptr&); 68 | void RecordTickSample(const TickSampleEventRecord&); 69 | 70 | private: 71 | std::shared_ptr profile_; 72 | }; 73 | 74 | class CpuProfileJSONSerialize { 75 | public: 76 | explicit CpuProfileJSONSerialize(const CpuProfile& profile) 77 | : profile_{profile} {} 78 | LEPUSValue Serialize(LEPUSContext*); 79 | 80 | private: 81 | std::vector FlattenProfileNodes(); 82 | LEPUSValue SerializeNodes(LEPUSContext*); 83 | LEPUSValue SerializeNode(LEPUSContext*, const ProfileNode&); 84 | LEPUSValue SerializeCallFrame(LEPUSContext*, const ProfileNode&); 85 | LEPUSValue SerializeChildren(LEPUSContext*, const ProfileNode&); 86 | LEPUSValue SerializePositionTicks(LEPUSContext*, const ProfileNode&); 87 | std::pair SerializeSamplesAndDeltas(LEPUSContext*); 88 | const CpuProfile& profile_; 89 | }; 90 | 91 | } // namespace CpuProfiler 92 | } // namespace primjs 93 | #endif 94 | #endif // SRC_INSPECTOR_CPUPROFILER_PROFILE_GENERATOR_H_ 95 | -------------------------------------------------------------------------------- /src/inspector/cpuprofiler/profile_tree.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2012 the V8 project authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Copyright 2024 The Lynx Authors. All rights reserved. 6 | // Licensed under the Apache License Version 2.0 that can be found in the 7 | // LICENSE file in the root directory of this source tree. 8 | 9 | #if !defined(_WIN32) 10 | #include "inspector/cpuprofiler/profile_tree.h" 11 | 12 | #include 13 | 14 | #include "inspector/cpuprofiler/profile_generator.h" 15 | #include "quickjs/include/quickjs-inner.h" 16 | 17 | namespace primjs { 18 | namespace CpuProfiler { 19 | // CodeEntry 20 | bool CodeEntry::IsSameFunctionAs(const CodeEntry& other) const { 21 | // no need to compare column number 22 | return script_id_ == other.script_id_ && name_ == other.name_ && 23 | resource_name_ == other.resource_name_ && 24 | line_number_ == other.line_number_ && 25 | column_number_ == other.column_number_; 26 | } 27 | 28 | uint32_t CodeEntry::GetHash() const { 29 | uint32_t hash = 0; 30 | hash ^= ComputedHashUint64(HashString(name_.c_str())); 31 | hash ^= ComputedHashUint64(HashString(resource_name_.c_str())); 32 | hash ^= ComputedHashUint64(static_cast(line_number_)); 33 | hash ^= ComputedHashUint64(static_cast(column_number_)); 34 | if (script_id_ != "-1") { 35 | hash ^= ComputedHashUint64(HashString(script_id_.c_str())); 36 | } 37 | return hash; 38 | } 39 | 40 | // ProfileNode 41 | ProfileNode* ProfileNode::FindOrAddChild(std::unique_ptr entry, 42 | int32_t line_number) { 43 | auto map_entry = children_.find({entry.get(), line_number}); 44 | if (map_entry == children_.end()) { 45 | auto node = std::make_unique(std::move(entry), this, tree_); 46 | auto ret = node.get(); 47 | children_[{node->entry_.get(), line_number}] = ret; 48 | children_list_.push_back(std::move(node)); 49 | return ret; 50 | } else { 51 | return map_entry->second; 52 | } 53 | } 54 | 55 | void ProfileNode::IncrementLineTicks(int32_t src_line) { 56 | assert(src_line > 0); 57 | // Increment a hit counter of a certain source line. 58 | // Add a new source line if not found. 59 | auto map_entry = line_ticks_.find(src_line); 60 | if (map_entry == line_ticks_.end()) { 61 | line_ticks_[src_line] = 1; 62 | } else { 63 | line_ticks_[src_line]++; 64 | } 65 | } 66 | 67 | void ProfileNode::IncrementSelfTicks() { ++self_ticks_; } 68 | 69 | bool ProfileNode::Equals::operator()(const CodeEntryAndLineNumber& lhs, 70 | const CodeEntryAndLineNumber& rhs) const { 71 | return (lhs.code_entry == rhs.code_entry || 72 | lhs.code_entry->IsSameFunctionAs(*rhs.code_entry)) && 73 | lhs.line_number == rhs.line_number; 74 | } 75 | 76 | std::size_t ProfileNode::Hasher::operator()( 77 | const CodeEntryAndLineNumber& pair) const { 78 | auto code_entry_hash = pair.code_entry->GetHash(); 79 | auto line_number_hash = 80 | ComputedHashUint64(static_cast(pair.line_number)); 81 | return code_entry_hash ^ line_number_hash; 82 | } 83 | } // namespace CpuProfiler 84 | } // namespace primjs 85 | #endif 86 | -------------------------------------------------------------------------------- /src/inspector/cpuprofiler/profiler_time.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 the V8 project authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Copyright 2024 The Lynx Authors. All rights reserved. 6 | // Licensed under the Apache License Version 2.0 that can be found in the 7 | // LICENSE file in the root directory of this source tree. 8 | 9 | #if !defined(_WIN32) 10 | #ifndef SRC_INSPECTOR_CPUPROFILER_PROFILER_TIME_H_ 11 | #define SRC_INSPECTOR_CPUPROFILER_PROFILER_TIME_H_ 12 | #include 13 | #ifdef OS_IOS 14 | #include 15 | #include 16 | #endif 17 | #include 18 | 19 | class TimeTicks { 20 | public: 21 | static constexpr int64_t kMillisecondsPerSecond = 1000; 22 | static constexpr int64_t kMicrosecondsPerMillisecond = 1000; 23 | static constexpr int64_t kNanosecondsPerMicrosecond = 1000; 24 | static constexpr int64_t kMicrosecondsPerSecond = 25 | kMicrosecondsPerMillisecond * kMillisecondsPerSecond; 26 | static constexpr int64_t kNanosecondsPerSecond = 27 | kNanosecondsPerMicrosecond * kMicrosecondsPerSecond; 28 | 29 | constexpr TimeTicks() = default; 30 | // This method never returns a null TimeTicks 31 | static uint64_t Now() { 32 | int64_t ticks; 33 | #ifdef OS_IOS 34 | static struct mach_timebase_info info; 35 | if (info.denom == 0) { 36 | kern_return_t result = mach_timebase_info(&info); 37 | assert(result == KERN_SUCCESS); 38 | } 39 | ticks = (mach_absolute_time() * (info.numer / info.denom) / 40 | kNanosecondsPerMicrosecond); 41 | #elif defined(OS_ANDROID) 42 | struct timespec ts; 43 | if (clock_gettime(CLOCK_BOOTTIME, &ts) != 0) { 44 | ticks = 0; 45 | } else { 46 | ticks = static_cast(ts.tv_sec) * kMicrosecondsPerSecond + 47 | ts.tv_nsec / kNanosecondsPerMicrosecond; 48 | } 49 | #else 50 | struct timespec ts; 51 | if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { 52 | ticks = 0; 53 | } else { 54 | ticks = static_cast(ts.tv_sec) * kMicrosecondsPerSecond + 55 | ts.tv_nsec / kNanosecondsPerMicrosecond; 56 | } 57 | #endif 58 | return ticks + 1; 59 | } 60 | }; 61 | 62 | #endif 63 | #endif // SRC_INSPECTOR_CPUPROFILER_PROFILER_TIME_H_ 64 | -------------------------------------------------------------------------------- /src/inspector/cpuprofiler/tracing_cpu_profiler.h: -------------------------------------------------------------------------------- 1 | // Copyright 2012 the V8 project authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // Copyright 2024 The Lynx Authors. All rights reserved. 6 | // Licensed under the Apache License Version 2.0 that can be found in the 7 | // LICENSE file in the root directory of this source tree. 8 | 9 | #ifndef SRC_INSPECTOR_CPUPROFILER_TRACING_CPU_PROFILER_H_ 10 | #define SRC_INSPECTOR_CPUPROFILER_TRACING_CPU_PROFILER_H_ 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | #include "quickjs/include/quickjs.h" 16 | #ifdef __cplusplus 17 | } 18 | #endif 19 | #include "inspector/cpuprofiler/cpu_profiler.h" 20 | 21 | typedef struct DebuggerParams DebuggerParams; 22 | // ref: 23 | // https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-setSamplingInterval 24 | void HandleSetSamplingInterval(DebuggerParams *); 25 | // ref: 26 | // https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-start 27 | void HandleProfilerStart(DebuggerParams *); 28 | // ref: 29 | // https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-enable 30 | void HandleProfilerEnable(DebuggerParams *); 31 | // ref: 32 | // https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-disable 33 | void HandleProfilerDisable(DebuggerParams *); 34 | // ref: 35 | // https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#method-stop 36 | void HandleProfilerStop(DebuggerParams *); 37 | #endif // SRC_INSPECTOR_CPUPROFILER_TRACING_CPU_PROFILER_H_ 38 | -------------------------------------------------------------------------------- /src/inspector/debugger/debugger_breakpoint.h: -------------------------------------------------------------------------------- 1 | /* 2 | * QuickJS Javascript Engine 3 | * 4 | * Copyright (c) 2017-2019 Fabrice Bellard 5 | * Copyright (c) 2017-2019 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without 10 | * restriction, including without limitation the rights to use, 11 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | * OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | // Copyright 2024 The Lynx Authors. All rights reserved. 29 | // Licensed under the Apache License Version 2.0 that can be found in the 30 | // LICENSE file in the root directory of this source tree. 31 | 32 | #ifndef SRC_INSPECTOR_DEBUGGER_DEBUGGER_BREAKPOINT_H_ 33 | #define SRC_INSPECTOR_DEBUGGER_DEBUGGER_BREAKPOINT_H_ 34 | 35 | #include "inspector/debugger/debugger.h" 36 | 37 | // handle protocol: Debugger.setBreakpoints. set a breakpoint in the script 38 | void SetBreakpointByURL(DebuggerParams *); 39 | 40 | // ref: 41 | // https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-getPossibleBreakpoints 42 | void HandleGetPossibleBreakpoints(DebuggerParams *); 43 | 44 | // handle protocol: Debugger.setBreakpointActive. make all the breakpoint active 45 | void HandleSetBreakpointActive(DebuggerParams *); 46 | 47 | // handle protocol: Debugger.removeBreakpoint. remove a breakpoint by breakpoint 48 | // id 49 | void HandleRemoveBreakpoint(DebuggerParams *); 50 | 51 | // when pause because of breakpoints, return Debugger.paused event and 52 | // Debugger.breakpointResolved event 53 | // ref:https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#event-breakpointResolved 54 | // ref: 55 | // https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#event-paused 56 | void PauseAtBreakpoint(LEPUSDebuggerInfo *info, LEPUSBreakpoint *bp, 57 | const uint8_t *cur_pc); 58 | 59 | /** 60 | * @brief check if current position is a breakpoint 61 | * @param cur_pc current pc 62 | * @return if current position is a breakpoint, return the breakpoint, else 63 | * return NULL 64 | */ 65 | LEPUSBreakpoint *CheckBreakpoint(LEPUSDebuggerInfo *, LEPUSContext *ctx, 66 | const uint8_t *cur_pc); 67 | 68 | // ref: 69 | // https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-continueToLocation 70 | void HandleContinueToLocation(DebuggerParams *); 71 | 72 | // delete breakpoint of index bp_index 73 | void DeleteBreakpoint(LEPUSDebuggerInfo *info, uint32_t bp_index); 74 | 75 | // if the condition is satisfied 76 | bool SatisfyCondition(LEPUSDebuggerInfo *info, LEPUSContext *ctx, 77 | LEPUSBreakpoint *bp); 78 | #endif // SRC_INSPECTOR_DEBUGGER_DEBUGGER_BREAKPOINT_H_ 79 | -------------------------------------------------------------------------------- /src/inspector/debugger/debugger_callframe.h: -------------------------------------------------------------------------------- 1 | /* 2 | * QuickJS Javascript Engine 3 | * 4 | * Copyright (c) 2017-2019 Fabrice Bellard 5 | * Copyright (c) 2017-2019 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without 10 | * restriction, including without limitation the rights to use, 11 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | * OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | // Copyright 2024 The Lynx Authors. All rights reserved. 29 | // Licensed under the Apache License Version 2.0 that can be found in the 30 | // LICENSE file in the root directory of this source tree. 31 | 32 | #ifndef SRC_INSPECTOR_DEBUGGER_DEBUGGER_CALLFRAME_H_ 33 | #define SRC_INSPECTOR_DEBUGGER_DEBUGGER_CALLFRAME_H_ 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | #include "quickjs/include/quickjs.h" 39 | #ifdef __cplusplus 40 | } 41 | #endif 42 | 43 | #include "inspector/debugger/debugger.h" 44 | 45 | /** 46 | * @brief call this function to handle "Debugger.evalueateOnCallFrame" 47 | */ 48 | // ref: 49 | // https://chromedevtools.github.io/devtools-protocol/tot/Debugger/#method-evaluateOnCallFrame 50 | void HandleEvaluateOnCallFrame(DebuggerParams *); 51 | 52 | // when vm paused, call this function to get callframe stack 53 | LEPUSValue BuildBacktrace(LEPUSContext *ctx, const uint8_t *cur_pc); 54 | 55 | // when console.xxx is called, use this function to get stack trace 56 | LEPUSValue BuildConsoleBacktrace(LEPUSContext *ctx, const uint8_t *cur_pc, 57 | LEPUSValue *stack_trace); 58 | 59 | #endif // SRC_INSPECTOR_DEBUGGER_DEBUGGER_CALLFRAME_H_ 60 | -------------------------------------------------------------------------------- /src/inspector/debugger/debugger_properties.h: -------------------------------------------------------------------------------- 1 | /* 2 | * QuickJS Javascript Engine 3 | * 4 | * Copyright (c) 2017-2019 Fabrice Bellard 5 | * Copyright (c) 2017-2019 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without 10 | * restriction, including without limitation the rights to use, 11 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | * OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | // Copyright 2024 The Lynx Authors. All rights reserved. 29 | // Licensed under the Apache License Version 2.0 that can be found in the 30 | // LICENSE file in the root directory of this source tree. 31 | 32 | #ifndef SRC_INSPECTOR_DEBUGGER_DEBUGGER_PROPERTIES_H_ 33 | #define SRC_INSPECTOR_DEBUGGER_DEBUGGER_PROPERTIES_H_ 34 | 35 | #include "inspector/debugger/debugger.h" 36 | 37 | typedef LEPUSValue (*GetPropertyCallback)(LEPUSContext *ctx, 38 | LEPUSValue property_name, 39 | LEPUSValue &property_value, 40 | int writeable, int configurable, 41 | int enumerable); 42 | 43 | typedef LEPUSValue (*GetEntryCallback)(LEPUSContext *ctx, 44 | LEPUSValue &entry_value, 45 | int32_t writeable, int32_t configurable, 46 | int32_t enumerable); 47 | /** 48 | * @brief handle "Runtime.getProperties" protocol 49 | */ 50 | // ref: 51 | // https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-getProperties 52 | void HandleGetProperties(DebuggerParams *); 53 | 54 | LEPUSValue GetSideEffectResult(LEPUSContext *ctx); 55 | 56 | // Gets the value of an object property 57 | LEPUSValue GetRemoteObject(LEPUSContext *ctx, LEPUSValue &property_value, 58 | int32_t need_preview, int32_t return_by_value); 59 | 60 | // generate unique object id for obj 61 | LEPUSValue GenerateUniqueObjId(LEPUSContext *ctx, LEPUSValue obj); 62 | 63 | LEPUSValue GetExceptionDescription(LEPUSContext *ctx, LEPUSValue exception); 64 | 65 | #endif // SRC_INSPECTOR_DEBUGGER_DEBUGGER_PROPERTIES_H_ 66 | -------------------------------------------------------------------------------- /src/inspector/debugger/debugger_queue.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * QuickJS Javascript Engine 3 | * 4 | * Copyright (c) 2017-2019 Fabrice Bellard 5 | * Copyright (c) 2017-2019 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without 10 | * restriction, including without limitation the rights to use, 11 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | * OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | // Copyright 2024 The Lynx Authors. All rights reserved. 29 | // Licensed under the Apache License Version 2.0 that can be found in the 30 | // LICENSE file in the root directory of this source tree. 31 | 32 | #include "inspector/debugger/debugger_queue.h" 33 | 34 | struct node *InitNode(const char *content) { 35 | struct node *new_node; 36 | if (content && (new_node = (struct node *)(malloc(sizeof(struct node))))) { 37 | new_node->content = 38 | reinterpret_cast(malloc(sizeof(char) * (strlen(content) + 1))); 39 | if (new_node->content) { 40 | strcpy(new_node->content, content); 41 | new_node->p_next = nullptr; 42 | return new_node; 43 | } 44 | } 45 | return nullptr; 46 | } 47 | 48 | void DeleteQueue(struct qjs_queue *q) { 49 | struct node *head_node = q->p_head; 50 | while (head_node) { 51 | struct node *node = head_node; 52 | head_node = head_node->p_next; 53 | free(node->content); 54 | free(node); 55 | } 56 | free(q); 57 | } 58 | 59 | struct qjs_queue *InitQueue() { 60 | struct qjs_queue *q; 61 | q = (struct qjs_queue *)malloc(sizeof(struct qjs_queue)); 62 | if (q) { 63 | q->p_head = NULL; 64 | q->p_tail = NULL; 65 | } 66 | return q; 67 | } 68 | 69 | void PushBackQueue(struct qjs_queue *q, const char *content) { 70 | struct node *new_node; 71 | new_node = InitNode(content); 72 | if (q->p_head == NULL) { 73 | q->p_head = new_node; 74 | q->p_tail = new_node; 75 | } else { 76 | q->p_tail->p_next = new_node; 77 | q->p_tail = new_node; 78 | } 79 | } 80 | 81 | char *GetFrontQueue(struct qjs_queue *q) { 82 | char *content = NULL; 83 | if (q->p_head == NULL) { 84 | content = nullptr; 85 | } else { 86 | content = q->p_head->content; 87 | } 88 | return content; 89 | } 90 | 91 | void PopFrontQueue(struct qjs_queue *q) { 92 | if (q->p_head) { 93 | struct node *head_node = q->p_head; 94 | q->p_head = q->p_head->p_next; 95 | free(head_node); 96 | } 97 | } 98 | 99 | bool QueueIsEmpty(struct qjs_queue *q) { return !(q->p_head); } 100 | -------------------------------------------------------------------------------- /src/inspector/debugger/debugger_queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * QuickJS Javascript Engine 3 | * 4 | * Copyright (c) 2017-2019 Fabrice Bellard 5 | * Copyright (c) 2017-2019 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without 10 | * restriction, including without limitation the rights to use, 11 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | * OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | // Copyright 2024 The Lynx Authors. All rights reserved. 29 | // Licensed under the Apache License Version 2.0 that can be found in the 30 | // LICENSE file in the root directory of this source tree. 31 | 32 | #ifndef SRC_INSPECTOR_DEBUGGER_DEBUGGER_QUEUE_H_ 33 | #define SRC_INSPECTOR_DEBUGGER_DEBUGGER_QUEUE_H_ 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | #define LENTH 10240 40 | 41 | #include 42 | #include 43 | /** 44 | * queue need by debugger to save protocol messages 45 | */ 46 | struct node { 47 | char *content; 48 | struct node *p_next; 49 | }; 50 | 51 | struct qjs_queue { 52 | struct node *p_head; 53 | struct node *p_tail; 54 | }; 55 | 56 | struct node *InitNode(const char *content); 57 | 58 | void PushBackQueue(struct qjs_queue *q, 59 | const char *content); // put message to the queue 60 | 61 | struct qjs_queue *InitQueue(void); // init message queue 62 | 63 | void PopFrontQueue( 64 | struct qjs_queue *q); // get message from the queue, and pop the front 65 | 66 | char *GetFrontQueue(struct qjs_queue *q); // get message from the queue 67 | 68 | void DeleteQueue(struct qjs_queue *q); // delete the queue 69 | 70 | bool QueueIsEmpty(struct qjs_queue *q); // return if the queue is empty 71 | 72 | #ifdef __cplusplus 73 | } 74 | #endif 75 | 76 | #endif // SRC_INSPECTOR_DEBUGGER_DEBUGGER_QUEUE_H_ 77 | -------------------------------------------------------------------------------- /src/inspector/heapprofiler/edge.cc: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2013 the V8 project authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style license that can be 4 | // found in the LICENSE file. 5 | // 6 | // Copyright 2024 The Lynx Authors. All rights reserved. 7 | // Licensed under the Apache License Version 2.0 that can be found in the 8 | // LICENSE file in the root directory of this source tree. 9 | #include "inspector/heapprofiler/edge.h" 10 | 11 | #include "inspector/heapprofiler/entry.h" 12 | #include "inspector/heapprofiler/snapshot.h" 13 | 14 | namespace quickjs { 15 | 16 | namespace heapprofiler { 17 | 18 | HeapGraphEdge::HeapGraphEdge(Type type, const char* name, HeapEntry* from, 19 | HeapEntry* to) 20 | : name_(name), 21 | to_entry_(to), 22 | bit_filed_((static_cast(type)) | 23 | ((static_cast(from->index())) << kEdegeTypeSize)), 24 | is_index_or_name_(false) {} 25 | 26 | HeapGraphEdge::HeapGraphEdge(Type type, const std::string& name, 27 | HeapEntry* from, HeapEntry* to) 28 | : name_(name), 29 | to_entry_(to), 30 | bit_filed_((static_cast(type)) | 31 | ((static_cast(from->index())) << kEdegeTypeSize)), 32 | is_index_or_name_(false) {} 33 | 34 | HeapGraphEdge::HeapGraphEdge(Type type, uint32_t index, HeapEntry* from, 35 | HeapEntry* to) 36 | : index_(index), 37 | to_entry_(to), 38 | bit_filed_((static_cast(type)) | 39 | ((static_cast(from->index())) << kEdegeTypeSize)), 40 | is_index_or_name_(true) {} 41 | 42 | HeapEntry* HeapGraphEdge::from() const { 43 | return &(snapshot()->entries()[from_index()]); 44 | } 45 | 46 | HeapSnapshot* HeapGraphEdge::snapshot() const { return to_entry_->snapshot(); } 47 | 48 | } // namespace heapprofiler 49 | } // namespace quickjs 50 | -------------------------------------------------------------------------------- /src/inspector/heapprofiler/edge.h: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2013 the V8 project authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style license that can be 4 | // found in the LICENSE file. 5 | // 6 | // Copyright 2024 The Lynx Authors. All rights reserved. 7 | // Licensed under the Apache License Version 2.0 that can be found in the 8 | // LICENSE file in the root directory of this source tree. 9 | 10 | #ifndef SRC_INSPECTOR_HEAPPROFILER_EDGE_H_ 11 | #define SRC_INSPECTOR_HEAPPROFILER_EDGE_H_ 12 | 13 | #include 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | #include "quickjs/include/cutils.h" 19 | 20 | #ifdef __cplusplus 21 | } 22 | #endif 23 | 24 | namespace quickjs { 25 | namespace heapprofiler { 26 | 27 | class HeapEntry; 28 | class HeapSnapshot; 29 | 30 | #define GC_ROOT_ID_LIST(V) \ 31 | V(kHandleScope, "(Handle scope)") \ 32 | V(kStackRoots, "(Stack roots)") \ 33 | V(kGlobalHandles, "(Global handles)") \ 34 | V(kContextList, "(Context lists)") 35 | 36 | #define DECLARE_ENUM(enum_item, ignore) enum_item, 37 | 38 | enum class Root { 39 | GC_ROOT_ID_LIST(DECLARE_ENUM) kNumberOfRoots, 40 | }; 41 | 42 | class HeapGraphEdge { 43 | public: 44 | enum Type { 45 | kContextVariable = 0, // A variable from a function context. 46 | kElement = 1, // An element of an array. 47 | kProperty = 2, // A named object property. 48 | kInternal = 3, // A link that can't be accessed from JS, 49 | // thus, its name isn't a real property name 50 | // (e.g. parts of a ConsString). 51 | kHidden = 4, // A link that is needed for proper sizes 52 | // calculation, but may be hidden from user. 53 | kShortcut = 5, // A link that must not be followed during 54 | // sizes calculation. 55 | kWeak = 6 // A weak reference (ignored by the GC). 56 | }; 57 | 58 | static constexpr uint32_t kFromEntrySize = 29; 59 | static constexpr uint32_t kEdegeTypeSize = 3; 60 | static constexpr uint32_t kTypeMask = (1 << kEdegeTypeSize) - 1; 61 | static constexpr uint32_t kFromeEntryMask = 62 | (((uint64_t)1 << 32) - (1 << kEdegeTypeSize)); 63 | 64 | HeapGraphEdge(Type type, const char* name, HeapEntry* from, HeapEntry* to); 65 | HeapGraphEdge(Type type, uint32_t index, HeapEntry* from, HeapEntry* to); 66 | HeapGraphEdge(Type type, const std::string& name, HeapEntry* from, 67 | HeapEntry* to); 68 | 69 | Type type() const { return static_cast(bit_filed_ & kTypeMask); } 70 | 71 | uint32_t index() const { return index_; } 72 | 73 | const std::string& name() const { return name_; } 74 | 75 | HeapEntry* from() const; 76 | HeapEntry* to() const { return to_entry_; } 77 | 78 | bool IsIndex() const { return is_index_or_name_; } 79 | 80 | private: 81 | force_inline HeapSnapshot* snapshot() const; 82 | // |-from_entry_index-|type| 83 | // |-------29bit------|3bit| 84 | uint32_t from_index() const { 85 | return (bit_filed_ & kFromeEntryMask) >> kEdegeTypeSize; 86 | } 87 | 88 | uint32_t index_; 89 | std::string name_; 90 | // index -> true, name -> false 91 | 92 | HeapEntry* to_entry_; 93 | uint32_t bit_filed_; 94 | bool is_index_or_name_; 95 | }; 96 | 97 | } // namespace heapprofiler 98 | } // namespace quickjs 99 | 100 | #endif // SRC_INSPECTOR_HEAPPROFILER_EDGE_H_ 101 | -------------------------------------------------------------------------------- /src/inspector/heapprofiler/entry.cc: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2013 the V8 project authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style license that can be 4 | // found in the LICENSE file. 5 | // 6 | // Copyright 2024 The Lynx Authors. All rights reserved. 7 | // Licensed under the Apache License Version 2.0 that can be found in the 8 | // LICENSE file in the root directory of this source tree. 9 | 10 | #include "inspector/heapprofiler/entry.h" 11 | 12 | #include "inspector/heapprofiler/snapshot.h" 13 | 14 | namespace quickjs { 15 | 16 | namespace heapprofiler { 17 | HeapEntry::HeapEntry(HeapSnapshot* snapshot, uint32_t index, Type type, 18 | const char* name, SnapshotObjectId id, size_t self_size) 19 | : type_(type), 20 | index_(index), 21 | name_(name), 22 | children_count_(0), 23 | snapshot_(snapshot), 24 | self_size_(self_size), 25 | id_(id) {} 26 | 27 | HeapEntry::HeapEntry(HeapSnapshot* snapshot, uint32_t index, Type type, 28 | const std::string& name, SnapshotObjectId id, 29 | size_t self_size) 30 | : type_(type), 31 | index_(index), 32 | name_(name), 33 | children_count_(0), 34 | snapshot_(snapshot), 35 | self_size_(self_size), 36 | id_(id) {} 37 | 38 | void HeapEntry::SetNamedReference(HeapGraphEdge::Type type, const char* name, 39 | HeapEntry* entry) { 40 | ++children_count_; 41 | snapshot_->edges().emplace_back(type, name, this, entry); 42 | } 43 | 44 | void HeapEntry::SetNamedReference(HeapGraphEdge::Type type, 45 | const std::string& name, HeapEntry* entry) { 46 | ++children_count_; 47 | snapshot_->edges().emplace_back(type, name, this, entry); 48 | } 49 | 50 | void HeapEntry::SetIndexedReference(HeapGraphEdge::Type type, uint32_t index, 51 | HeapEntry* entry) { 52 | ++children_count_; 53 | snapshot_->edges().emplace_back(type, index, this, entry); 54 | } 55 | 56 | void HeapEntry::SetIndexedAutoIndexReference(HeapGraphEdge::Type type, 57 | HeapEntry* child) { 58 | SetIndexedReference(type, children_count_ + 1, child); 59 | } 60 | 61 | void HeapEntry::SetNamedAutoIndexReference(HeapGraphEdge::Type type, 62 | HeapEntry* child) { 63 | SetNamedReference(type, std::to_string(children_count_ + 1), child); 64 | return; 65 | } 66 | 67 | std::vector::iterator HeapEntry::children_end() const { 68 | return snapshot_->childrens().begin() + children_end_index_; 69 | } 70 | 71 | std::vector::iterator HeapEntry::children_begin() const { 72 | return index_ == 0 ? snapshot_->childrens().begin() 73 | : snapshot_->entries()[index_ - 1].children_end(); 74 | } 75 | 76 | uint32_t HeapEntry::set_chiledren_index(uint32_t index) { 77 | uint32_t next_index = index + children_count_; 78 | children_end_index_ = index; 79 | return next_index; 80 | } 81 | 82 | void HeapEntry::add_child(HeapGraphEdge* edge) { 83 | snapshot_->childrens()[children_end_index_++] = edge; 84 | } 85 | 86 | HeapGraphEdge* HeapEntry::child(uint32_t i) { return children_begin()[i]; } 87 | 88 | uint32_t HeapEntry::children_count() const { 89 | return static_cast(children_end() - children_begin()); 90 | } 91 | 92 | } // namespace heapprofiler 93 | } // namespace quickjs 94 | -------------------------------------------------------------------------------- /src/inspector/heapprofiler/gen.cc: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2013 the V8 project authors. All rights reserved. 3 | // Use of this source code is governed by a BSD-style license that can be 4 | // found in the LICENSE file. 5 | // 6 | // Copyright 2024 The Lynx Authors. All rights reserved. 7 | // Licensed under the Apache License Version 2.0 that can be found in the 8 | // LICENSE file in the root directory of this source tree. 9 | 10 | #include "inspector/heapprofiler/gen.h" 11 | 12 | #include 13 | #include 14 | 15 | #include "gc/collector.h" 16 | #include "inspector/heapprofiler/heapexplorer.h" 17 | #include "quickjs/include/quickjs-inner.h" 18 | 19 | namespace quickjs { 20 | namespace heapprofiler { 21 | 22 | SnapshotObjectId HeapObjectIdMaps::GetHeapObjId(const HeapPtr ptr) { 23 | auto itr = objectid_maps_.find(ptr); 24 | 25 | if (itr != objectid_maps_.end()) { 26 | return itr->second; 27 | } 28 | objectid_maps_.emplace(ptr, (next_id_ += kObjectIdStep)); 29 | return next_id_; 30 | } 31 | 32 | std::ostream& HeapObjectIdMaps::DumpObjectIdMaps(std::ostream& output) { 33 | std::string header = "Object Id Maps: \nObjAddress : ObjectId\n"; 34 | output << header.c_str(); 35 | for (auto& itr : objectid_maps_) { 36 | output << (itr.first) << " : " << itr.second << "\n"; 37 | } 38 | return output; 39 | } 40 | 41 | SnapshotObjectId HeapObjectIdMaps::GetEntryObjectId(const LEPUSValue& value) { 42 | // only allocate entry if value's tag < 0 or value is number. 43 | // if the value is a heap object, use it's ptr as object id. 44 | return GetHeapObjId(LEPUS_VALUE_GET_PTR(value)); 45 | } 46 | 47 | HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot, 48 | LEPUSContext* ctx, 49 | ProgressReportInterface* report) 50 | : snapshot_(snapshot), 51 | context_(ctx), 52 | quickjs_heap_explorer_(snapshot, ctx), 53 | reporter_(report) {} 54 | 55 | void HeapSnapshotGenerator::GenerateSnapshot() { 56 | // TODO: Implement 57 | // 1. GC 58 | // 2. count total obj 59 | // 3. Traverse the obj list and alloc entry for obj 60 | // 4. Iterate and extract every obj 61 | LEPUS_RunGC(LEPUS_GetRuntime(context_)); 62 | 63 | snapshot_->AddSyntheticRootEntries(); 64 | FillReferences(); 65 | snapshot_->FillChildren(); 66 | ProgressGenResult(); 67 | 68 | snapshot_->RememberLastJsObjectId(); 69 | return; 70 | } 71 | 72 | void HeapSnapshotGenerator::FillReferences() { 73 | quickjs_heap_explorer_.IterateAndExtractReference(this); 74 | return; 75 | } 76 | 77 | void HeapSnapshotGenerator::ProgressGenResult() { 78 | if (!reporter_) return; 79 | reporter_->ProgressResult(snapshot_->entries().size(), 80 | snapshot_->entries().size(), true); 81 | } 82 | } // namespace heapprofiler 83 | } // namespace quickjs 84 | -------------------------------------------------------------------------------- /src/inspector/heapprofiler/serialize.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 the V8 project authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Copyright 2024 The Lynx Authors. All rights reserved. 6 | // Licensed under the Apache License Version 2.0 that can be found in the 7 | // LICENSE file in the root directory of this source tree. 8 | 9 | #ifndef SRC_INSPECTOR_HEAPPROFILER_SERIALIZE_H_ 10 | #define SRC_INSPECTOR_HEAPPROFILER_SERIALIZE_H_ 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "inspector/heapprofiler/snapshot.h" 20 | 21 | namespace quickjs { 22 | namespace heapprofiler { 23 | 24 | // outputstream 25 | class OutputStream { 26 | public: 27 | virtual ~OutputStream() = default; 28 | virtual uint32_t GetChunkSize() { return 1024; } // default value 29 | virtual void WriteChunk(const std::string& chunk) = 0; 30 | }; 31 | 32 | // write to devtool helper 33 | class OutputStreamWriter { 34 | public: 35 | explicit OutputStreamWriter(OutputStream* stream) 36 | : front_stream_(stream), chunk_size(stream->GetChunkSize()) {} 37 | 38 | ~OutputStreamWriter() {} 39 | 40 | void AddString(const char* input) { 41 | ss_ << input; 42 | WriteChunkToOutStream(); 43 | } 44 | 45 | void AddString(const std::string& input) { 46 | ss_ << input; 47 | WriteChunkToOutStream(); 48 | } 49 | 50 | template 51 | void AddString(T input) { 52 | ss_ << input; 53 | } 54 | 55 | void Finalize() { 56 | front_stream_->WriteChunk(ss_.str()); 57 | Clear(); 58 | } 59 | 60 | void Clear() { 61 | ss_.clear(); 62 | ss_.str(""); 63 | } 64 | 65 | private: 66 | void WriteChunkToOutStream() { 67 | if (ss_.str().length() >= chunk_size) { 68 | front_stream_->WriteChunk(ss_.str()); 69 | Clear(); 70 | } 71 | } 72 | std::stringstream ss_; 73 | OutputStream* front_stream_; 74 | uint32_t chunk_size = 0; 75 | }; 76 | 77 | class HeapSnapshotJSONSerializer { 78 | public: 79 | explicit HeapSnapshotJSONSerializer(HeapSnapshot* snapshot) 80 | : writer_(nullptr), snapshot_(snapshot), next_string_id_(1) {} 81 | 82 | HeapSnapshotJSONSerializer(const HeapSnapshotJSONSerializer&) = delete; 83 | 84 | HeapSnapshotJSONSerializer& operator=(const HeapSnapshotJSONSerializer&) = 85 | delete; 86 | 87 | // dump heapsnapshot to outputstreamwriter 88 | void Serialize(OutputStream*); 89 | using Strings = std::unordered_map; 90 | 91 | private: 92 | uint32_t GetStringId(const std::string& str); 93 | force_inline uint32_t to_node_index(uint32_t entry_index); 94 | force_inline uint32_t to_node_index(const HeapEntry& entry); 95 | force_inline uint32_t to_node_index(const HeapEntry* entry); 96 | 97 | void SerializeImpl(); 98 | void SerializeSnapshot(); 99 | 100 | void SerializeNodes(); 101 | void SerializeNode(const HeapEntry& entry); 102 | 103 | void SerializeEdges(); 104 | void SerializeEdge(const HeapGraphEdge& edge, bool first_edge); 105 | 106 | void SerializeString(const std::string&) const; 107 | void SerializeStrings() const; 108 | 109 | static constexpr uint32_t kNodeFieldsCount = 6; 110 | static constexpr uint32_t kEdgeFiledsCount = 3; 111 | OutputStreamWriter* writer_; 112 | HeapSnapshot* snapshot_; 113 | Strings strings_map_; 114 | uint32_t next_string_id_; 115 | }; 116 | 117 | // dump str to file ,filename is time + file_suffix 118 | void js_heap_dump_file(const std::string& str, const std::string& file_suffix); 119 | } // namespace heapprofiler 120 | } // namespace quickjs 121 | #endif // SRC_INSPECTOR_HEAPPROFILER_SERIALIZE_H_ 122 | -------------------------------------------------------------------------------- /src/inspector/heapprofiler/snapshot.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2013 the V8 project authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Copyright 2024 The Lynx Authors. All rights reserved. 6 | // Licensed under the Apache License Version 2.0 that can be found in the 7 | // LICENSE file in the root directory of this source tree. 8 | 9 | #include "inspector/heapprofiler/snapshot.h" 10 | 11 | #include 12 | 13 | #include "inspector/heapprofiler/gen.h" 14 | #include "inspector/heapprofiler/heapprofiler.h" 15 | 16 | namespace quickjs { 17 | 18 | namespace heapprofiler { 19 | 20 | HeapEntry* HeapSnapshot::GetEntryById(SnapshotObjectId id) { 21 | if (entries_by_id_cache_.empty()) { 22 | entries_by_id_cache_.reserve(entries_.size()); 23 | for (auto& entry : entries_) { 24 | entries_by_id_cache_.emplace(entry.id(), &entry); 25 | } 26 | } 27 | auto it = entries_by_id_cache_.find(id); 28 | return it != entries_by_id_cache_.end() ? it->second : nullptr; 29 | } 30 | 31 | void HeapSnapshot::RememberLastJsObjectId() { 32 | max_object_id = profiler_->object_id_maps()->LastAssignedId(); 33 | } 34 | 35 | void HeapSnapshot::Delete() { profiler_->RemoveSnapshot(this); } 36 | 37 | HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type, const std::string& name, 38 | SnapshotObjectId id, size_t size) { 39 | entries_.emplace_back(this, static_cast(entries().size()), type, 40 | name, id, size); 41 | return &entries_.back(); 42 | } 43 | 44 | void HeapSnapshot::FillChildren() { 45 | uint32_t children_index = 0; 46 | for (auto& entry : entries_) { 47 | children_index = entry.set_chiledren_index(children_index); 48 | } 49 | assert(edges_.size() == static_cast(children_index)); 50 | children_.resize(edges_.size()); 51 | 52 | for (auto& edge : edges_) { 53 | edge.from()->add_child(&edge); 54 | } 55 | } 56 | 57 | void HeapSnapshot::AddSyntheticRootEntries() { 58 | AddRootEntry(); 59 | AddGcRootEntry(); 60 | AddGcSubRootEntry(); 61 | return; 62 | } 63 | 64 | const char* HeapSnapshot::GetSubRootName(Root root) { 65 | switch (root) { 66 | #define ROOT_CASE(root_id, description) \ 67 | case Root::root_id: \ 68 | return description; 69 | GC_ROOT_ID_LIST(ROOT_CASE) 70 | #undef ROOT_CASE 71 | default: 72 | break; 73 | } 74 | return nullptr; 75 | } 76 | 77 | void HeapSnapshot::AddRootEntry() { 78 | root_entry_ = 79 | AddEntry(HeapEntry::kSynthetic, "", HeapObjectIdMaps::kRootEntryId, 0); 80 | } 81 | 82 | void HeapSnapshot::AddGcRootEntry() { 83 | gc_root_entry_ = AddEntry(HeapEntry::kSynthetic, "(GC roots)", 84 | HeapObjectIdMaps::kGcRootsObjectId, 0); 85 | return; 86 | } 87 | void HeapSnapshot::AddGcSubRootEntry() { 88 | auto first_gc_subroots_id = HeapObjectIdMaps::kGcFirstGcRootsObjectId; 89 | for (size_t i = 0; i < static_cast(Root::kNumberOfRoots); ++i) { 90 | gc_subroot_entries_[i] = 91 | AddEntry(HeapEntry::kSynthetic, GetSubRootName(static_cast(i)), 92 | first_gc_subroots_id + i * HeapObjectIdMaps::kObjectIdStep, 0); 93 | } 94 | return; 95 | } 96 | 97 | } // namespace heapprofiler 98 | } // namespace quickjs 99 | -------------------------------------------------------------------------------- /src/inspector/heapprofiler/snapshot.h: -------------------------------------------------------------------------------- 1 | // Copyright 2013 the V8 project authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | // 5 | // Copyright 2024 The Lynx Authors. All rights reserved. 6 | // Licensed under the Apache License Version 2.0 that can be found in the 7 | // LICENSE file in the root directory of this source tree. 8 | 9 | #ifndef SRC_INSPECTOR_HEAPPROFILER_SNAPSHOT_H_ 10 | #define SRC_INSPECTOR_HEAPPROFILER_SNAPSHOT_H_ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "inspector/heapprofiler/entry.h" 19 | 20 | namespace quickjs { 21 | namespace heapprofiler { 22 | class HeapProfiler; 23 | 24 | class HeapSnapshot { 25 | public: 26 | explicit HeapSnapshot(HeapProfiler* profiler) : profiler_(profiler) { 27 | memset(gc_subroot_entries_, 0, sizeof(gc_subroot_entries_)); 28 | } 29 | HeapSnapshot(const HeapSnapshot&) = delete; 30 | HeapSnapshot& operator=(const HeapSnapshot&) = delete; 31 | void Delete(); 32 | 33 | HeapProfiler* profiler() const { return profiler_; } 34 | std::deque& entries() { return entries_; } 35 | const std::deque& entries() const { return entries_; } 36 | std::deque& edges() { return edges_; } 37 | const std::deque& edges() const { return edges_; } 38 | std::vector& childrens() { return children_; } 39 | 40 | bool is_complete() const { return !children_.empty(); } 41 | 42 | HeapEntry* AddEntry(HeapEntry::Type, const std::string& name, 43 | SnapshotObjectId id, size_t size); 44 | 45 | void FillChildren(); 46 | void AddSyntheticRootEntries(); 47 | HeapEntry* GetEntryById(SnapshotObjectId id); 48 | 49 | HeapEntry* root() const { return root_entry_; } 50 | HeapEntry* gc_root() const { return gc_root_entry_; } 51 | HeapEntry* gc_subroot(Root root) const { 52 | return gc_subroot_entries_[static_cast(root)]; 53 | } 54 | 55 | void RememberLastJsObjectId(); 56 | SnapshotObjectId max_snapshot_js_object_id() const { return max_object_id; } 57 | 58 | private: 59 | const char* GetSubRootName(Root root); 60 | void AddRootEntry(); 61 | void AddGcRootEntry(); 62 | void AddGcSubRootEntry(); 63 | 64 | std::deque entries_; // all node 65 | std::deque edges_; // all edges 66 | std::vector children_; // all edges by sort 67 | std::unordered_map entries_by_id_cache_; 68 | 69 | HeapProfiler* profiler_ = nullptr; 70 | HeapEntry* root_entry_ = nullptr; 71 | HeapEntry* gc_root_entry_ = nullptr; 72 | HeapEntry* gc_subroot_entries_[static_cast(Root::kNumberOfRoots)]; 73 | 74 | SnapshotObjectId max_object_id = 0; 75 | }; 76 | 77 | } // namespace heapprofiler 78 | } // namespace quickjs 79 | 80 | #endif // SRC_INSPECTOR_HEAPPROFILER_SNAPSHOT_H_ 81 | -------------------------------------------------------------------------------- /src/inspector/protocols.h: -------------------------------------------------------------------------------- 1 | /* 2 | * QuickJS Javascript Engine 3 | * 4 | * Copyright (c) 2017-2019 Fabrice Bellard 5 | * Copyright (c) 2017-2019 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without 10 | * restriction, including without limitation the rights to use, 11 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | * OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | // Copyright 2024 The Lynx Authors. All rights reserved. 29 | // Licensed under the Apache License Version 2.0 that can be found in the 30 | // LICENSE file in the root directory of this source tree. 31 | 32 | #ifndef SRC_INSPECTOR_PROTOCOLS_H_ 33 | #define SRC_INSPECTOR_PROTOCOLS_H_ 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | #include "quickjs/include/quickjs.h" 40 | 41 | #ifdef __cplusplus 42 | } 43 | #endif 44 | 45 | #include 46 | #include 47 | #include 48 | typedef struct DebuggerParams DebuggerParams; 49 | 50 | typedef enum ProtocolType { 51 | DEBUGGER_ENABLE, 52 | DEBUGGER_DISABLE, 53 | RUNTIME_ENABLE, 54 | RUNTIME_DISABLE, 55 | PROFILER_ENABLE, 56 | PROFILER_DISABLE, 57 | OTHER, 58 | } ProtocolType; 59 | 60 | // compare function and hash function for const char* 61 | unsigned int DEKHash(const char *str, unsigned int length); 62 | struct cmp { 63 | bool operator()(const char *s1, const char *s2) const { 64 | return !strcmp(s1, s2); 65 | } 66 | }; 67 | 68 | struct hash_func { 69 | size_t operator()(const char *arg) const { return DEKHash(arg, strlen(arg)); } 70 | }; 71 | 72 | // send protocol response to fontend 73 | void SendResponse(LEPUSContext *ctx, LEPUSValue message, LEPUSValue result); 74 | 75 | // send protocol notification to frontend 76 | void SendNotification(LEPUSContext *ctx, const char *method, LEPUSValue params, 77 | int32_t view_id = -1); 78 | 79 | // check if the xxx.enable is already processing 80 | bool CheckEnable(LEPUSContext *ctx, LEPUSValue message, ProtocolType protocol); 81 | 82 | #endif // SRC_INSPECTOR_PROTOCOLS_H_ 83 | -------------------------------------------------------------------------------- /src/inspector/runtime/runtime.h: -------------------------------------------------------------------------------- 1 | /* 2 | * QuickJS Javascript Engine 3 | * 4 | * Copyright (c) 2017-2019 Fabrice Bellard 5 | * Copyright (c) 2017-2019 Charlie Gordon 6 | * 7 | * Permission is hereby granted, free of charge, to any person 8 | * obtaining a copy of this software and associated documentation 9 | * files (the "Software"), to deal in the Software without 10 | * restriction, including without limitation the rights to use, 11 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the 13 | * Software is furnished to do so, subject to the following 14 | * conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | * OTHER DEALINGS IN THE SOFTWARE. 27 | */ 28 | 29 | // Copyright 2024 The Lynx Authors. All rights reserved. 30 | // Licensed under the Apache License Version 2.0 that can be found in the 31 | // LICENSE file in the root directory of this source tree. 32 | 33 | #ifndef SRC_INSPECTOR_RUNTIME_RUNTIME_H_ 34 | #define SRC_INSPECTOR_RUNTIME_RUNTIME_H_ 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif 39 | #include "quickjs/include/quickjs.h" 40 | #ifdef __cplusplus 41 | } 42 | #endif 43 | 44 | #include 45 | #include 46 | #include 47 | 48 | typedef struct DebuggerParams DebuggerParams; 49 | // handle runtime.enable protocol 50 | // ref: 51 | // https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-enable 52 | void HandleRuntimeEnable(DebuggerParams*); 53 | 54 | // ref: 55 | // https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-discardConsoleEntries 56 | void HandleDiscardConsoleEntries(DebuggerParams*); 57 | 58 | // evaluate a script 59 | // ref: 60 | // https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-evaluate 61 | void HandleEvaluate(DebuggerParams*); 62 | 63 | // compile a script 64 | // ref: 65 | // https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-compileScript 66 | void HandleCompileScript(DebuggerParams*); 67 | 68 | // ref: 69 | // https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-callFunctionOn 70 | void HandleCallFunctionOn(DebuggerParams*); 71 | 72 | // ref: 73 | // https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-globalLexicalScopeNames 74 | void HandleGlobalLexicalScopeNames(DebuggerParams*); 75 | 76 | // ref: 77 | // https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-runScript 78 | void HandleRunScript(DebuggerParams*); 79 | 80 | // handle runtime.enable protocol 81 | // ref: 82 | // https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#method-disenable 83 | void HandleRuntimeDisable(DebuggerParams*); 84 | 85 | void HandleRuntimeGetHeapUsage(DebuggerParams*); 86 | 87 | // given object id, return the real obj 88 | LEPUSValue GetObjFromObjectId(LEPUSContext* ctx, const char* object_id_str, 89 | uint64_t*); 90 | #endif // SRC_INSPECTOR_RUNTIME_RUNTIME_H_ 91 | -------------------------------------------------------------------------------- /src/inspector/string_tools.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | #include "quickjs/include/quickjs.h" 14 | 15 | #ifdef __cplusplus 16 | } 17 | #endif 18 | 19 | static bool IsASCII(char c) { return !(c & ~0x7F); } 20 | 21 | static bool IsSpaceOrNewLine(char c) { 22 | return IsASCII(c) && c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); 23 | } 24 | 25 | static std::string StripWhiteSpace(std::string& str) { 26 | std::string result = ""; 27 | if (!str.length()) return result; 28 | size_t start = 0; 29 | size_t end = str.length() - 1; 30 | 31 | // skip white space from start 32 | while (start <= end && IsSpaceOrNewLine(str[start])) ++start; 33 | 34 | // only white space 35 | if (start > end) return result; 36 | 37 | // skip white space from end 38 | while (end && IsSpaceOrNewLine(str[end])) --end; 39 | 40 | if (!start && end == str.length() - 1) { 41 | result = str; 42 | } else { 43 | result = str.substr(start, end + 1 - start); 44 | } 45 | return result; 46 | } 47 | 48 | static bool ContentNotSatisfied(const std::string& content, size_t pos, 49 | uint8_t multi_line) { 50 | bool condition1 = (content[pos] != '/'); 51 | bool condition2 = ((content[pos + 1] != '/' || multi_line) && 52 | (content[pos + 1] != '*' || !multi_line)); 53 | bool condition3 = (content[pos + 2] != '#' && content[pos + 2] != '@'); 54 | bool condition4 = (content[pos + 3] != ' ' && content[pos + 3] != '\t'); 55 | 56 | return condition1 && condition2 && condition3 && condition4; 57 | } 58 | 59 | char* FindDebuggerMagicContent(LEPUSContext* ctx, char* source, 60 | char* search_name, uint8_t multi_line) { 61 | std::string content = source; 62 | std::string name = search_name; 63 | size_t length = content.length(); 64 | size_t name_length = name.length(); 65 | 66 | size_t pos = length; 67 | size_t equal_sign_pos = 0; 68 | size_t closing_comment_pos = 0; 69 | 70 | while (true) { 71 | pos = content.rfind(name, pos); 72 | if (pos == std::string::npos) { 73 | return NULL; 74 | } 75 | 76 | // Check for a /\/[\/*][@#][ \t]/ regexp (length of 4) before found name. 77 | if (pos < 4) return NULL; 78 | pos -= 4; 79 | if (ContentNotSatisfied(content, pos, multi_line)) { 80 | continue; 81 | } 82 | equal_sign_pos = pos + 4 + name_length; 83 | if (equal_sign_pos >= length) continue; 84 | if (content[equal_sign_pos] != '=') continue; 85 | if (multi_line) { 86 | closing_comment_pos = content.find("*/", equal_sign_pos + 1); 87 | if (closing_comment_pos == std::string::npos) return NULL; 88 | } 89 | break; 90 | } 91 | 92 | size_t url_pos = equal_sign_pos + 1; 93 | std::string match = 94 | multi_line ? content.substr(url_pos, closing_comment_pos - url_pos) 95 | : content.substr(url_pos); 96 | 97 | size_t newLine = match.find("\n"); 98 | if (newLine != std::string::npos) match = match.substr(0, newLine); 99 | match = StripWhiteSpace(match); 100 | 101 | for (size_t i = 0; i < match.length(); ++i) { 102 | char c = match[i]; 103 | if (c == '"' || c == '\'' || c == ' ' || c == '\t') { 104 | match = ""; 105 | break; 106 | } 107 | } 108 | char* result = static_cast(lepus_malloc( 109 | ctx, sizeof(char) * (match.length() + 1), ALLOC_TAG_WITHOUT_PTR)); 110 | if (result) { 111 | strcpy(result, match.c_str()); 112 | } 113 | return result; 114 | } 115 | -------------------------------------------------------------------------------- /src/interpreter/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | 5 | import("//Primjs.gni") 6 | 7 | primjs_source_set("interpreter") { 8 | public_deps = [ "quickjs" ] 9 | 10 | if (enable_primjs_snapshot) { 11 | public_deps += [ "primjs" ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/interpreter/primjs/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | import("//Primjs.gni") 3 | import("snapshot_toolchain.gni") 4 | 5 | if (enable_primjs_snapshot && current_toolchain == snapshot_toolchain) { 6 | if (enable_quickjs_debugger) { 7 | embedded_file = "embedded-inspector.S" 8 | } else { 9 | embedded_file = "embedded.S" 10 | } 11 | 12 | primjs_source_set("primjs") { 13 | sources = [ "$target_os/$embedded_file" ] 14 | } 15 | } else { 16 | primjs_source_set("primjs") { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/interpreter/primjs/snapshot_toolchain.gni: -------------------------------------------------------------------------------- 1 | declare_args() { 2 | # The Prim snapshot needs to be built by code that is compiled with a 3 | # toolchain that matches the bit-width of the target CPU, but runs on 4 | # the host. 5 | snapshot_toolchain = "" 6 | } 7 | 8 | if (snapshot_toolchain == "") { 9 | if (current_os == host_os && current_cpu == host_cpu) { 10 | # This is not a cross-compile, so build the snapshot with the current 11 | # toolchain. 12 | snapshot_toolchain = current_toolchain 13 | } else if (current_os == host_os && current_cpu == "x86" && 14 | host_cpu == "x64") { 15 | # This is an x64 -> x86 cross-compile, but x64 hosts can usually run x86 16 | # binaries built for the same OS, so build the snapshot with the current 17 | # toolchain here, too. 18 | snapshot_toolchain = current_toolchain 19 | } else if (current_os == host_os && host_cpu == "arm64" && 20 | current_cpu == "arm") { 21 | # Trying to compile 32-bit arm on arm64. Good luck! 22 | snapshot_toolchain = current_toolchain 23 | } else if (host_cpu == "x64" && 24 | (current_cpu == "mips" || current_cpu == "mips64")) { 25 | # We don't support snapshot generation for big-endian targets, 26 | # therefore snapshots will need to be built using native mksnapshot 27 | # in combination with qemu 28 | snapshot_toolchain = current_toolchain 29 | } else if (is_android && host_os == "linux" && target_cpu == "arm64") { 30 | snapshot_toolchain = "//build/toolchain/${host_os}:clang_arm64" 31 | } else { 32 | snapshot_toolchain = current_toolchain 33 | } 34 | } 35 | 36 | assert(snapshot_toolchain != "", 37 | "Do not know how to build a snapshot for $current_toolchain " + 38 | "on $host_os $host_cpu") 39 | -------------------------------------------------------------------------------- /src/interpreter/quickjs/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | import("//Primjs.gni") 5 | primjs_source_set("quickjs") { 6 | sources = [ 7 | "source/cutils.cc", 8 | "source/libbf.cc", 9 | "source/libregexp.cc", 10 | "source/libunicode.cc", 11 | "source/primjs_monitor.cc", 12 | "source/quickjs-libc.cc", 13 | "source/quickjs.cc", 14 | "source/quickjs_gc.cc", 15 | "source/quickjs_queue.cc", 16 | "source/quickjs_version.cc", 17 | ] 18 | if (use_bignum) { 19 | sources += [ "source/libbf.cc" ] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/interpreter/quickjs/include/base_export.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | #ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_BASE_EXPORT_H_ 6 | #define SRC_INTERPRETER_QUICKJS_INCLUDE_BASE_EXPORT_H_ 7 | 8 | #if defined(WIN32) 9 | #define QJS_EXPORT __declspec(dllimport) 10 | #define QJS_EXPORT_FOR_DEVTOOL __declspec(dllimport) 11 | #define QJS_HIDE 12 | #else // defined(WIN32) 13 | #define QJS_EXPORT __attribute__((visibility("default"))) 14 | #define QJS_EXPORT_FOR_DEVTOOL __attribute__((visibility("default"))) 15 | #define QJS_HIDE __attribute__((visibility("hidden"))) 16 | #endif // defined(WIN32) 17 | 18 | #endif // SRC_INTERPRETER_QUICKJS_INCLUDE_BASE_EXPORT_H_ 19 | -------------------------------------------------------------------------------- /src/interpreter/quickjs/include/libregexp-opcode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Regular Expression Engine 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | // Copyright 2024 The Lynx Authors. All rights reserved. 26 | // Licensed under the Apache License Version 2.0 that can be found in the 27 | // LICENSE file in the root directory of this source tree. 28 | #ifdef DEF 29 | 30 | DEF(invalid, 1) /* never used */ 31 | DEF(char, 3) 32 | DEF(char32, 5) 33 | DEF(dot, 1) 34 | DEF(any, 1) /* same as dot but match any character including line terminator */ 35 | DEF(line_start, 1) 36 | DEF(line_end, 1) 37 | DEF(goto, 5) 38 | DEF(split_goto_first, 5) 39 | DEF(split_next_first, 5) 40 | DEF(match, 1) 41 | DEF(save_start, 2) /* save start position */ 42 | DEF(save_end, 2) /* save end position, must come after saved_start */ 43 | DEF(save_reset, 3) /* reset save positions */ 44 | DEF(loop, 5) /* decrement the top the stack and goto if != 0 */ 45 | DEF(push_i32, 5) /* push integer on the stack */ 46 | DEF(drop, 1) 47 | DEF(word_boundary, 1) 48 | DEF(not_word_boundary, 1) 49 | DEF(back_reference, 2) 50 | DEF(backward_back_reference, 2) /* must come after back_reference */ 51 | DEF(range, 3) /* variable length */ 52 | DEF(range32, 3) /* variable length */ 53 | DEF(lookahead, 5) 54 | DEF(negative_lookahead, 5) 55 | DEF(push_char_pos, 1) /* push the character position on the stack */ 56 | DEF(bne_char_pos, 5) /* pop one stack element and jump if equal to the character 57 | position */ 58 | DEF(prev, 1) /* go to the previous char */ 59 | DEF(simple_greedy_quant, 17) 60 | 61 | #endif /* DEF */ 62 | -------------------------------------------------------------------------------- /src/interpreter/quickjs/include/libregexp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Regular Expression Engine 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | // Copyright 2024 The Lynx Authors. All rights reserved. 26 | // Licensed under the Apache License Version 2.0 that can be found in the 27 | // LICENSE file in the root directory of this source tree. 28 | #ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_LIBREGEXP_H_ 29 | #define SRC_INTERPRETER_QUICKJS_INCLUDE_LIBREGEXP_H_ 30 | 31 | #include 32 | 33 | #include "base_export.h" 34 | #include "libunicode.h" 35 | 36 | #define LRE_BOOL int /* for documentation purposes */ 37 | 38 | #define LRE_FLAG_GLOBAL (1 << 0) 39 | #define LRE_FLAG_IGNORECASE (1 << 1) 40 | #define LRE_FLAG_MULTILINE (1 << 2) 41 | #define LRE_FLAG_DOTALL (1 << 3) 42 | #define LRE_FLAG_UTF16 (1 << 4) 43 | #define LRE_FLAG_STICKY (1 << 5) 44 | 45 | #define LRE_FLAG_NAMED_GROUPS \ 46 | (1 << 7) /* named groups are present in the regexp */ 47 | 48 | uint8_t *lre_compile(int *plen, char *error_msg, int error_msg_size, 49 | const char *buf, size_t buf_len, int re_flags, 50 | void *opaque); 51 | int lre_get_capture_count(const uint8_t *bc_buf); 52 | QJS_HIDE int lre_get_flags(const uint8_t *bc_buf); 53 | int lre_exec(uint8_t **capture, const uint8_t *bc_buf, const uint8_t *cbuf, 54 | int cindex, int clen, int cbuf_type, void *opaque); 55 | 56 | QJS_HIDE int lre_parse_escape(const uint8_t **pp, int allow_utf16); 57 | QJS_HIDE LRE_BOOL lre_is_space(int c); 58 | 59 | /* must be provided by the user */ 60 | QJS_HIDE LRE_BOOL lre_check_stack_overflow(void *opaque, size_t alloca_size); 61 | void *lre_realloc(__attribute__((unused)) void *opaque, void *ptr, size_t size, 62 | __attribute__((unused)) int alloc_tag); 63 | 64 | /* LEPUS identifier test */ 65 | extern uint32_t const lre_id_start_table_ascii[4]; 66 | extern uint32_t const lre_id_continue_table_ascii[4]; 67 | 68 | static inline int lre_js_is_ident_first(int c) { 69 | if ((uint32_t)c < 128) { 70 | return (lre_id_start_table_ascii[c >> 5] >> (c & 31)) & 1; 71 | } else { 72 | #ifdef CONFIG_ALL_UNICODE 73 | return lre_is_id_start(c); 74 | #else 75 | return !lre_is_space(c); 76 | #endif 77 | } 78 | } 79 | 80 | void lre_free(void *); 81 | 82 | static inline int lre_js_is_ident_next(int c) { 83 | if ((uint32_t)c < 128) { 84 | return (lre_id_continue_table_ascii[c >> 5] >> (c & 31)) & 1; 85 | } else { 86 | /* ZWNJ and ZWJ are accepted in identifiers */ 87 | #ifdef CONFIG_ALL_UNICODE 88 | return lre_is_id_continue(c) || c == 0x200C || c == 0x200D; 89 | #else 90 | return !lre_is_space(c) || c == 0x200C || c == 0x200D; 91 | #endif 92 | } 93 | } 94 | 95 | #undef LRE_BOOL 96 | 97 | #endif // SRC_INTERPRETER_QUICKJS_INCLUDE_LIBREGEXP_H_ 98 | -------------------------------------------------------------------------------- /src/interpreter/quickjs/include/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Linux klist like system 3 | * 4 | * Copyright (c) 2016-2017 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | // Copyright 2024 The Lynx Authors. All rights reserved. 26 | // Licensed under the Apache License Version 2.0 that can be found in the 27 | // LICENSE file in the root directory of this source tree. 28 | #ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_LIST_H_ 29 | #define SRC_INTERPRETER_QUICKJS_INCLUDE_LIST_H_ 30 | 31 | #ifndef NULL 32 | #include 33 | #endif 34 | 35 | struct list_head { 36 | struct list_head *prev; 37 | struct list_head *next; 38 | }; 39 | 40 | #define LIST_HEAD_INIT(el) \ 41 | { &(el), &(el) } 42 | 43 | /* return the pointer of type 'type *' containing 'el' as field 'member' */ 44 | #define list_entry(el, type, member) \ 45 | ((type *)((uint8_t *)(el)-offsetof(type, member))) 46 | 47 | static inline void init_list_head(struct list_head *head) { 48 | head->prev = head; 49 | head->next = head; 50 | } 51 | 52 | /* insert 'el' between 'prev' and 'next' */ 53 | static inline void __list_add(struct list_head *el, struct list_head *prev, 54 | struct list_head *next) { 55 | prev->next = el; 56 | el->prev = prev; 57 | el->next = next; 58 | next->prev = el; 59 | } 60 | 61 | /* add 'el' at the head of the list 'head' (= after element head) */ 62 | static inline void list_add(struct list_head *el, struct list_head *head) { 63 | __list_add(el, head, head->next); 64 | } 65 | 66 | /* add 'el' at the end of the list 'head' (= before element head) */ 67 | static inline void list_add_tail(struct list_head *el, struct list_head *head) { 68 | __list_add(el, head->prev, head); 69 | } 70 | 71 | static inline void list_del(struct list_head *el) { 72 | struct list_head *prev, *next; 73 | prev = el->prev; 74 | next = el->next; 75 | prev->next = next; 76 | next->prev = prev; 77 | el->prev = NULL; /* fail safe */ 78 | el->next = NULL; /* fail safe */ 79 | } 80 | 81 | static inline int list_empty(struct list_head *el) { return el->next == el; } 82 | 83 | #define list_for_each(el, head) \ 84 | for (el = (head)->next; el != (head); el = el->next) 85 | 86 | #define list_for_each_safe(el, el1, head) \ 87 | for (el = (head)->next, el1 = el->next; el != (head); \ 88 | el = el1, el1 = el->next) 89 | 90 | #define list_for_each_prev(el, head) \ 91 | for (el = (head)->prev; el != (head); el = el->prev) 92 | 93 | #define list_for_each_prev_safe(el, el1, head) \ 94 | for (el = (head)->prev, el1 = el->prev; el != (head); \ 95 | el = el1, el1 = el->prev) 96 | 97 | #endif // SRC_INTERPRETER_QUICKJS_INCLUDE_LIST_H_ 98 | -------------------------------------------------------------------------------- /src/interpreter/quickjs/include/primjs_monitor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | #ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_PRIMJS_MONITOR_H_ 6 | #define SRC_INTERPRETER_QUICKJS_INCLUDE_PRIMJS_MONITOR_H_ 7 | 8 | #define MODULE_PRIMJS "primjs" 9 | #define MODULE_QUICK "quickjs" 10 | 11 | #define MODULE_NAPI "napi" 12 | #define DEFAULT_BIZ_NAME "unknown_biz_name" 13 | 14 | void MonitorEvent(const char* moduleName, const char* bizName, 15 | const char* dataKey, const char* dataValue); 16 | bool GetSettingsWithKey(const char* key); 17 | int GetSettingsFlag(); 18 | 19 | #endif // SRC_INTERPRETER_QUICKJS_INCLUDE_PRIMJS_MONITOR_H_ 20 | -------------------------------------------------------------------------------- /src/interpreter/quickjs/include/quickjs-libc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * QuickJS C library 3 | * 4 | * Copyright (c) 2017-2018 Fabrice Bellard 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | */ 24 | 25 | // Copyright 2024 The Lynx Authors. All rights reserved. 26 | // Licensed under the Apache License Version 2.0 that can be found in the 27 | // LICENSE file in the root directory of this source tree. 28 | #ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_LIBC_H_ 29 | #define SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_LIBC_H_ 30 | 31 | #include 32 | #include 33 | 34 | #if defined(_WIN32) 35 | #ifndef WIN32_LEAN_AND_MEAN 36 | #define WIN32_LEAN_AND_MEAN 37 | #endif 38 | #include // portable: uint64_t MSVC: __int64 39 | #include 40 | #endif 41 | 42 | #include "quickjs.h" 43 | 44 | QJS_HIDE LEPUSModuleDef *lepus_init_module_std(LEPUSContext *ctx, 45 | const char *module_name); 46 | QJS_HIDE LEPUSModuleDef *lepus_init_module_os(LEPUSContext *ctx, 47 | const char *module_name); 48 | QJS_HIDE void lepus_std_add_helpers(LEPUSContext *ctx, int argc, char **argv); 49 | void lepus_std_loop(LEPUSContext *ctx); 50 | QJS_HIDE void lepus_std_free_handlers(LEPUSRuntime *rt); 51 | QJS_HIDE void lepus_std_dump_error(LEPUSContext *ctx); 52 | 53 | #if LYNX_SIMPLIFY 54 | uint8_t *lepus_load_file(LEPUSContext *ctx, size_t *pbuf_len, 55 | const char *filename); 56 | LEPUSModuleDef *lepus_module_loader(LEPUSContext *ctx, const char *module_name, 57 | void *opaque); 58 | void lepus_std_eval_binary(LEPUSContext *ctx, const uint8_t *buf, 59 | size_t buf_len, int flags); 60 | #endif 61 | 62 | #if defined(_WIN32) 63 | int gettimeofday(struct timeval *tp, void *tzp); 64 | #endif 65 | 66 | #endif // SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_LIBC_H_ 67 | -------------------------------------------------------------------------------- /src/interpreter/quickjs/include/quickjs-tag.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | // 5 | // clang-format off 6 | #ifdef DEFTAG 7 | 8 | #ifndef deftag 9 | #define deftag(type, description) DEFTAG(type, description) 10 | #endif 11 | // LEPUSValue tag 12 | DEFTAG(LEPUSObject, "LEPUSObject") 13 | DEFTAG(LEPUSLepusRef, "LEPUSLepusRef") 14 | DEFTAG(JSString, "JSString") 15 | DEFTAG(JSShape, "JSShape") 16 | DEFTAG(LEPUSFunctionBytecode, "LEPUSFunctionBytecode") 17 | DEFTAG(JSTypedArray, "JSTypedArray") 18 | DEFTAG(JSMapState, "JSMapState") 19 | DEFTAG(JSMapIteratorData, "JSMapIteratorData") 20 | DEFTAG(JSFunctionDef, "JSFunctionDef") 21 | DEFTAG(JSArrayBuffer, "JSArrayBuffer") 22 | DEFTAG(LEPUSScriptSource, "LEPUSScriptSource") 23 | DEFTAG(LEPUSModuleDef, "LEPUSModuleDef") 24 | DEFTAG(JSGeneratorData, "JSGeneratorData") 25 | DEFTAG(JSAsyncFunctionData, "JSAsyncFunctionData") 26 | DEFTAG(JSVarRef, "JSVarRef") 27 | // LEPUSObject, class_id, finalizer 28 | DEFTAG(JSBoundFunction, "JSBoundFunction") 29 | DEFTAG(JSCFunctionDataRecord, "JSCFunctionDataRecord") 30 | DEFTAG(JSForInIterator, "JSForInIterator") 31 | DEFTAG(JSSeparableString, "JSSeparableString") 32 | DEFTAG(JSArrayIteratorData, "JSArrayIteratorData") 33 | DEFTAG(JSRegExpStringIteratorData, "JSRegExpStringIteratorData") 34 | DEFTAG(JSProxyData, "JSProxyData") 35 | DEFTAG(JSPromiseData, "JSPromiseData") 36 | DEFTAG(JSPromiseReactionData, "JSPromiseReactionData") 37 | DEFTAG(JSPromiseFunctionData, "JSPromiseFunctionData") 38 | DEFTAG(JSAsyncFromSyncIteratorData, "JSAsyncFromSyncIteratorData") 39 | DEFTAG(JSAsyncGeneratorData, "JSAsyncGeneratorData") 40 | // other 41 | DEFTAG(LEPUSPropertyEnum, "LEPUSPropertyEnum") 42 | DEFTAG(JSMapRecord, "JSMapRecord") 43 | DEFTAG(ValueSlot, "ValueSlot") 44 | DEFTAG(LEPUSDebuggerInfo, "DebuggerInfo") 45 | 46 | DEFTAG(FinalizationRegistryData, "FinalizationRegistryData") 47 | DEFTAG(WeakRefData, "WeakRefData") 48 | 49 | #ifdef CONFIG_BIGNUM 50 | // big number 51 | DEFTAG(JSBigFloat, "JSBigFloat") 52 | #endif 53 | 54 | DEFTAG(JSOSRWHandler, "JSOSRWHandler") 55 | DEFTAG(JSOSSignalHandler, "JSOSSignalHandler") 56 | DEFTAG(JSOSTimer, "JSOSTimer") 57 | DEFTAG(JSSTDFile, "JSSTDFile") 58 | 59 | deftag(JSSymbol, "JSSymbol") 60 | deftag(JSValueArray, "JSValueArray") 61 | deftag(JSConstString, "JSConstString") 62 | deftag(JsonStrArray, "JsonStrArray") 63 | #undef deftag 64 | #endif 65 | -------------------------------------------------------------------------------- /src/interpreter/quickjs/include/quickjs_queue.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | #ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_QUEUE_H_ 5 | #define SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_QUEUE_H_ 6 | 7 | #include 8 | 9 | #define DEFAULT_INIT_SIZE 2048 10 | 11 | #ifdef ENABLE_GC_DEBUG_TOOLS 12 | #include 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | bool check_valid_ptr(void *runtime, void *ptr); 17 | #ifdef __cplusplus 18 | } 19 | #endif 20 | #endif 21 | 22 | // rear is emptry, capacity == size - 1 23 | class Queue { 24 | public: 25 | Queue(void *runtime, int initial_size); 26 | explicit Queue(void *runtime); 27 | ~Queue(); 28 | 29 | bool IsEmpty(); 30 | bool IsFull(); 31 | void EnQueue(void *ptr); 32 | void *DeQueue(); 33 | void *Front(); 34 | void ResizeQueue(); 35 | // void Display(); 36 | int GetCount() { return count; } 37 | int GetSize() { return size; } 38 | const void *GetQueue() { return queue; } 39 | void Split(int cnt, Queue *q) { 40 | for (int i = 0; i < cnt; i++) { 41 | q->EnQueue(DeQueue()); 42 | } 43 | } 44 | 45 | private: 46 | void **queue; 47 | void *rt_; 48 | int front; 49 | int rear; 50 | int count; 51 | int size; 52 | }; 53 | 54 | #endif // SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_QUEUE_H_ 55 | -------------------------------------------------------------------------------- /src/interpreter/quickjs/include/quickjs_version.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | #ifndef SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_VERSION_H_ 6 | #define SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_VERSION_H_ 7 | 8 | #include 9 | #include 10 | #define FEATURE_LEPUSNG_DEBUGINFO_OUTSIDE "2.5" 11 | #define PRIMJS_ADD_VERSION_CODE "2.14" 12 | 13 | typedef struct Version { 14 | int major, minor, revision, build; 15 | } Version; 16 | 17 | void VersionInit(Version* v, const char* version); 18 | uint8_t VersionLessOrEqual(Version v1, Version other); 19 | uint8_t IsHigherOrEqual(const char* targetV, const char* baseV); 20 | 21 | #endif // SRC_INTERPRETER_QUICKJS_INCLUDE_QUICKJS_VERSION_H_ 22 | -------------------------------------------------------------------------------- /src/interpreter/quickjs/source/primjs_monitor.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | #include "quickjs/include/primjs_monitor.h" 6 | 7 | void MonitorEvent(const char*, const char*, const char*, const char*) {} 8 | 9 | int GetSettingsFlag() { return 0; } 10 | bool GetSettingsWithKey(const char* key) { return false; } 11 | -------------------------------------------------------------------------------- /src/interpreter/quickjs/source/quickjs_version.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | #include "quickjs/include/quickjs_version.h" 9 | #ifdef __cplusplus 10 | } 11 | #endif 12 | 13 | #include 14 | void VersionInit(struct Version* v, const char* version) { 15 | v->build = 0; 16 | v->major = 0; 17 | v->minor = 0; 18 | v->revision = 0; 19 | sscanf(version, "%d.%d.%d.%d", &v->major, &v->minor, &v->revision, &v->build); 20 | if (v->major < 0) v->major = 0; 21 | if (v->minor < 0) v->minor = 0; 22 | if (v->revision < 0) v->revision = 0; 23 | if (v->build < 0) v->build = 0; 24 | } 25 | 26 | static uint8_t VersionLess(struct Version v1, struct Version other) { 27 | if (v1.major < other.major) 28 | return 1; 29 | else if (v1.major > other.major) 30 | return 0; 31 | 32 | // Compare moinor 33 | if (v1.minor < other.minor) 34 | return 1; 35 | else if (v1.minor > other.minor) 36 | return 0; 37 | 38 | // Compare revision 39 | if (v1.revision < other.revision) 40 | return 1; 41 | else if (v1.revision > other.revision) 42 | return 0; 43 | 44 | // Compare build 45 | if (v1.build < other.build) 46 | return 1; 47 | else if (v1.build > other.build) 48 | return 0; 49 | 50 | return 0; 51 | } 52 | 53 | static uint8_t VersionEqual(Version v1, Version other) { 54 | return (v1.major == other.major && v1.minor == other.minor && 55 | v1.revision == other.revision && v1.build == other.build); 56 | } 57 | 58 | uint8_t VersionLessOrEqual(Version v1, Version other) { 59 | return VersionEqual(v1, other) || VersionLess(v1, other); 60 | } 61 | 62 | uint8_t IsHigherOrEqual(const char* targetV, const char* baseV) { 63 | if (!targetV || strcmp(targetV, "") == 0 || strcmp(targetV, "null") == 0) { 64 | return 1; 65 | } 66 | Version base; 67 | VersionInit(&base, baseV); 68 | Version target; 69 | VersionInit(&target, targetV); 70 | return VersionLessOrEqual(base, target); 71 | } 72 | -------------------------------------------------------------------------------- /src/napi/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | import("//Primjs.gni") 5 | config("napi_public_config") { 6 | include_dirs = [ "." ] 7 | 8 | defines = [ 9 | "PRIMJS_MIN_LOG_LEVEL=5", # disable alog in unittests 10 | ] 11 | if (use_rtti) { 12 | defines += [ "NAPI_CPP_RTTI" ] 13 | } else { 14 | defines += [ "NAPI_DISABLE_CPP_RTTI" ] 15 | } 16 | } 17 | 18 | napi_source_set("napi") { 19 | public = [ 20 | "js_native_api.h", 21 | "js_native_api_types.h", 22 | "napi.h", 23 | "napi_module.h", 24 | ] 25 | sources = [ 26 | "napi.cc", 27 | "napi_module.cc", 28 | ] 29 | public_deps = [ "./common:common" ] 30 | public_configs = [ ":napi_public_config" ] 31 | } 32 | 33 | napi_source_set("napi_env") { 34 | public_deps = [ 35 | ":napi", 36 | "./env:env", 37 | ] 38 | } 39 | 40 | napi_source_set("napi_runtime") { 41 | public_deps = [ 42 | ":napi", 43 | "env:runtime", 44 | ] 45 | } 46 | 47 | napi_source_set("napi_jsc") { 48 | public_deps = [ 49 | ":napi", 50 | "jsc:jsc", 51 | ] 52 | } 53 | 54 | napi_source_set("napi_quickjs") { 55 | public_deps = [ 56 | ":napi", 57 | "quickjs:native_quickjs", 58 | ] 59 | } 60 | 61 | napi_source_set("napi_v8") { 62 | public_deps = [ 63 | ":napi", 64 | "v8:v8", 65 | ] 66 | } 67 | -------------------------------------------------------------------------------- /src/napi/adapter/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | 5 | import("//Primjs.gni") 6 | config("config") { 7 | include_dirs = [ "." ] 8 | } 9 | 10 | napi_source_set("adapter") { 11 | sources = [ "js_native_api_adapter.cc" ] 12 | public = [ "./js_native_api_adapter.h" ] 13 | public_configs = [ ":config" ] 14 | public_deps = [ "../..:napi_lib" ] 15 | } 16 | -------------------------------------------------------------------------------- /src/napi/common/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | import("//Primjs.gni") 5 | config("common_config") { 6 | cflags = [ "-Wno-unused-value" ] 7 | } 8 | 9 | napi_source_set("common") { 10 | public = [ "napi_state.h" ] 11 | public_configs = [ ":common_config" ] 12 | sources = [ "code_cache.cc" ] 13 | } 14 | -------------------------------------------------------------------------------- /src/napi/common/code_cache.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a MIT license that can be 5 | * found in the LICENSE file in the root of the source tree. 6 | */ 7 | 8 | // Copyright 2024 The Lynx Authors. All rights reserved. 9 | // Licensed under the Apache License Version 2.0 that can be found in the 10 | // LICENSE file in the root directory of this source tree. 11 | 12 | #ifndef SRC_NAPI_COMMON_CODE_CACHE_H_ 13 | #define SRC_NAPI_COMMON_CODE_CACHE_H_ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | struct CachedData { 22 | CachedData() : length_(0), data_(nullptr), file_name_("") {} 23 | 24 | CachedData(int length, const uint8_t* data, const std::string& name) 25 | : length_(length), data_(data), file_name_(name) {} 26 | 27 | // carefully copy and move objects of this class 28 | ~CachedData() { 29 | if (data_) delete[] data_; 30 | } 31 | 32 | static bool compare(CachedData* left, CachedData* right) { 33 | if (left->used_times_ > right->used_times_) { 34 | return true; 35 | } else if (left->used_times_ < right->used_times_) { 36 | return false; 37 | } else { 38 | return left->length_ > right->length_; 39 | } 40 | } 41 | 42 | int used_times_ = 0; 43 | int length_; 44 | const uint8_t* data_; 45 | const std::string file_name_; 46 | }; 47 | 48 | typedef std::unordered_map CacheMap; 49 | typedef std::vector CacheVector; 50 | #define SHORT_SIZE 2 51 | #define INT_SIZE 4 52 | #define DOUBLE_SIZE 8 53 | 54 | class CacheBlob { 55 | private: 56 | enum CacheMode { kWriting, kAppending }; 57 | static constexpr double MAGIC = 3.14159265; 58 | 59 | public: 60 | explicit CacheBlob(const std::string& path, int max_cap = 1 << 20) 61 | : current_size_(0), 62 | target_path_(path), 63 | max_capacity_(max_cap), 64 | empty_cache_(new CachedData) {} 65 | 66 | virtual ~CacheBlob() { 67 | for (auto it : cache_map_) delete it.second; 68 | if (append_vec_) delete append_vec_; 69 | } 70 | 71 | // NOTE: modifications on CacheBlob can only happen in worker Thread. 72 | bool insert(const std::string& filename, const uint8_t* data, int length); 73 | const CachedData* find(const std::string& filename, int* len) const; 74 | void remove(const std::string& filename); 75 | void output(); 76 | bool input(); 77 | int size() const { return current_size_; } 78 | 79 | #ifdef PROFILE_CODECACHE 80 | void dump_status(void* p); 81 | #endif // PROFILE_CODECACHE 82 | 83 | private: 84 | void write_cache_unit(FILE* file_out, const CachedData* unit); 85 | void read_cache_unit(FILE* file_in); 86 | void remove_from_ranking_list(CachedData* target); 87 | // This is a vast-time-costing function 88 | bool get_enough_space(int data_size); 89 | 90 | CacheMap cache_map_; 91 | // ranking list for caches' frequency of being used. 92 | CacheVector heat_ranking_; 93 | int current_size_; 94 | const std::string target_path_; 95 | int max_capacity_; 96 | mutable std::mutex write_mutex_; 97 | std::unique_ptr empty_cache_; 98 | 99 | #ifdef PROFILE_CODECACHE 100 | mutable int total_query_ = 0; 101 | mutable int missed_query_ = 0; 102 | mutable int expired_query_ = 0; 103 | #endif // PROFILE_CODECACHE 104 | CacheMode mode_ = kWriting; 105 | CacheVector* append_vec_ = nullptr; 106 | }; 107 | 108 | #endif // SRC_NAPI_COMMON_CODE_CACHE_H_ 109 | -------------------------------------------------------------------------------- /src/napi/common/napi_state.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a MIT license that can be 5 | * found in the LICENSE file in the root of the source tree. 6 | */ 7 | 8 | // Copyright 2024 The Lynx Authors. All rights reserved. 9 | // Licensed under the Apache License Version 2.0 that can be found in the 10 | // LICENSE file in the root directory of this source tree. 11 | 12 | #ifndef SRC_NAPI_COMMON_NAPI_STATE_H_ 13 | #define SRC_NAPI_COMMON_NAPI_STATE_H_ 14 | 15 | #include "js_native_api.h" 16 | #ifdef USE_PRIMJS_NAPI 17 | #include "primjs_napi_defines.h" 18 | #endif 19 | typedef struct napi_env_data__* napi_env_data; 20 | 21 | struct napi_state__ { 22 | napi_extended_error_info last_error; 23 | napi_env_data env_data; 24 | }; 25 | 26 | inline napi_status napi_clear_last_error(napi_env env) { 27 | env->state->last_error.error_code = napi_ok; 28 | 29 | env->state->last_error.engine_error_code = 0; 30 | return napi_ok; 31 | } 32 | 33 | inline napi_status napi_set_last_error(napi_env env, napi_status error_code) { 34 | env->state->last_error.error_code = error_code; 35 | return error_code; 36 | } 37 | #ifdef USE_PRIMJS_NAPI 38 | #include "primjs_napi_undefs.h" 39 | #endif 40 | #endif // SRC_NAPI_COMMON_NAPI_STATE_H_ 41 | -------------------------------------------------------------------------------- /src/napi/env/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | 5 | import("//Primjs.gni") 6 | 7 | config("config") { 8 | include_dirs = [ 9 | "./", 10 | "../..", 11 | ] 12 | } 13 | 14 | napi_source_set("env") { 15 | sources = [ "napi_env.cc" ] 16 | public = [ "napi_env.h" ] 17 | configs = [ ":config" ] 18 | } 19 | 20 | napi_source_set("runtime") { 21 | sources = [ "napi_runtime.cc" ] 22 | public = [ "napi_runtime.h" ] 23 | configs = [ ":config" ] 24 | } 25 | -------------------------------------------------------------------------------- /src/napi/env/napi_env.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a MIT license that can be 5 | * found in the LICENSE file in the root of the source tree. 6 | */ 7 | 8 | // Copyright 2024 The Lynx Authors. All rights reserved. 9 | // Licensed under the Apache License Version 2.0 that can be found in the 10 | // LICENSE file in the root directory of this source tree. 11 | 12 | #ifndef SRC_NAPI_ENV_NAPI_ENV_H_ 13 | #define SRC_NAPI_ENV_NAPI_ENV_H_ 14 | 15 | #include "js_native_api.h" 16 | #ifdef USE_PRIMJS_NAPI 17 | #include "primjs_napi_defines.h" 18 | #endif 19 | 20 | EXTERN_C_START 21 | 22 | NAPI_EXTERN napi_env napi_new_env(); 23 | 24 | NAPI_EXTERN void napi_free_env(napi_env); 25 | 26 | NAPI_EXTERN void napi_setup_loader(napi_env env, const char* name); 27 | 28 | EXTERN_C_END 29 | 30 | #ifdef USE_PRIMJS_NAPI 31 | #include "primjs_napi_undefs.h" 32 | #endif 33 | 34 | #endif // SRC_NAPI_ENV_NAPI_ENV_H_ 35 | -------------------------------------------------------------------------------- /src/napi/env/napi_runtime.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a MIT license that can be 5 | * found in the LICENSE file in the root of the source tree. 6 | */ 7 | 8 | // Copyright 2024 The Lynx Authors. All rights reserved. 9 | // Licensed under the Apache License Version 2.0 that can be found in the 10 | // LICENSE file in the root directory of this source tree. 11 | 12 | #ifndef SRC_NAPI_ENV_NAPI_RUNTIME_H_ 13 | #define SRC_NAPI_ENV_NAPI_RUNTIME_H_ 14 | 15 | #include "js_native_api.h" 16 | #ifdef USE_PRIMJS_NAPI 17 | #include "primjs_napi_defines.h" 18 | #endif 19 | typedef struct napi_runtime_configuration__* napi_runtime_configuration; 20 | 21 | typedef void (*napi_uncaught_exception_handler)(napi_env env, 22 | napi_value exception, 23 | void* ctx); 24 | 25 | typedef void (*napi_foreground_cb)(void* task); 26 | 27 | typedef void (*napi_foreground_handler)(napi_foreground_cb js_cb, void* task, 28 | void* ctx); 29 | 30 | typedef void (*napi_worker_lifecycle_callback)(void* ctx); 31 | 32 | typedef void (*napi_worker_task_runner)(void* task); 33 | 34 | typedef void (*napi_worker_task_handler)(napi_worker_task_runner task_runner, 35 | void* task, void* ctx); 36 | 37 | EXTERN_C_START 38 | 39 | /** 40 | * Deprecated 41 | * use `napi_attach_runtime_with_configuration` instead 42 | */ 43 | NAPI_EXTERN void NAPI_DEPRECATED napi_attach_runtime( 44 | napi_env env, napi_foreground_handler task_handler, void* task_ctx, 45 | napi_uncaught_exception_handler uncaught_handler, void* uncaught_ctx); 46 | 47 | NAPI_EXTERN napi_runtime_configuration napi_create_runtime_configuration(); 48 | 49 | NAPI_EXTERN void napi_delete_runtime_configuration(napi_runtime_configuration); 50 | 51 | NAPI_EXTERN void napi_runtime_config_foreground_handler( 52 | napi_runtime_configuration configuration, 53 | napi_foreground_handler task_handler, void* task_ctx); 54 | NAPI_EXTERN void napi_runtime_config_uncaught_handler( 55 | napi_runtime_configuration configuration, 56 | napi_uncaught_exception_handler task_handler, void* uncaught_ctx); 57 | NAPI_EXTERN void napi_runtime_config_worker_handler( 58 | napi_runtime_configuration configuration, 59 | napi_worker_lifecycle_callback on_worker_start, 60 | napi_worker_lifecycle_callback on_worker_stop, 61 | napi_worker_task_handler worker_task_handler, void* worker_ctx); 62 | NAPI_EXTERN void napi_runtime_config_worker_stack_size( 63 | napi_runtime_configuration configuration, size_t stack_size); 64 | 65 | NAPI_EXTERN void napi_attach_runtime_with_configuration( 66 | napi_env env, napi_runtime_configuration configuration); 67 | 68 | NAPI_EXTERN void napi_detach_runtime(napi_env env); 69 | 70 | NAPI_EXTERN napi_status napi_runtime_get_threadsafe_function_context( 71 | napi_threadsafe_function func, void** result); 72 | 73 | NAPI_EXTERN napi_status napi_runtime_call_threadsafe_function( 74 | napi_threadsafe_function func, void* data, 75 | napi_threadsafe_function_call_mode is_blocking); 76 | 77 | NAPI_EXTERN napi_status 78 | napi_runtime_delete_threadsafe_function(napi_threadsafe_function func); 79 | 80 | EXTERN_C_END 81 | 82 | #ifdef USE_PRIMJS_NAPI 83 | #include "primjs_napi_undefs.h" 84 | #endif 85 | #endif // SRC_NAPI_ENV_NAPI_RUNTIME_H_ 86 | -------------------------------------------------------------------------------- /src/napi/internal/primjs_napi_undefs.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | #undef napi_addon_register_func 6 | #undef napi_module 7 | #undef napi_add_env_cleanup_hook 8 | #undef napi_remove_env_cleanup_hook 9 | #undef napi_async_execute_callback 10 | #undef napi_async_complete_callback 11 | #undef napi_async_work 12 | #undef napi_threadsafe_function 13 | #undef napi_env 14 | #undef napi_env__ 15 | #undef napi_value 16 | #undef napi_value__ 17 | #undef napi_ref 18 | #undef napi_ref__ 19 | #undef napi_handle_scope 20 | #undef napi_handle_scope__ 21 | #undef napi_escapable_handle_scope 22 | #undef napi_escapable_handle_scope__ 23 | #undef napi_callback_info 24 | #undef napi_callback_info__ 25 | #undef napi_deferred 26 | #undef napi_deferred__ 27 | #undef napi_default 28 | #undef napi_writable 29 | #undef napi_enumerable 30 | #undef napi_configurable 31 | #undef napi_static 32 | #undef napi_default_method 33 | #undef napi_default_jsproperty 34 | #undef napi_property_attributes 35 | #undef napi_undefined 36 | #undef napi_null 37 | #undef napi_boolean 38 | #undef napi_number 39 | #undef napi_string 40 | #undef napi_symbol 41 | #undef napi_object 42 | #undef napi_function 43 | #undef napi_external 44 | #undef napi_bigint 45 | #undef napi_valuetype 46 | #undef napi_int8_array 47 | #undef napi_uint8_array 48 | #undef napi_uint8_clamped_array 49 | #undef napi_int16_array 50 | #undef napi_uint16_array 51 | #undef napi_int32_array 52 | #undef napi_uint32_array 53 | #undef napi_float32_array 54 | #undef napi_float64_array 55 | #undef napi_bigint64_array 56 | #undef napi_biguint64_array 57 | #undef napi_typedarray_type 58 | #undef napi_tsfn_nonblocking 59 | #undef napi_tsfn_blocking 60 | #undef napi_threadsafe_function_call_mode 61 | #undef napi_threadsafe_function_call_js 62 | #undef napi_ok 63 | #undef napi_invalid_arg 64 | #undef napi_object_expected 65 | #undef napi_string_expected 66 | #undef napi_name_expected 67 | #undef napi_function_expected 68 | #undef napi_number_expected 69 | #undef napi_boolean_expected 70 | #undef napi_array_expected 71 | #undef napi_generic_failure 72 | #undef napi_pending_exception 73 | #undef napi_cancelled 74 | #undef napi_escape_called_twice 75 | #undef napi_handle_scope_mismatch 76 | #undef napi_callback_scope_mismatch 77 | #undef napi_queue_full 78 | #undef napi_closing 79 | #undef napi_bigint_expected 80 | #undef napi_date_expected 81 | #undef napi_arraybuffer_expected 82 | #undef napi_detachable_arraybuffer_expected 83 | #undef napi_conflict_instance_data 84 | #undef napi_context_scope_mismatch 85 | #undef napi_status 86 | #undef napi_callback 87 | #undef napi_finalize 88 | #undef napi_property_descriptor 89 | #undef napi_extended_error_info 90 | #undef napi_get_last_error_info 91 | #undef napi_key_include_prototypes 92 | #undef napi_key_own_only 93 | #undef napi_key_collection_mode 94 | #undef napi_key_all_properties 95 | #undef napi_key_writable 96 | #undef napi_key_enumerable 97 | #undef napi_key_configurable 98 | #undef napi_key_skip_strings 99 | #undef napi_key_skip_symbols 100 | #undef napi_key_filter 101 | #undef napi_key_keep_numbers 102 | #undef napi_key_numbers_to_strings 103 | #undef napi_key_conversion 104 | -------------------------------------------------------------------------------- /src/napi/jsc/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | 5 | import("//Primjs.gni") 6 | 7 | config("jsc_config") { 8 | include_dirs = [ "." ] 9 | 10 | cflags_cc = [ 11 | "-Wno-unused-function", 12 | "-Wno-deprecated-declarations", 13 | "-Wno-sign-compare", 14 | ] 15 | defines = [ "PRIMJS_MIN_LOG_LEVEL=5" ] # disable alog in unittests 16 | } 17 | 18 | napi_source_set("jsc") { 19 | sources = [ "js_native_api_JavaScriptCore.cc" ] 20 | public = [ "js_native_api_JavaScriptCore.h" ] 21 | public_configs = [ ":jsc_config" ] 22 | frameworks = [ "JavaScriptCore.framework" ] 23 | } 24 | -------------------------------------------------------------------------------- /src/napi/jsc/napi_env_jsc.h: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | #ifndef SRC_NAPI_JSC_NAPI_ENV_JSC_H_ 6 | #define SRC_NAPI_JSC_NAPI_ENV_JSC_H_ 7 | 8 | #include 9 | 10 | #include "js_native_api.h" 11 | 12 | EXTERN_C_START 13 | 14 | NAPI_EXTERN void napi_attach_jsc(napi_env env, JSGlobalContextRef global_ctx); 15 | 16 | NAPI_EXTERN void napi_detach_jsc(napi_env env); 17 | 18 | NAPI_EXTERN JSGlobalContextRef napi_get_env_context_jsc(napi_env env); 19 | 20 | NAPI_EXTERN JSValueRef napi_js_value_to_jsc_value(napi_env env, 21 | napi_value value); 22 | 23 | NAPI_EXTERN napi_value napi_jsc_value_to_js_value(napi_env env, 24 | JSValueRef value); 25 | 26 | EXTERN_C_END 27 | 28 | #endif // SRC_NAPI_JSC_NAPI_ENV_JSC_H_ 29 | -------------------------------------------------------------------------------- /src/napi/napi_module.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a MIT license that can be 5 | * found in the LICENSE file in the root of the source tree. 6 | */ 7 | // Copyright 2024 The Lynx Authors. All rights reserved. 8 | // Licensed under the Apache License Version 2.0 that can be found in the 9 | // LICENSE file in the root directory of this source tree. 10 | #include "napi_module.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #ifdef USE_PRIMJS_NAPI 16 | #include "primjs_napi_defines.h" 17 | #endif 18 | class spin_lock { 19 | std::atomic_flag locked = ATOMIC_FLAG_INIT; 20 | 21 | public: 22 | void lock() { 23 | while (locked.test_and_set(std::memory_order_acquire)) { 24 | } 25 | } 26 | void unlock() { locked.clear(std::memory_order_release); } 27 | }; 28 | 29 | // do not use std::mutex 30 | // it will increase AAR size by 70kb 31 | static spin_lock mod_lock; 32 | 33 | static napi_module* modlist = nullptr; 34 | 35 | void napi_module_register_xx(napi_module* mod) { 36 | std::lock_guard lock(mod_lock); 37 | 38 | mod->nm_link = modlist; 39 | modlist = mod; 40 | } 41 | 42 | const napi_module* napi_find_module(const char* name) { 43 | std::lock_guard lock(mod_lock); 44 | 45 | for (const napi_module* m = modlist; m; m = m->nm_link) { 46 | if (std::strcmp(name, m->nm_modname) == 0) { 47 | return m; 48 | } 49 | } 50 | return nullptr; 51 | } 52 | -------------------------------------------------------------------------------- /src/napi/napi_module.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a MIT license that can be 5 | * found in the LICENSE file in the root of the source tree. 6 | */ 7 | 8 | // Copyright 2024 The Lynx Authors. All rights reserved. 9 | // Licensed under the Apache License Version 2.0 that can be found in the 10 | // LICENSE file in the root directory of this source tree. 11 | 12 | #ifndef SRC_NAPI_NAPI_MODULE_H_ 13 | #define SRC_NAPI_NAPI_MODULE_H_ 14 | 15 | #include "js_native_api.h" 16 | #ifdef USE_PRIMJS_NAPI 17 | #include "primjs_napi_defines.h" 18 | #endif 19 | typedef napi_value (*napi_addon_register_func)(napi_env env, 20 | napi_value exports); 21 | 22 | #define NAPI_MODULE_VERSION 1 23 | 24 | typedef struct napi_module { 25 | int nm_version; 26 | const char* nm_filename; 27 | napi_addon_register_func nm_register_func; 28 | const char* nm_modname; 29 | struct napi_module* nm_link; 30 | } napi_module; 31 | 32 | #define NAPI_C_CTOR_PRIMJS(fn) \ 33 | void fn(void) __attribute__((constructor)); \ 34 | void fn(void) 35 | 36 | EXTERN_C_START 37 | // do not use `napi_module_register` to prevent symbol conflict with node.js 38 | NAPI_EXTERN void napi_module_register_xx(napi_module* mod); 39 | 40 | NAPI_EXTERN const napi_module* napi_find_module(const char* name); 41 | EXTERN_C_END 42 | 43 | #define NAPI_MODULE_PRIMJS(modname, regfunc) \ 44 | EXTERN_C_START \ 45 | static napi_module _module = {NAPI_MODULE_VERSION, __FILE__, regfunc, \ 46 | #modname, 0}; \ 47 | NAPI_C_CTOR_PRIMJS(_napi_register_xx_##modname) { \ 48 | napi_module_register_xx(&_module); \ 49 | } \ 50 | EXTERN_C_END 51 | 52 | #define NAPI_USE(modname) \ 53 | EXTERN_C_START \ 54 | extern void _napi_register_xx_##modname(void); \ 55 | void* _napi_module_##modname##_p = (void*)&_napi_register_xx_##modname; \ 56 | EXTERN_C_END 57 | #ifdef USE_PRIMJS_NAPI 58 | #include "primjs_napi_undefs.h" 59 | #endif 60 | #endif // SRC_NAPI_NAPI_MODULE_H_ 61 | -------------------------------------------------------------------------------- /src/napi/quickjs/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | 5 | import("//Primjs.gni") 6 | config("config") { 7 | include_dirs = [ "." ] 8 | } 9 | 10 | napi_source_set("native_quickjs") { 11 | sources = [ "js_native_api_QuickJS.cc" ] 12 | public = [ "./napi_env_quickjs.h" ] 13 | public_configs = [ ":config" ] 14 | public_deps = [ "../..:quickjs_lib" ] 15 | } 16 | -------------------------------------------------------------------------------- /src/napi/quickjs/napi_env_quickjs.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a MIT license that can be 5 | * found in the LICENSE file in the root of the source tree. 6 | */ 7 | // Copyright 2024 The Lynx Authors. All rights reserved. 8 | // Licensed under the Apache License Version 2.0 that can be found in the 9 | // LICENSE file in the root directory of this source tree. 10 | #ifndef SRC_NAPI_QUICKJS_NAPI_ENV_QUICKJS_H_ 11 | #define SRC_NAPI_QUICKJS_NAPI_ENV_QUICKJS_H_ 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif // __cplusplus 16 | #include "quickjs/include/quickjs.h" 17 | #ifdef __cplusplus 18 | } 19 | #endif // __cplusplus 20 | 21 | #include "js_native_api.h" 22 | #ifdef USE_PRIMJS_NAPI 23 | #include "primjs_napi_defines.h" 24 | #endif 25 | EXTERN_C_START 26 | 27 | NAPI_EXTERN void napi_attach_quickjs(napi_env env, LEPUSContext* ctx); 28 | 29 | NAPI_EXTERN void napi_detach_quickjs(napi_env env); 30 | 31 | NAPI_EXTERN LEPUSContext* napi_get_env_context_quickjs(napi_env env); 32 | 33 | // return value should be freed by caller 34 | NAPI_EXTERN LEPUSValue napi_js_value_to_quickjs_value(napi_env env, 35 | napi_value value); 36 | 37 | // input value is freed by napi 38 | // return value is only valid in current handle scope 39 | NAPI_EXTERN napi_value napi_quickjs_value_to_js_value(napi_env env, 40 | LEPUSValue value); 41 | 42 | EXTERN_C_END 43 | 44 | #ifdef USE_PRIMJS_NAPI 45 | #include "primjs_napi_undefs.h" 46 | #endif 47 | #endif // SRC_NAPI_QUICKJS_NAPI_ENV_QUICKJS_H_ 48 | -------------------------------------------------------------------------------- /src/napi/v8/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | 5 | import("//Primjs.gni") 6 | config("v8_config") { 7 | defines = [ "JS_ENGINE_V8" ] 8 | include_dirs = [ "//third_party/v8/include" ] 9 | } 10 | 11 | if (target_os == "mac") { 12 | napi_source_set("v8") { 13 | public = [ "napi_env_v8.h" ] 14 | sources = [ "//${napi_src_dir}/v8/js_native_api_v8.cc" ] 15 | public_configs = [ ":v8_config" ] 16 | libs = [ 17 | "//third_party/v8/darwin/lib/libcppgc_base.a", 18 | "//third_party/v8/darwin/lib/libfuzzer_support.a", 19 | "//third_party/v8/darwin/lib/libinspector_fuzzer.a", 20 | "//third_party/v8/darwin/lib/libjson_fuzzer.a", 21 | "//third_party/v8/darwin/lib/libparser_fuzzer.a", 22 | "//third_party/v8/darwin/lib/libregexp_builtins_fuzzer.a", 23 | "//third_party/v8/darwin/lib/libregexp_fuzzer.a", 24 | "//third_party/v8/darwin/lib/libtorque_base.a", 25 | "//third_party/v8/darwin/lib/libtorque_generated_definitions.a", 26 | "//third_party/v8/darwin/lib/libtorque_generated_initializers.a", 27 | "//third_party/v8/darwin/lib/libtorque_ls_base.a", 28 | "//third_party/v8/darwin/lib/libv8_base_without_compiler.a", 29 | "//third_party/v8/darwin/lib/libv8_bigint.a", 30 | "//third_party/v8/darwin/lib/libv8_compiler.a", 31 | "//third_party/v8/darwin/lib/libv8_compiler_for_mksnapshot_source_set.a", 32 | "//third_party/v8/darwin/lib/libv8_heap_base.a", 33 | "//third_party/v8/darwin/lib/libv8_init.a", 34 | "//third_party/v8/darwin/lib/libv8_initializers.a", 35 | "//third_party/v8/darwin/lib/libv8_libbase.a", 36 | "//third_party/v8/darwin/lib/libv8_libplatform.a", 37 | "//third_party/v8/darwin/lib/libv8_monolith.a", 38 | "//third_party/v8/darwin/lib/libv8_snapshot.a", 39 | "//third_party/v8/darwin/lib/libv8_turboshaft.a", 40 | ] 41 | } 42 | } else if (target_os == "linux") { 43 | napi_source_set("v8") { 44 | public = [ "napi_env_v8.h" ] 45 | sources = [ "//${napi_src_dir}/v8/js_native_api_v8.cc" ] 46 | public_configs = [ ":v8_config" ] 47 | libs = [ "//third_party/v8/linux/lib/libv8_monolith.a" ] 48 | } 49 | } else { 50 | napi_source_set("v8") { 51 | sources = [] 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/napi/v8/js_native_api_v8_internals.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a MIT license that can be 5 | * found in the LICENSE file in the root of the source tree. 6 | */ 7 | 8 | // Copyright 2024 The Lynx Authors. All rights reserved. 9 | // Licensed under the Apache License Version 2.0 that can be found in the 10 | // LICENSE file in the root directory of this source tree. 11 | #if !defined(SRC_NAPI_V8_JS_NATIVE_API_V8_INTERNALS_H_) && \ 12 | !defined(_NAPI_V8_EXPORT_SOURCE_ONLY_) 13 | #define SRC_NAPI_V8_JS_NATIVE_API_V8_INTERNALS_H_ 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #define NAPI_ARRAYSIZE(array) (sizeof(array) / sizeof(array[0])) 21 | 22 | inline v8::Local OneByteString(v8::Isolate* isolate, 23 | const char* data, int length) { 24 | return v8::String::NewFromOneByte(isolate, 25 | reinterpret_cast(data), 26 | v8::NewStringType::kNormal, length) 27 | .ToLocalChecked(); 28 | } 29 | 30 | #define NAPI_FIXED_ONE_BYTE_STRING(isolate, string) \ 31 | OneByteString((isolate), (string), sizeof(string) - 1) 32 | 33 | namespace v8impl { 34 | 35 | template 36 | using Persistent = v8::Global; 37 | 38 | class PersistentToLocal { 39 | public: 40 | template 41 | static inline v8::Local Strong( 42 | const v8::PersistentBase& persistent) { 43 | return *reinterpret_cast*>( 44 | const_cast*>(&persistent)); 45 | } 46 | }; 47 | } // end of namespace v8impl 48 | 49 | #ifndef CHECK 50 | #define CHECK(expr) assert(expr) 51 | #endif 52 | 53 | #ifndef CHECK_EQ 54 | #define CHECK_EQ(a, b) CHECK((a) == (b)) 55 | #endif 56 | 57 | #ifndef CHECK_LE 58 | #define CHECK_LE(a, b) CHECK((a) <= (b)) 59 | #endif 60 | 61 | #endif // SRC_NAPI_V8_JS_NATIVE_API_V8_INTERNALS_H_ 62 | -------------------------------------------------------------------------------- /src/napi/v8/napi_env_v8.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 Node.js API collaborators. All Rights Reserved. 3 | * 4 | * Use of this source code is governed by a MIT license that can be 5 | * found in the LICENSE file in the root of the source tree. 6 | */ 7 | 8 | // Copyright 2024 The Lynx Authors. All rights reserved. 9 | // Licensed under the Apache License Version 2.0 that can be found in the 10 | // LICENSE file in the root directory of this source tree. 11 | 12 | #if !defined(SRC_NAPI_V8_NAPI_ENV_V8_H_) && \ 13 | !defined(_NAPI_V8_EXPORT_SOURCE_ONLY_) 14 | #define SRC_NAPI_V8_NAPI_ENV_V8_H_ 15 | 16 | #include 17 | 18 | #include "js_native_api.h" 19 | #ifdef USE_PRIMJS_NAPI 20 | #include "primjs_napi_defines.h" 21 | #endif 22 | NAPI_EXTERN void napi_attach_v8(napi_env env, v8::Local ctx); 23 | 24 | NAPI_EXTERN void napi_detach_v8(napi_env env); 25 | 26 | NAPI_EXTERN v8::Local napi_get_env_context_v8(napi_env env); 27 | 28 | NAPI_EXTERN v8::Local napi_js_value_to_v8_value(napi_env env, 29 | napi_value value); 30 | 31 | NAPI_EXTERN napi_value napi_v8_value_to_js_value(napi_env env, 32 | v8::Local value); 33 | #ifdef USE_PRIMJS_NAPI 34 | #include "primjs_napi_undefs.h" 35 | #endif 36 | #endif // SRC_NAPI_V8_NAPI_ENV_V8_H_ 37 | -------------------------------------------------------------------------------- /testing/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | 5 | # Targets needed for isolate script to execute. 6 | group("testing") { 7 | testonly = true 8 | deps = [ 9 | "napi:napi_unittest", 10 | "quickjs:run_test262", 11 | "quickjs/compiler:quickjs_unittest", 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /testing/napi/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | import("//testing/test.gni") 5 | 6 | unittest_set("napi_testset") { 7 | defines = [ "JS_ENGINE_QJS" ] 8 | public_deps = [ "../../src/napi:napi_v8" ] 9 | deps = [ 10 | "../../src/napi:napi", 11 | "../../src/napi:napi_env", 12 | "../../src/napi:napi_quickjs", 13 | "../../src/napi:napi_runtime", 14 | ] 15 | if (target_os == "mac") { 16 | defines += [ "JS_ENGINE_JSC" ] 17 | deps += [ "../../src/napi:napi_jsc" ] 18 | } 19 | public = [ "testlib.h" ] 20 | sources = [ "testlib.cc" ] 21 | } 22 | 23 | unit_test("napi_unittest") { 24 | testonly = true 25 | 26 | sources = [] 27 | 28 | public_deps = [ ":napi_testset" ] 29 | } 30 | -------------------------------------------------------------------------------- /testing/napi/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 [Node.js API collaborators](https://github.com/nodejs/node-addon-api#collaborators) 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /testing/napi/benchmark.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "testlib.h" 4 | 5 | class NAPIBench : public test::NAPITestBase {}; 6 | 7 | static int METHOD_COUNT = 30; 8 | static int CALL_COUNT = 1e6; 9 | 10 | TEST_P(NAPIBench, PureJS_1e6) { 11 | Napi::HandleScope hscope(env); 12 | 13 | auto fun = env.RunScript( 14 | "(function bench(methodCount, count) {" 15 | " function BenchObject() {" 16 | " this.num = 0;" 17 | " }" 18 | " for (let i = 0; i < methodCount; i++) {" 19 | " BenchObject.prototype['method' + i] = " 20 | "function (a) { if (typeof a !== 'number') return 0; " 21 | "this.num++; return this.num + a; };" 22 | " }" 23 | " const obj = new BenchObject();" 24 | " for (let i = 0; i < count; i++) {" 25 | " obj.method10(10);" 26 | " }" 27 | "})") 28 | .As(); 29 | 30 | fun.Call({Napi::Number::New(env, METHOD_COUNT), 31 | Napi::Number::New(env, CALL_COUNT)}); 32 | } 33 | 34 | TEST_P(NAPIBench, JS_ClassStyle_1e6) { 35 | Napi::HandleScope hscope(env); 36 | 37 | std::string script = 38 | "(function bench(methodCount, count) {" 39 | " class BenchObject {" 40 | " constructor() { this.num = 0; }"; 41 | for (int i = 0; i < METHOD_COUNT; i++) { 42 | script += " method" + std::to_string(i) + 43 | "(a) { if (typeof a !== 'number') return 0; " 44 | "this.num++; return this.num + a; }"; 45 | } 46 | script += 47 | "}" 48 | " const obj = new BenchObject();" 49 | " for (let i = 0; i < count; i++) {" 50 | " obj.method10(10);" 51 | " }" 52 | "})"; 53 | auto fun = env.RunScript(script.c_str()).As(); 54 | 55 | fun.Call({Napi::Number::New(env, METHOD_COUNT), 56 | Napi::Number::New(env, CALL_COUNT)}); 57 | } 58 | 59 | TEST_P(NAPIBench, ObjectWrap_1e6) { 60 | class BenchObject : public Napi::ScriptWrappable { 61 | public: 62 | BenchObject(const Napi::CallbackInfo& info) {} 63 | 64 | Napi::Value Method(const Napi::CallbackInfo& info) { 65 | auto val = info[0]; 66 | if (!val.IsNumber()) { 67 | return Napi::Number::New(info.Env(), 0); 68 | } 69 | num++; 70 | return Napi::Number::New(info.Env(), 71 | val.As().Uint32Value() + num); 72 | } 73 | 74 | int num = 0; 75 | 76 | static Napi::Function Create(Napi::Env env, size_t methodCount) { 77 | using Wrapped = Napi::ObjectWrap; 78 | std::vector names(methodCount); 79 | std::vector props; 80 | 81 | for (size_t i = 0; i < methodCount; i++) { 82 | names[i] = std::string("method") + std::to_string(i); 83 | props.push_back( 84 | Wrapped::InstanceMethod(names[i].c_str(), &BenchObject::Method)); 85 | } 86 | return Wrapped::DefineClass(env, "BenchObject", props).Get(env); 87 | } 88 | }; 89 | 90 | Napi::HandleScope hscope(env); 91 | 92 | auto fun = env.RunScript( 93 | "(function bench(BenchObject, count) {" 94 | " const obj = new BenchObject();" 95 | " for (let i = 0; i < count; i++) {" 96 | " obj.method1(10);" 97 | " }" 98 | "})") 99 | .As(); 100 | auto BenchObjectClass = BenchObject::Create(env, METHOD_COUNT); 101 | 102 | fun.Call({BenchObjectClass, Napi::Number::New(env, CALL_COUNT)}); 103 | } 104 | 105 | INSTANTIATE_TEST_SUITE_P( 106 | EngineTest, NAPIBench, ::testing::ValuesIn(test::runtimeFactory), 107 | [](const testing::TestParamInfo& info) { 108 | return std::get<0>(info.param); 109 | }); 110 | -------------------------------------------------------------------------------- /testing/quickjs/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | 5 | import("//testing/test.gni") 6 | executable("run_test262") { 7 | testonly = true 8 | sources = [ "run-test262.cc" ] 9 | 10 | public_deps = [ "../../src:quickjs_lib" ] 11 | 12 | data_deps = [ 13 | ":test262_conf_copy", 14 | ":test262_sources", 15 | ] 16 | 17 | defines = [ "QJS_UNITTEST" ] 18 | 19 | # FIXME():run-test262.c:444:33: error: incompatible pointer 20 | # types passing 'int *' to parameter of type 'size_t *' 21 | # (aka 'unsigned long *') [-Werror,-Wincompatible-pointer-types] 22 | # cflags = [ 23 | # "-Wno-incompatible-pointer-types", 24 | # "-Wno-c99-designator" 25 | # ] 26 | } 27 | 28 | # copy test262 into the $root_build_dir/quickjs_test/test262 29 | copy("test262_sources") { 30 | sources = [ "//third_party/test262" ] 31 | outputs = [ "$root_build_dir/quickjs_test/test262" ] 32 | } 33 | 34 | # the test262.config will copy into 35 | # "$root_out_dir/quickjs_test/{{source_file_part}}" 36 | copy("test262_conf_copy") { 37 | sources = [ 38 | "test262.conf", 39 | "test262_errors.txt", 40 | ] 41 | outputs = [ "$root_out_dir/quickjs_test/{{source_file_part}}" ] 42 | } 43 | -------------------------------------------------------------------------------- /testing/quickjs/compiler/BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | 5 | import("//Primjs.gni") 6 | import("//testing/test.gni") 7 | 8 | copy("test_cases_copy") { 9 | sources = [ "unit_test" ] 10 | outputs = [ "$root_out_dir/qjs_tests/{{source_file_part}}" ] 11 | } 12 | 13 | copy("quickjs_test_cases_copy") { 14 | sources = [ 15 | "common_test", 16 | "finalization_registry_test", 17 | "qjs_debug_test", 18 | "structuredClone", 19 | "unit_test", 20 | "weak_ref_test", 21 | ] 22 | outputs = [ "$root_out_dir/quick_tests/{{source_file_part}}" ] 23 | } 24 | 25 | test_case_dir = rebase_path("$root_out_dir/quick_tests/", "//") 26 | 27 | config("config") { 28 | include_dirs = [ 29 | ".", 30 | "../../../src/interpreter", 31 | "../../../src/inspector", 32 | "${root_gen_dir}", 33 | ] 34 | 35 | defines = [ "TEST_CASE_DIR=\"${test_case_dir}\"" ] 36 | } 37 | 38 | unittest_set("quickjs_testset") { 39 | public_configs = [ ":config" ] 40 | public_deps = [ "../../../src:quickjs_lib" ] 41 | 42 | sources = [ 43 | "test_common.cc", 44 | "test_finalization_registry.cc", 45 | "test_parse_program.cc", 46 | "test_primjs_version.cc", 47 | "test_promise_rejection.cc", 48 | "test_weak_ref.cc", 49 | ] 50 | cflags = [ "-Wno-c99-designator" ] 51 | data_deps = [ ":quickjs_test_cases_copy" ] 52 | } 53 | 54 | unit_test("quickjs_unittest") { 55 | testonly = true 56 | 57 | sources = [] 58 | 59 | public_deps = [ ":quickjs_testset" ] 60 | data_deps = [ ":test_cases_copy" ] 61 | } 62 | 63 | unittest_set("qjs_debug_testset") { 64 | public_configs = [ ":config" ] 65 | public_deps = [ "../../../src:quickjs_lib" ] 66 | 67 | sources = [ 68 | "test-heap-profiler.cc", 69 | "test_debug_base.cc", 70 | "test_debug_common.cc", 71 | "test_debug_complex_properties.cc", 72 | "test_debug_parse_script_flag.cc", 73 | "test_debug_pause.cc", 74 | "test_debug_step.cc", 75 | "test_shared_context_debug.cc", 76 | ] 77 | defines = [ 78 | "LEPUS_PC", 79 | "LEPUS_TEST", 80 | "ENABLE_CLI = 1", 81 | ] 82 | 83 | cflags = [ "-Wno-c99-designator" ] 84 | data_deps = [ ":quickjs_test_cases_copy" ] 85 | } 86 | 87 | unit_test("qjs_debug_test") { 88 | testonly = true 89 | sources = [] 90 | public_deps = [ ":qjs_debug_testset" ] 91 | } 92 | -------------------------------------------------------------------------------- /testing/quickjs/compiler/common_test/wchar_rejection.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | Promise.reject("测试©å˙ç˙√π˜∂"); -------------------------------------------------------------------------------- /testing/quickjs/compiler/finalization_registry_test/wrong_param_fg_test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | let cleanup; 6 | let fg = new FinalizationRegistry(cleanup); 7 | -------------------------------------------------------------------------------- /testing/quickjs/compiler/qjs_debug_test/qjs_debug_test1.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | function test() { 6 | let a = 1; 7 | console.log("hello"); 8 | a = a + 1; 9 | console.log(a); 10 | return true; 11 | } 12 | 13 | test(); -------------------------------------------------------------------------------- /testing/quickjs/compiler/test_debug_parse_script_flag.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | #include "test_debug_base.h" 5 | 6 | namespace qjs_debug_test { 7 | class QjsSharedDebugFlag : public ::testing::Test { 8 | protected: 9 | QjsSharedDebugFlag() = default; 10 | ~QjsSharedDebugFlag() override = default; 11 | 12 | void SetUp() override { 13 | QjsDebugQueue::GetReceiveMessageQueue() = std::queue(); 14 | QjsDebugQueue::GetSendMessageQueue() = std::queue(); 15 | rt_ = LEPUS_NewRuntime(); 16 | auto funcs = GetQJSCallbackFuncs(); 17 | ctx_ = LEPUS_NewContext(rt_); 18 | PrepareQJSDebuggerForSharedContext(ctx_, funcs.data(), funcs.size(), true); 19 | PrepareQJSDebuggerForSharedContext(ctx_, funcs.data(), funcs.size(), true); 20 | QJSDebuggerInitialize(ctx_); 21 | } 22 | 23 | void TearDown() override { 24 | auto info = GetDebuggerInfo(ctx_); 25 | auto* mq = GetDebuggerMessageQueue(info); 26 | while (!QueueIsEmpty(mq)) { 27 | char* message_str = GetFrontQueue(mq); 28 | free(message_str); 29 | message_str = NULL; 30 | } 31 | QJSDebuggerFree(ctx_); 32 | LEPUS_FreeContext(ctx_); 33 | LEPUS_FreeRuntime(rt_); 34 | } 35 | 36 | LEPUSContext* ctx_; 37 | LEPUSRuntime* rt_; 38 | }; 39 | 40 | TEST_F(QjsSharedDebugFlag, TESTParseScriptFlag) { 41 | QjsDebugQueue::GetSendMessageQueue().push( 42 | "{\"id\":0,\"method\":\"Debugger.enable\",\"params\":{" 43 | "\"maxScriptsCacheSize\":100000000}}"); 44 | QjsDebugQueue::GetSendMessageQueue().push( 45 | "{\"id\":1,\"method\":\"Debugger.getScriptSource\",\"params\":{" 46 | "\"scriptId\":1}}"); 47 | 48 | int eval_flags; 49 | const char* buf = 50 | "function test() {\n lynxConsole.log('lynx_runtimeId:1, hahaha'); " 51 | "lynxConsole.log('lynx_runtimeId:2,hehehe');\n}\n test();\n"; 52 | eval_flags = LEPUS_EVAL_TYPE_GLOBAL; 53 | LEPUSValue ret = 54 | LEPUS_Eval(ctx_, buf, strlen(buf), "test_lynxConsole.js", eval_flags); 55 | if (!ctx_->rt->gc_enable) LEPUS_FreeValue(ctx_, ret); 56 | QjsDebugQueue::GetSendMessageQueue().push( 57 | "{\"id\":2,\"method\":\"Runtime.enable\",\"params\":{}}"); 58 | buf = "function test() {} \n test();"; 59 | ret = LEPUS_Eval(ctx_, buf, strlen(buf), "trigger_debugger.js", eval_flags); 60 | if (!ctx_->rt->gc_enable) LEPUS_FreeValue(ctx_, ret); 61 | } 62 | } // namespace qjs_debug_test 63 | -------------------------------------------------------------------------------- /testing/quickjs/compiler/test_parse_program.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | #include 6 | 7 | #include "gtest/gtest.h" 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | #include "quickjs/include/quickjs-libc.h" 13 | #include "quickjs/include/quickjs.h" 14 | #ifdef __cplusplus 15 | } 16 | #endif 17 | #include "quickjs/include/quickjs-inner.h" 18 | 19 | TEST(QjsCompiler, Parse) { 20 | std::string src = R"(let arr = [1, 2, 3]; 21 | arr.length = 10; 22 | console.log(arr.splice(1, 1)); 23 | console.log(arr); 24 | )"; 25 | 26 | auto *rt = LEPUS_NewRuntime(); 27 | auto *ctx = LEPUS_NewContext(rt); 28 | 29 | LEPUSValue ret = LEPUS_Eval(ctx, src.c_str(), src.length(), "", 30 | LEPUS_EVAL_FLAG_COMPILE_ONLY); 31 | ASSERT_TRUE(LEPUS_VALUE_GET_TAG(ret) != LEPUS_TAG_EXCEPTION); 32 | 33 | if (!ctx->rt->gc_enable) LEPUS_FreeValue(ctx, ret); 34 | LEPUS_FreeContext(ctx); 35 | LEPUS_FreeRuntime(rt); 36 | } 37 | -------------------------------------------------------------------------------- /testing/quickjs/compiler/unit_test/async_closure_gc.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | (async function run() { 6 | let obj = { test_prop: true }; 7 | 8 | let done = () => { 9 | console.log("before obj"); 10 | obj; 11 | console.log("after obj"); 12 | }; 13 | 14 | 15 | Promise.resolve().then(done); 16 | 17 | const p = new Promise(() => {}); 18 | 19 | console.log("before await"); 20 | await p; 21 | console.log("after await"); 22 | })(); 23 | // force gc 24 | gc(); 25 | -------------------------------------------------------------------------------- /testing/quickjs/compiler/unit_test/debugger.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | function test_debugger_keyword() { 5 | let a = true; 6 | if(a) { 7 | debugger; 8 | } 9 | } 10 | 11 | test_debugger_keyword(); 12 | 13 | 14 | function test_debugger_keyword2() { 15 | for(let i = 0; i < 10; i++) { 16 | debugger; 17 | } 18 | } 19 | test_debugger_keyword2(); -------------------------------------------------------------------------------- /testing/quickjs/compiler/unit_test/demo_test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | let a = 0; 6 | console.log("hello, world"); 7 | Assert(a == 0); 8 | -------------------------------------------------------------------------------- /testing/quickjs/compiler/unit_test/es6.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | 6 | (function() { 7 | var proxied = function () { 8 | }; 9 | var passed = false; 10 | new new Proxy(proxied, { 11 | construct: function (t, args) { 12 | passed = t === proxied && args + "" === "foo,bar"; 13 | return {}; 14 | } 15 | })("foo", "bar"); 16 | Assert(passed); 17 | })(); 18 | 19 | 20 | (function() { 21 | var passed = false; 22 | new Proxy({}, {}); 23 | // A Proxy exotic object only has a [[Construct]] internal method if the 24 | // initial value of its [[ProxyTarget]] internal slot is an object 25 | // that has a [[Construct]] internal method. 26 | try { 27 | new new Proxy({}, { 28 | construct: function (t, args) { 29 | return {}; 30 | } 31 | })(); 32 | Assert(false); 33 | } catch (e) { 34 | Assert(true); 35 | } 36 | })(); 37 | 38 | // The result of [[Construct]] must be an Object. 39 | (function() { 40 | var passed = false; 41 | try { 42 | new new Proxy(function () { 43 | }, { 44 | construct: function (t, args) { 45 | passed = true; 46 | return 5; 47 | } 48 | })(); 49 | } catch (e) { 50 | } 51 | Assert(passed); 52 | })(); 53 | 54 | Assert(Math.hypot() === 0 && 55 | Math.hypot(1) === 1 && 56 | Math.hypot(9, 12, 20) === 25 && 57 | Math.hypot(27, 36, 60, 100) === 125); 58 | 59 | 60 | var a = [...[,,]]; 61 | Assert("0" in a && "1" in a && '' + a[0] + a[1] === "undefinedundefined"); 62 | 63 | 64 | 65 | function correctProtoBound(proto) { 66 | var f = function(){}; 67 | Object.setPrototypeOf(f, proto); 68 | var boundF = Function.prototype.bind.call(f, null); 69 | //console.log(boundF); 70 | return Object.getPrototypeOf(boundF) === proto; 71 | } 72 | Assert(correctProtoBound(Function.prototype) 73 | && correctProtoBound({}) 74 | && correctProtoBound(null)); 75 | 76 | 77 | 78 | function correctProtoBound1(proto) { 79 | var f = function*(){}; 80 | if (Object.setPrototypeOf) { 81 | Object.setPrototypeOf(f, proto); 82 | } else { 83 | f.__proto__ = proto; 84 | } 85 | var boundF = Function.prototype.bind.call(f, null); 86 | return Object.getPrototypeOf(boundF) === proto; 87 | } 88 | Assert(correctProtoBound1(Function.prototype) 89 | && correctProtoBound1({}) 90 | && correctProtoBound1(null)); 91 | 92 | function correctProtoBound2(proto) { 93 | var f = ()=>5; 94 | if (Object.setPrototypeOf) { 95 | Object.setPrototypeOf(f, proto); 96 | } else { 97 | f.__proto__ = proto; 98 | } 99 | var boundF = Function.prototype.bind.call(f, null); 100 | return Object.getPrototypeOf(boundF) === proto; 101 | } 102 | Assert(correctProtoBound2(Function.prototype) 103 | && correctProtoBound2({}) 104 | && correctProtoBound2(null)); 105 | 106 | 107 | function correctProtoBound3(proto) { 108 | class C {} 109 | if (Object.setPrototypeOf) { 110 | Object.setPrototypeOf(C, proto); 111 | } else { 112 | C.__proto__ = proto; 113 | } 114 | var boundF = Function.prototype.bind.call(C, null); 115 | return Object.getPrototypeOf(boundF) === proto; 116 | } 117 | Assert(correctProtoBound3(Function.prototype) 118 | && correctProtoBound3({}) 119 | && correctProtoBound3(null)); 120 | 121 | function correctProtoBound4(superclass) { 122 | class C extends superclass { 123 | constructor() { 124 | return Object.create(null); 125 | } 126 | } 127 | var boundF = Function.prototype.bind.call(C, null); 128 | return Object.getPrototypeOf(boundF) === Object.getPrototypeOf(C); 129 | } 130 | Assert(correctProtoBound4(function(){}) 131 | && correctProtoBound4(Array) 132 | && correctProtoBound4(null)); 133 | 134 | 135 | Assert(/x{1/.exec("x{1")[0] === "x{1" && /x]1/.exec("x]1")[0] === "x]1"); -------------------------------------------------------------------------------- /testing/quickjs/compiler/unit_test/finalization_registry_test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | function test_FinalizationRegistryConstructorCallAsFunction() { 6 | let caught = false; 7 | let message = ""; 8 | try { 9 | let f = FinalizationRegistry(() => {}); 10 | } catch (e) { 11 | message = e.message; 12 | caught = true; 13 | } finally { 14 | Assert(caught == true); 15 | } 16 | } 17 | 18 | function test_UnregisterWithNonExistentKey() { 19 | let fg = new FinalizationRegistry(() => {}); 20 | let success = fg.unregister({"k": "whatever"}); 21 | Assert(success == false); 22 | } 23 | 24 | function test_FinalizationRegistry() { 25 | let cleanup_called = false; 26 | let cleanup = function(holdings) { 27 | let holdings_list = []; 28 | holdings_list.push(holdings); 29 | cleanup_called = true; 30 | } 31 | 32 | let fg = new FinalizationRegistry(cleanup); 33 | (function() { 34 | let o = {}; 35 | fg.register(o, "holdings"); 36 | })(); 37 | 38 | Assert(cleanup_called == true); 39 | } 40 | 41 | function test_UnregisterTwice() { 42 | let cleanup_call_count = 0; 43 | let cleanup = function(holdings) { 44 | ++cleanup_call_count; 45 | } 46 | let fg = new FinalizationRegistry(cleanup); 47 | let key = {"k": "this is the key"}; 48 | 49 | let object = {}; 50 | fg.register(object, "holdings", key); 51 | 52 | let success = fg.unregister(key); 53 | Assert(success == true); 54 | 55 | success = fg.unregister(key); 56 | Assert(success == false); 57 | } 58 | 59 | test_FinalizationRegistryConstructorCallAsFunction(); 60 | test_UnregisterWithNonExistentKey(); 61 | // test_FinalizationRegistry(); 62 | test_UnregisterTwice(); 63 | -------------------------------------------------------------------------------- /testing/quickjs/compiler/unit_test/force_gc.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | function test_force_gc() { 6 | for (var t = 0; i < 10000; i++) { 7 | var ab = new Int8Array(32 * 1024 * 1024); 8 | for (var i = 0; i < 32 * 1024 * 1024; i++) { 9 | ab[i] = i; 10 | } 11 | } 12 | } 13 | 14 | test_force_gc(); 15 | -------------------------------------------------------------------------------- /testing/quickjs/compiler/unit_test/json_parse.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | try { 6 | let str = "¢"; 7 | let str1 = "\"" + str; 8 | JSON.parse(str1); 9 | } catch(e) { 10 | console.log(e); 11 | } -------------------------------------------------------------------------------- /testing/quickjs/compiler/unit_test/object_get_set_property_test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | let get = 10; 6 | let obj = { 7 | get, 8 | }; 9 | -------------------------------------------------------------------------------- /testing/quickjs/compiler/unit_test/prototype_test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | function b(e, t, r) { 6 | return t && k(e.prototype, t), r && k(e, r), Object.defineProperty(e, "prototype", { 7 | writable: 1, 8 | }), e 9 | } 10 | 11 | function y(e, t) { 12 | if (!(e instanceof t)) throw new TypeError("Cannot call a class as a function") 13 | console.log(e.prototype); 14 | } 15 | 16 | var gs = b((function e() { 17 | var t = this; 18 | y(this, e); 19 | })); 20 | 21 | var bs = new gs; -------------------------------------------------------------------------------- /testing/quickjs/compiler/unit_test/rejection_reason_object.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | function setTimeout(callback, delay) { 6 | const start = new Date().getTime(); 7 | 8 | while (new Date().getTime() < start + delay); 9 | 10 | callback(); 11 | } 12 | 13 | function Person(name, age) { 14 | this.name = name; 15 | this.age = age; 16 | 17 | this.greet = function() { 18 | console.log('Hello!'); 19 | }; 20 | } 21 | 22 | const person = new Person('John', 30); 23 | 24 | function fetchData() { 25 | return new Promise((resolve, reject) => { 26 | 27 | setTimeout(() => { 28 | 29 | const isError = true; 30 | 31 | if (isError) { 32 | reject(); 33 | } else { 34 | resolve('Get Data Success'); 35 | } 36 | }, 2000); 37 | }); 38 | } 39 | 40 | function handleRejection(error) { 41 | return new Promise((resolve, reject) => { 42 | 43 | setTimeout(() => { 44 | console.log('Async Process Rejected:', error.message); 45 | resolve(); 46 | }, 1000); 47 | }); 48 | } 49 | 50 | fetchData() 51 | .then((data) => { 52 | console.log('Success:', data); 53 | }); 54 | 55 | -------------------------------------------------------------------------------- /testing/quickjs/compiler/unit_test/unhandled_register_test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | foo = () => { 6 | return new Promise((_, reject) => { 7 | reject("NO"); 8 | }); 9 | }; 10 | 11 | foo(); 12 | 13 | console.log("first"); 14 | 15 | bar = () => { 16 | return new Promise((_, reject) => reject(new Error("woops"))).then(() => { 17 | console.log("fufill"); 18 | }); 19 | }; 20 | 21 | bar(); 22 | 23 | console.log("second"); 24 | 25 | 26 | -------------------------------------------------------------------------------- /testing/quickjs/compiler/unit_test/weak_ref_test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | function test_NewWeakRef() { 6 | let obj = { key: 'val' }; 7 | let weakRef = new WeakRef(obj); 8 | Assert(weakRef.deref() == obj); 9 | } 10 | 11 | function test_NewWeakRefWithTwoParam() { 12 | let obj = {"key": "val"}; 13 | let obj2 = {"key2": "val2"}; 14 | let weakRef = new WeakRef(obj, obj2); 15 | Assert(weakRef.deref() == obj); 16 | } 17 | 18 | function test_ExpandReferenceRecord() { 19 | let obj = { key: 'val' }; 20 | let weakRef1 = new WeakRef(obj); 21 | let weakRef2 = new WeakRef(obj); 22 | let weakRef3 = new WeakRef(obj); 23 | let weakRef4 = new WeakRef(obj); 24 | let weakRef5 = new WeakRef(obj); 25 | Assert(weakRef5.deref() == obj); 26 | } 27 | 28 | function test_UndefAfterFree() { 29 | var weakRef; 30 | function SetWeakRef() { 31 | let obj = { key: 'val' }; 32 | weakRef = new WeakRef(obj); 33 | } // free obj 34 | 35 | SetWeakRef(); 36 | Assert(weakRef.deref() == undefined); 37 | } 38 | 39 | function test_WeakRefFreeFirst() { 40 | let obj = { key: 'val' }; 41 | function freeRef() { 42 | let weakRef = new WeakRef(obj); 43 | } // free ref 44 | 45 | freeRef(); 46 | console.log(obj); 47 | } 48 | 49 | test_NewWeakRef(); 50 | test_NewWeakRefWithTwoParam(); 51 | test_ExpandReferenceRecord(); 52 | // test_UndefAfterFree(); 53 | test_WeakRefFreeFirst(); 54 | -------------------------------------------------------------------------------- /testing/quickjs/compiler/weak_ref_test/wrong_param_weakref_test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | let obj; 6 | let weakRef = new WeakRef(obj); 7 | -------------------------------------------------------------------------------- /testing/quickjs/compiler/weak_ref_test/zero_param_weakref_test.js: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The Lynx Authors. All rights reserved. 2 | // Licensed under the Apache License Version 2.0 that can be found in the 3 | // LICENSE file in the root directory of this source tree. 4 | 5 | let obj = { key: "val" }; 6 | let weakRef = new WeakRef(); 7 | -------------------------------------------------------------------------------- /testing/quickjs/run_test262.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | 5 | #!/use/bin/python 6 | import subprocess 7 | import sys 8 | from threading import Thread, Lock 9 | import json 10 | import argparse 11 | 12 | dirs = "." 13 | lock = Lock() 14 | result = [] 15 | args = "" 16 | 17 | def readTests(args): 18 | # remove useless case 19 | cmd = "find ./test -name '*.fail' |xargs rm" 20 | subprocess.getstatusoutput(cmd) 21 | 22 | cmd = "test262-harness --help" 23 | status, output = subprocess.getstatusoutput(cmd) 24 | if status == 0: 25 | subprocess.getstatusoutput("npm install -g test262-harness") 26 | cmd = "find %s -name '*.js'" % args.test262Dir 27 | status, output = subprocess.getstatusoutput(cmd) 28 | return output 29 | 30 | def run_test(tests): 31 | global args 32 | cmd = "test262-harness --reporter json --reporter-keys result,file --hostType %s --hostPath %s" % (args.type, args.bin) 33 | print(("run_test: %d" % len(tests))) 34 | success = 0 35 | runned = 0 36 | global result 37 | localResult = [] 38 | for test in tests: 39 | cmd = cmd + " " + test + " " 40 | 41 | status, output = subprocess.getstatusoutput(cmd) 42 | retArray = (output.strip("[\n").strip("]\n").split("\n,")) 43 | 44 | for ret in retArray: 45 | try: 46 | ret = json.loads(ret) 47 | localResult.append(str(ret["result"]["pass"]) + " " + ret["file"] + "\n") 48 | except: 49 | print(ret) 50 | 51 | lock.acquire() 52 | result.extend(localResult) 53 | lock.release() 54 | print("finish_test: %d" % len(tests)) 55 | 56 | def main(): 57 | parser = argparse.ArgumentParser(description="To run test262 cases") 58 | parser.add_argument("--test262Dir", help="test262 root dirctory", type=str, default=".") 59 | parser.add_argument("--type", help="host vm type example d8/node/qjs", type=str) 60 | parser.add_argument("--bin", help="vm execute path", type=str) 61 | parser.add_argument("--output", help="result output file", type=str, default="./result.output") 62 | parser.add_argument("--t", help="thread count to run", type=int, default=5) 63 | 64 | 65 | global args 66 | args = parser.parse_args() 67 | 68 | with open(args.output, "w") as f: 69 | f = f 70 | # find all tests 71 | tests = readTests(args).split("\n") 72 | totalCount = len(tests) 73 | print("Total case: %d" % totalCount) 74 | 75 | thread_lists = [] 76 | thread_count = args.t 77 | 78 | prethread = totalCount / thread_count 79 | # use 10 thread to run tests 80 | for i in range(0, thread_count): 81 | t = Thread(target=run_test, args=(tests[i*prethread : (i+1) * prethread ],)) 82 | thread_lists.append(t) 83 | t.start() 84 | 85 | for t in thread_lists: 86 | t.join() 87 | 88 | if prethread * thread_count < totalCount: 89 | run_test(tests[prethread * thread_count :]) 90 | 91 | # write to file 92 | global result 93 | print("Run case: %d" % len(result)) 94 | with open(args.output, "a") as f: 95 | for line in result: 96 | f.write(line) 97 | 98 | if __name__=="__main__": 99 | main() 100 | -------------------------------------------------------------------------------- /testing/quickjs/test-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2024 The Lynx Authors. All rights reserved. 3 | # Licensed under the Apache License Version 2.0 that can be found in the 4 | # LICENSE file in the root directory of this source tree. 5 | gn gen out/Default --args="enable_unittests=true is_asan=true is_ubsan=true use_clang_coverage = true" 6 | ninja -C out/Default -j32 qjs 7 | -------------------------------------------------------------------------------- /tools/ci/check_test_run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2020 The Lynx Authors. All rights reserved. 3 | # Licensed under the Apache License Version 2.0 that can be found in the 4 | # LICENSE file in the root directory of this source tree. 5 | """ 6 | This script checks unit test results 7 | """ 8 | import os 9 | import subprocess 10 | import sys 11 | 12 | test_case = ["", "_snapshot"] 13 | 14 | 15 | def CheckUnitTestRun(): 16 | print("Check unittest cases...") 17 | cwd = os.getcwd() 18 | for case in test_case: 19 | info = "" 20 | if case == "": 21 | info = "quickjs" 22 | else: 23 | info = "primjs" + case 24 | print("{}: Check quickjs test cases...".format(info)) 25 | binary_dir = "./out/Default{}/".format(case) 26 | os.environ["LLVM_PROFILE_FILE"] = binary_dir + "qjs.profraw" 27 | unittest_cases = [ 28 | "qjs_debug_test", 29 | "quickjs_unittest", 30 | "napi_unittest", 31 | ] 32 | for unittest in unittest_cases: 33 | print("{}: Check %s cases...".format(info) % unittest) 34 | os.environ["LLVM_PROFILE_FILE"] = ( 35 | binary_dir + "%s.profraw" % unittest 36 | ) 37 | output = subprocess.run( 38 | [ 39 | binary_dir + unittest, 40 | ], 41 | stderr=sys.stderr, 42 | stdout=sys.stdout, 43 | text=True, 44 | check=True, 45 | ) 46 | 47 | # run js test using qjs 48 | output = subprocess.run( 49 | ["python3", "tools/ci/run_quickjs_unittests.py", "-b", binary_dir], 50 | text=True, 51 | stdout=sys.stdout, 52 | stderr=sys.stderr, 53 | check=True, 54 | ) 55 | # run test262 56 | output = subprocess.run( 57 | [ 58 | binary_dir + "run_test262", 59 | "-m", 60 | "-a", 61 | "-c", 62 | binary_dir + "quickjs_test/test262.conf", 63 | ], 64 | text= True, 65 | stdout= sys.stdout, 66 | stderr= sys.stderr, 67 | check=True, 68 | ) 69 | print("Congratulations! All %s are passed.\n" % unittest) 70 | 71 | 72 | def main(): 73 | CheckUnitTestRun() 74 | 75 | if __name__ == "__main__": 76 | sys.exit(main()) 77 | -------------------------------------------------------------------------------- /tools/ci/run_quickjs_unittests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2024 The Lynx Authors. All rights reserved. 3 | # Licensed under the Apache License Version 2.0 that can be found in the 4 | # LICENSE file in the root directory of this source tree. 5 | import subprocess 6 | import sys 7 | import os 8 | import time 9 | import optparse 10 | 11 | primjs_root_dir = sys.path[0] + "/../.." 12 | qjs = primjs_root_dir + "/out/Default/qjs" 13 | tests = primjs_root_dir + "/out/Default/qjs_tests" 14 | total_case = 0 15 | total_passed = 0 16 | 17 | def readFile(dirName): 18 | files = os.listdir(dirName) 19 | result_list = [] 20 | for f in files: 21 | abs_file = os.path.join(dirName, f) 22 | result_list.append(abs_file) 23 | if os.path.isdir(abs_file): 24 | for actual_files in readFile(abs_file): 25 | result_list.append(actual_files) 26 | 27 | return result_list 28 | 29 | def runCommand(f, flags): 30 | # # !!! TODO: skip local_variables.js 31 | if f.endswith("local_variables.js"): 32 | print("skip local_variables.js") 33 | return 0 34 | returncode = 0 35 | output = None 36 | timeout = False 37 | try: 38 | output = subprocess.run( 39 | [qjs, f], 40 | timeout=30, 41 | stdout=subprocess.PIPE, 42 | stderr=subprocess.PIPE, 43 | check=True, 44 | ) 45 | print(output.stdout.decode(), output.stderr.decode()) 46 | except subprocess.TimeoutExpired: 47 | timeout = True 48 | returncode = -1 49 | except subprocess.CalledProcessError as e: 50 | returncode = e.returncode 51 | output = e.output 52 | print("return code:{}, output: {}".format(returncode, output)) 53 | if returncode == 0: 54 | return 0 55 | 56 | if timeout: 57 | print("TIMEOUT") 58 | else: 59 | for line in output: 60 | print(line) 61 | print("exit code %d CRASHED" % returncode) 62 | return -1 63 | 64 | 65 | def runCase(file, default_flags): 66 | global total_case 67 | global total_passed 68 | total_case = total_case + 1 69 | returncode = runCommand(file, default_flags) 70 | if returncode == 0: 71 | total_passed = total_passed + 1 72 | 73 | 74 | def Main(): 75 | parser = optparse.OptionParser() 76 | parser.add_option( 77 | "-b", 78 | "--binary", 79 | default="out/Default", 80 | help="The binary directory, default is 'out/Default'.", 81 | ) 82 | opts, args = parser.parse_args() 83 | global qjs 84 | global tests 85 | qjs = primjs_root_dir + "/" + opts.binary + "/qjs" 86 | tests = primjs_root_dir + "/" + opts.binary + "/qjs_tests" 87 | for f in readFile(tests): 88 | t = f.split(".")[-1] 89 | if t == "js": 90 | runCase(f, "") 91 | print("Test passed %d of %d" % (total_passed, total_case)) 92 | if total_case != total_passed: 93 | raise RuntimeError("failed") 94 | 95 | 96 | if __name__ == "__main__": 97 | sys.exit(Main()) 98 | -------------------------------------------------------------------------------- /tools/envsetup.sh: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Lynx Authors. All rights reserved. 2 | # Licensed under the Apache License Version 2.0 that can be found in the 3 | # LICENSE file in the root directory of this source tree. 4 | 5 | #!/bin/bash 6 | case "$OSTYPE" in 7 | linux*) 8 | HOST_TOOLCHAIN_PATH="linux64" 9 | ;; 10 | darwin*) 11 | HOST_TOOLCHAIN_PATH="mac" 12 | ;; 13 | *) echo "Unsupported Host OS, abort."; exit 1;; 14 | esac 15 | 16 | PRIMJS_envsetup() { 17 | local OS_NAME=`echo $(uname -s) | awk '{print tolower($0)}'` 18 | local SCRIPT_DIR=`python3 -c "import os; print(os.path.dirname(os.path.realpath('$1')))"` 19 | export PRIMJS_ROOT_DIR="$(dirname $SCRIPT_DIR)" 20 | export PRIMJS_BUILDTOOLS_DIR="${PRIMJS_ROOT_DIR}/buildtools" 21 | echo $SCRIPT_DIR 22 | export PATH=${SCRIPT_DIR}:$PATH 23 | export PATH=${PRIMJS_BUILDTOOLS_DIR}/gn:$PATH 24 | export PATH=${PRIMJS_BUILDTOOLS_DIR}/ninja:$PATH 25 | export PATH=${PRIMJS_BUILDTOOLS_DIR}/llvm/bin:$PATH 26 | export PATH=${SCRIPT_DIR}/cli:$PATH 27 | export PATH=${SCRIPT_DIR}/release:$PATH 28 | export PATH=${PRIMJS_ROOT_DIR}/tools_shared:$PATH 29 | 30 | } 31 | PRIMJS_envsetup "${BASH_SOURCE:-$0}" 32 | -------------------------------------------------------------------------------- /tools/hab: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2024 The Lynx Authors. All rights reserved. 4 | # Licensed under the Apache License Version 2.0 that can be found in the 5 | # LICENSE file in the root directory of this source tree. 6 | 7 | set -e 8 | 9 | ############################################################################## 10 | ## 11 | ## habitat start up script for UN*X 12 | ## 13 | ############################################################################## 14 | __version__="0.3.136" 15 | 16 | if [[ "$HABITAT_DEBUG" = "true" ]]; then 17 | set -x 18 | fi 19 | 20 | debug () { 21 | if [[ "$HABITAT_DEBUG" = "true" ]]; then 22 | echo "$*" 23 | fi 24 | } 25 | 26 | warn ( ) { 27 | echo "$*" 28 | } 29 | 30 | die ( ) { 31 | echo 32 | echo "$*" 33 | echo 34 | exit 1 35 | } 36 | 37 | HABITAT_VERSION=${HABITAT_VERSION:-$__version__} 38 | HABITAT_RELEASES_URL=https://github.com/lynx-family/habitat/releases/download 39 | 40 | # Attempt to set APP_HOME 41 | # Resolve links: $0 may be a link 42 | PRG="$0" 43 | # Need this for relative symlinks. 44 | while [ -h "$PRG" ] ; do 45 | ls=`ls -ld "$PRG"` 46 | link=`expr "$ls" : '.*-> \(.*\)$'` 47 | if expr "$link" : '/.*' > /dev/null; then 48 | PRG="$link" 49 | else 50 | PRG=`dirname "$PRG"`"/$link" 51 | fi 52 | done 53 | SAVED="`pwd`" 54 | cd "`dirname \"$PRG\"`/" >/dev/null 55 | APP_HOME="`pwd -P`" 56 | cd "$SAVED" >/dev/null 57 | 58 | # Use the maximum available, or set MAX_FD != -1 to use that value. 59 | MAX_FD="maximum" 60 | 61 | # OS specific support (must be 'true' or 'false'). 62 | cygwin=false 63 | msys=false 64 | darwin=false 65 | nonstop=false 66 | case "`uname`" in 67 | CYGWIN* ) 68 | cygwin=true 69 | ;; 70 | Darwin* ) 71 | darwin=true 72 | ;; 73 | MINGW* ) 74 | msys=true 75 | ;; 76 | NONSTOP* ) 77 | nonstop=true 78 | ;; 79 | esac 80 | 81 | # Increase the maximum file descriptors if we can. 82 | if [ "$cygwin" = "false" -a "$nonstop" = "false" ] ; then 83 | MAX_FD_LIMIT=`ulimit -H -n` 84 | if [ $? -eq 0 ] ; then 85 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 86 | MAX_FD="$MAX_FD_LIMIT" 87 | fi 88 | ulimit -n $MAX_FD 89 | if [ $? -ne 0 ] ; then 90 | warn "Could not set maximum file descriptor limit: $MAX_FD" 91 | fi 92 | else 93 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 94 | fi 95 | fi 96 | 97 | HABITAT_CACHE_DIR=$HOME/.habitat_cache/habitat 98 | HAB_EXECUTABLE_FILE=$HABITAT_CACHE_DIR/$HABITAT_VERSION/hab.pex 99 | export PATH=$HABITAT_CACHE_DIR/$HABITAT_VERSION:$PATH 100 | 101 | install() { 102 | if [ ! -f "$HAB_EXECUTABLE_FILE" ]; then 103 | debug "Installing habitat ($HABITAT_VERSION)..." 104 | mkdir -p $HABITAT_CACHE_DIR/$HABITAT_VERSION 105 | curl -sL "$HABITAT_RELEASES_URL/$HABITAT_VERSION/hab.pex" -o $HAB_EXECUTABLE_FILE 106 | chmod +x $HAB_EXECUTABLE_FILE 107 | else 108 | debug "hab already exists." 109 | fi 110 | } 111 | 112 | install 113 | 114 | # check habitat version 115 | CURRENT_VERSION=$($HAB_EXECUTABLE_FILE -v | tail -n 1) 116 | if [[ $CURRENT_VERSION != "${HABITAT_VERSION}" ]]; then 117 | install 118 | fi 119 | 120 | for arg in "$@" 121 | do 122 | if [ -d "$arg" ]; then 123 | root_dir=$arg 124 | fi 125 | if [[ "$arg" == "sync" ]]; then 126 | is_sync_command=true 127 | fi 128 | done 129 | 130 | exit_code=0 131 | if [ $is_sync_command ]; then 132 | err_msg=$($HAB_EXECUTABLE_FILE deps "$root_dir" 2>&1 1>/dev/null) || exit_code=$? 133 | if [[ $exit_code = 126 ]]; then 134 | # 126 means incompatible version 135 | PATTERN="([0-9]+\.[0-9]+\.[0-9]{1,3}(-[a-z]+\.[0-9]+)?)" 136 | HABITAT_VERSION=$(echo "$err_msg" | grep -E "^expected\ version:\ " | grep -E "$PATTERN" -o) 137 | debug "required version ${HABITAT_VERSION} does not exist, try installing it first (current version is $CURRENT_VERSION)" 138 | install 139 | fi 140 | fi 141 | 142 | exit_code=0 143 | echo Using habitat "$HABITAT_VERSION" 144 | $HAB_EXECUTABLE_FILE "$@" || exit_code=$? 145 | 146 | exit $exit_code 147 | --------------------------------------------------------------------------------