├── .cargo └── config.toml ├── .clang-format ├── .github └── workflows │ ├── ci.yml │ ├── release.yml │ └── update-v8.yml ├── .gitignore ├── .gitmodules ├── .gn ├── .prettierrc.json ├── .rustfmt.toml ├── BUILD.gn ├── Cargo.lock ├── Cargo.toml ├── Cross.toml ├── Dockerfile ├── LICENSE ├── README.md ├── benches └── function.rs ├── build.rs ├── examples ├── android │ ├── Cargo.toml │ ├── fractal.js │ └── lib.rs ├── count-hosts.js ├── cppgc-object.rs ├── cppgc.rs ├── hello_world.rs ├── process.rs └── shell.rs ├── gen └── .gitkeep ├── rust-toolchain.toml ├── src ├── V8.rs ├── array_buffer.rs ├── array_buffer_view.rs ├── bigint.rs ├── binding.cc ├── binding.hpp ├── binding.rs ├── context.rs ├── cppgc.rs ├── data.rs ├── date.rs ├── exception.rs ├── external.rs ├── external_references.rs ├── fast_api.rs ├── fixed_array.rs ├── function.rs ├── gc.rs ├── get_property_names_args_builder.rs ├── handle.rs ├── icu.rs ├── inspector.rs ├── isolate.rs ├── isolate_create_params.rs ├── json.rs ├── lib.rs ├── microtask.rs ├── module.rs ├── name.rs ├── number.rs ├── object.rs ├── platform.rs ├── primitive_array.rs ├── primitives.rs ├── private.rs ├── promise.rs ├── property_attribute.rs ├── property_descriptor.rs ├── property_filter.rs ├── property_handler_flags.rs ├── proxy.rs ├── regexp.rs ├── scope.rs ├── script.rs ├── script_compiler.rs ├── script_or_module.rs ├── shared_array_buffer.rs ├── snapshot.rs ├── string.rs ├── support.h ├── support.rs ├── symbol.rs ├── template.rs ├── typed_array.rs ├── unbound_module_script.rs ├── unbound_script.rs ├── value.rs ├── value_deserializer.rs ├── value_serializer.rs └── wasm.rs ├── tests ├── compile_fail │ ├── boxed_local.rs │ ├── boxed_local.stderr │ ├── handle_scope_escape_lifetime.rs │ ├── handle_scope_escape_lifetime.stderr │ ├── handle_scope_escape_to_nowhere.rs │ ├── handle_scope_escape_to_nowhere.stderr │ ├── handle_scope_lifetime_1.rs │ ├── handle_scope_lifetime_1.stderr │ ├── handle_scope_lifetime_2.rs │ ├── handle_scope_lifetime_2.stderr │ ├── handle_scope_lifetime_3.rs │ ├── handle_scope_lifetime_3.stderr │ ├── handle_scope_lifetime_4.rs │ ├── handle_scope_lifetime_4.stderr │ ├── object_without_context_scope.rs │ ├── object_without_context_scope.stderr │ ├── try_catch_exception_lifetime.rs │ ├── try_catch_exception_lifetime.stderr │ ├── try_catch_message_lifetime.rs │ └── try_catch_message_lifetime.stderr ├── slots.rs ├── test_api.rs ├── test_api_entropy_source.rs ├── test_api_flags.rs ├── test_concurrent_isolate_creation_and_disposal.rs ├── test_cppgc.rs ├── test_external_deserialize.rs ├── test_platform_atomics_pump_message_loop.rs ├── test_single_threaded_default_platform.rs └── test_ui.rs ├── third_party └── googletest │ └── src │ └── googletest │ └── include │ └── gtest │ └── gtest_prod.h └── tools ├── auto_update_v8.ts ├── download_file.py ├── get_bindgen_args.py ├── ninja_gn_binaries.py ├── setup_rbe.py ├── update_deps.py ├── upgrade_v8.sh └── v8_deps.py /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.aarch64-linux-android] 2 | linker = "./third_party/android_ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang++" 3 | 4 | [target.x86_64-linux-android] 5 | linker = "./third_party/android_ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android24-clang++" 6 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: google 2 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | releaseKind: 7 | description: 'Kind of release' 8 | default: 'minor' 9 | type: choice 10 | options: 11 | - minor 12 | - patch 13 | required: true 14 | 15 | jobs: 16 | rust: 17 | name: release 18 | runs-on: ubuntu-latest 19 | timeout-minutes: 120 20 | 21 | steps: 22 | - name: Clone repository 23 | uses: actions/checkout@v3 24 | with: 25 | token: ${{ secrets.DENOBOT_PAT }} 26 | 27 | - uses: denoland/setup-deno@v1 28 | - uses: dsherret/rust-toolchain-file@v1 29 | 30 | - name: Tag and release 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.DENOBOT_PAT }} 33 | GH_WORKFLOW_ACTOR: ${{ github.actor }} 34 | run: | 35 | git config user.email "denobot@users.noreply.github.com" 36 | git config user.name "denobot" 37 | deno run -A https://raw.githubusercontent.com/denoland/automation/0.16.1/tasks/publish_release.ts --${{github.event.inputs.releaseKind}} --skip-release v8 38 | -------------------------------------------------------------------------------- /.github/workflows/update-v8.yml: -------------------------------------------------------------------------------- 1 | name: Update V8 2 | 3 | on: 4 | schedule: 5 | - cron: "1 10 * * *" # this is 1 hour after the autoroll in denoland/v8 6 | workflow_dispatch: 7 | 8 | permissions: write-all 9 | 10 | jobs: 11 | update: 12 | runs-on: ubuntu-latest 13 | if: github.repository == 'denoland/rusty_v8' 14 | steps: 15 | - name: Clone repository 16 | uses: actions/checkout@v3 17 | - name: Fetch origin/main 18 | run: git fetch origin main 19 | - uses: denoland/setup-deno@main 20 | with: 21 | deno-version: v1.x 22 | - name: Setup Git user 23 | run: | 24 | git config --global user.email "33910674+denobot@users.noreply.github.com" 25 | git config --global user.name "denobot" 26 | git config --global user.password ${{ secrets.DENOBOT_PAT }} 27 | echo "GIT_USER=${{ secrets.DENOBOT_PAT }}" >> $GITHUB_ENV 28 | git remote set-url origin https://${{ secrets.DENOBOT_PAT }}@github.com/denoland/rusty_v8.git 29 | - run: deno run -A ./tools/auto_update_v8.ts 30 | env: 31 | GITHUB_TOKEN: ${{ secrets.DENOBOT_PAT }} 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.rs.bk 2 | **/*.orig 3 | 4 | .envrc 5 | 6 | /.vscode/ 7 | /.idea/ 8 | /target/ 9 | /.cipd/ 10 | 11 | third_party/android_ndk 12 | third_party/android_platform 13 | third_party/catapult 14 | third_party/llvm-build 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "v8"] 2 | path = v8 3 | url = https://github.com/denoland/v8.git 4 | [submodule "build"] 5 | path = build 6 | url = https://github.com/denoland/chromium_build.git 7 | [submodule "tools/clang"] 8 | path = tools/clang 9 | url = https://chromium.googlesource.com/chromium/src/tools/clang.git 10 | [submodule "third_party/jinja2"] 11 | path = third_party/jinja2 12 | url = https://chromium.googlesource.com/chromium/src/third_party/jinja2.git 13 | [submodule "third_party/markupsafe"] 14 | path = third_party/markupsafe 15 | url = https://chromium.googlesource.com/chromium/src/third_party/markupsafe.git 16 | [submodule "buildtools"] 17 | path = buildtools 18 | url = https://chromium.googlesource.com/chromium/src/buildtools.git 19 | [submodule "third_party/icu"] 20 | path = third_party/icu 21 | url = https://chromium.googlesource.com/chromium/deps/icu.git 22 | [submodule "third_party/abseil-cpp"] 23 | path = third_party/abseil-cpp 24 | url = https://chromium.googlesource.com/chromium/src/third_party/abseil-cpp.git 25 | [submodule "third_party/fp16/src"] 26 | path = third_party/fp16/src 27 | url = https://github.com/Maratyszcza/FP16.git 28 | [submodule "third_party/libc++/src"] 29 | path = third_party/libc++/src 30 | url = https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxx.git 31 | [submodule "third_party/libc++abi/src"] 32 | path = third_party/libc++abi/src 33 | url = https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libcxxabi.git 34 | [submodule "third_party/libunwind/src"] 35 | path = third_party/libunwind/src 36 | url = https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libunwind.git 37 | [submodule "third_party/fast_float/src"] 38 | path = third_party/fast_float/src 39 | url = https://chromium.googlesource.com/external/github.com/fastfloat/fast_float.git 40 | [submodule "third_party/llvm-libc/src"] 41 | path = third_party/llvm-libc/src 42 | url = https://chromium.googlesource.com/external/github.com/llvm/llvm-project/libc.git 43 | [submodule "third_party/simdutf"] 44 | path = third_party/simdutf 45 | url = https://chromium.googlesource.com/chromium/src/third_party/simdutf 46 | [submodule "third_party/highway/src"] 47 | path = third_party/highway/src 48 | url = https://chromium.googlesource.com/external/github.com/google/highway.git 49 | [submodule "third_party/partition_alloc"] 50 | path = third_party/partition_alloc 51 | url = https://chromium.googlesource.com/chromium/src/base/allocator/partition_allocator.git 52 | -------------------------------------------------------------------------------- /.gn: -------------------------------------------------------------------------------- 1 | # This file is used by the GN meta build system to find the root of the source 2 | # tree and to set startup options. For documentation on the values set in this 3 | # file, run "gn help dotfile" at the command line. 4 | 5 | # The location of the build configuration file. 6 | buildconfig = "//build/config/BUILDCONFIG.gn" 7 | 8 | # These are the targets to check headers for by default. The files in targets 9 | # matching these patterns (see "gn help label_pattern" for format) will have 10 | # their includes checked for proper dependencies when you run either 11 | # "gn check" or "gn gen --check". 12 | check_targets = [] 13 | 14 | # The secondary source root is a parallel directory tree where 15 | # GN build files are placed when they can not be placed directly 16 | # in the source tree, e.g. for third party source trees. 17 | secondary_source = "//v8/" 18 | 19 | default_args = { 20 | clang_use_chrome_plugins = false 21 | is_component_build = false 22 | linux_use_bundled_binutils = false 23 | use_dummy_lastchange = true 24 | use_sysroot = false 25 | simple_template_names = false 26 | 27 | # Minimize size of debuginfo in distributed static library. 28 | line_tables_only = true 29 | no_inline_line_tables = true 30 | symbol_level = 1 31 | use_debug_fission = false 32 | 33 | v8_embedder_string = "-rusty" 34 | 35 | v8_enable_sandbox = false 36 | v8_enable_javascript_promise_hooks = true 37 | v8_promise_internal_field_count = 1 38 | v8_use_external_startup_data = false 39 | 40 | v8_enable_pointer_compression = false 41 | 42 | v8_imminent_deprecation_warnings = false 43 | 44 | # This flag speeds up the performance of fork/execve on Linux systems for 45 | # embedders which use it (like Node.js). It works by marking the pages that 46 | # V8 allocates as MADV_DONTFORK. Without MADV_DONTFORK, the Linux kernel 47 | # spends a long time manipulating page mappings on fork and exec which the 48 | # child process doesn't generally need to access. 49 | v8_enable_private_mapping_fork_optimization = true 50 | 51 | # We prefer embedders to bring their own compression 52 | v8_use_zlib = false 53 | v8_enable_snapshot_compression = false 54 | 55 | # Disable handle zapping for performance 56 | v8_enable_handle_zapping = false 57 | # Ensure allocation of typed arrays and arraybuffers always goes through 58 | # the embedder's ArrayBufferAllocator, otherwise small buffers get moved 59 | # around by the garbage collector but embedders normally want them to have 60 | # fixed addresses. 61 | v8_typed_array_max_size_in_heap = 0 62 | 63 | # Historically these always had 2 slots. Keep for compat. 64 | v8_array_buffer_internal_field_count = 2 65 | v8_array_buffer_view_internal_field_count = 2 66 | 67 | # Enabling the shared read-only heap comes with a restriction that all 68 | # isolates running at the same time must be created from the same snapshot. 69 | # This is problematic for Deno, which has separate "runtime" and "typescript 70 | # compiler" snapshots, and sometimes uses them both at the same time. 71 | v8_enable_shared_ro_heap = false 72 | 73 | # V8 11.6 hardcoded an assumption in `mksnapshot` that shared RO heap 74 | # is enabled. In our case it's disabled so without this flag we can't 75 | # compile. 76 | v8_enable_verify_heap = false 77 | 78 | # Enable V8 object print for debugging. 79 | # v8_enable_object_print = true 80 | 81 | # V8 12.3 added google/fuzztest as a third party dependency. 82 | # https://chromium.googlesource.com/v8/v8.git/+/d5acece0c9b89b18716c177d1fcc8f734191e1e2%5E%21/#F4 83 | # 84 | # This flag disables it. 85 | v8_enable_fuzztest = false 86 | 87 | # Disable v8::HandleScope LIFO checks. 88 | # https://chromium-review.googlesource.com/c/v8/v8/+/5110566 89 | # 90 | # rusty_v8 scopes are not on the stack. 91 | v8_enable_v8_checks = false 92 | 93 | use_relative_vtables_abi = false 94 | 95 | v8_depend_on_icu_data_file = false 96 | icu_copy_icudata_to_root_build_dir = false 97 | } 98 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "proseWrap": "always" 3 | } 4 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. 2 | max_width = 80 3 | tab_spaces = 2 4 | -------------------------------------------------------------------------------- /BUILD.gn: -------------------------------------------------------------------------------- 1 | # Copyright 2018-2019 the Deno authors. All rights reserved. MIT license. 2 | import("//build/config/host_byteorder.gni") 3 | 4 | static_library("rusty_v8") { 5 | complete_static_lib = true 6 | sources = [ "src/binding.cc" ] 7 | deps = [ 8 | "//build/config:shared_library_deps", 9 | "//v8:v8", 10 | "//v8:v8_libbase", 11 | "//v8:v8_libplatform", 12 | ] 13 | configs -= [ 14 | "//build/config/compiler:default_init_stack_vars", 15 | "//build/config/compiler:thin_archive", 16 | ] 17 | configs += [ ":rusty_v8_config" ] 18 | } 19 | 20 | config("rusty_v8_config") { 21 | configs = [ 22 | "//v8:external_config", 23 | "//v8:toolchain", 24 | "//v8:features", 25 | ] 26 | cflags = [] 27 | if (is_win) { 28 | # The `/Zl` ("omit default library name") flag makes the compiler produce 29 | # object files that can link with both the static and dynamic CRT. 30 | cflags += [ "/Zl" ] 31 | } 32 | 33 | # We need these directories in the search path to be able to include some 34 | # internal V8 headers. 35 | include_dirs = [ 36 | "v8", 37 | "$target_gen_dir/v8", 38 | ] 39 | 40 | if (is_debug) { 41 | defines = [ "DEBUG" ] 42 | } 43 | 44 | if (is_clang) { 45 | cflags += [ 46 | "-fcolor-diagnostics", 47 | "-fansi-escape-codes", 48 | ] 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "v8" 3 | version = "137.2.0" 4 | description = "Rust bindings to V8" 5 | readme = "README.md" 6 | authors = ["the Deno authors"] 7 | license = "MIT" 8 | edition = "2024" 9 | repository = "https://github.com/denoland/rusty_v8" 10 | exclude = [ 11 | # To keep the package under the 10mb limit imposed by crates.io we exclude 12 | # a lot of files that are not needed for the build. 13 | "*.css", 14 | "*.gyp", 15 | "*.gypi", 16 | "*.html", 17 | "*.m4", 18 | "*.md", 19 | "*.settings", 20 | "*.txt", 21 | "*OWNERS*", 22 | ".*", 23 | "AUTHORS", 24 | "Doxyfile*", 25 | "LICENSE*", 26 | "Makefile*", 27 | "README*", 28 | "build/android/", 29 | "build/chromeos/", 30 | "build/fuchsia/", 31 | "buildtools/checkdeps/", 32 | "buildtools/clang_format/", 33 | "third_party/libc++/src/benchmarks/", 34 | "third_party/libc++/src/docs/", 35 | "third_party/libc++/src/lib/", 36 | "third_party/libc++/src/test/", 37 | "third_party/libc++/src/utils/", 38 | "third_party/libc++/src/www/", 39 | "third_party/libc++abi/src/test/", 40 | "third_party/llvm-libc/src/benchmarks/", 41 | "third_party/llvm-libc/src/docs/", 42 | "third_party/llvm-libc/src/fuzzing/", 43 | "third_party/llvm-libc/src/test/", 44 | "third_party/llvm-libc/src/utils/docgen", 45 | "third_party/icu/android/", 46 | "third_party/icu/android_small/", 47 | "third_party/icu/cast/", 48 | "third_party/icu/chromeos/", 49 | "third_party/icu/common/icudtl.dat", 50 | "third_party/icu/common/icudtb.dat", 51 | "third_party/icu/flutter/", 52 | "third_party/icu/ios/", 53 | "third_party/icu/patches/", 54 | "third_party/icu/source/config/", 55 | "third_party/icu/source/data/", 56 | "third_party/icu/source/extra/", 57 | "third_party/icu/source/io/", 58 | "third_party/icu/source/python/", 59 | "third_party/icu/source/samples/", 60 | "third_party/icu/source/test/", 61 | "third_party/icu/source/tools/", 62 | "third_party/icu/tzres/", 63 | "third_party/abseil-cpp/*.def", 64 | "third_party/abseil-cpp/absl/time/internal/cctz/testdata", 65 | "third_party/highway/src/hwy/tests", 66 | "third_party/highway/src/docs", 67 | "third_party/highway/src/g3doc", 68 | "third_party/fp16/src/test", 69 | "third_party/fast_float/src/tests", 70 | "tools/clang", 71 | "v8/ChangeLog", 72 | "v8/benchmarks/", 73 | "v8/docs/", 74 | "v8/samples/", 75 | "v8/test/", 76 | "v8/tools/", 77 | # These files are required for the build. 78 | "!.gn", 79 | "!BUILD.gn", 80 | "!tools/clang/scripts/update.py", 81 | "!v8/test/torque/test-torque.tq", 82 | "!v8/tools/gen-postmortem-metadata.py", 83 | "!v8/tools/gen-v8-gn.py", 84 | "!v8/tools/js2c.py", 85 | "!v8/tools/run.py", 86 | "!v8/tools/snapshot/asm_to_inline_asm.py", 87 | "!v8/tools/testrunner/utils/dump_build_config.py", 88 | ] 89 | 90 | [profile.dev] 91 | # rusty_v8 may miscompile at opt-level=0. 92 | # https://github.com/rust-lang/rust/issues/87215 93 | # https://github.com/rust-lang/rust/issues/75839 94 | # https://github.com/rust-lang/rust/issues/121028 95 | opt-level = 1 96 | 97 | [features] 98 | default = ["use_custom_libcxx"] 99 | use_custom_libcxx = [] 100 | v8_enable_pointer_compression = [] 101 | 102 | [dependencies] 103 | bitflags = "2.5" 104 | paste = "1.0" 105 | 106 | [build-dependencies] 107 | miniz_oxide = "0.8.8" 108 | gzip-header = "1.0.0" 109 | fslock = "0.2" 110 | which = "6" 111 | home = "0" 112 | bindgen = "0.71" 113 | 114 | [dev-dependencies] 115 | miniz_oxide = "0.8.8" 116 | gzip-header = "1.0.0" 117 | bytes = "1" 118 | align-data = "0.1.0" 119 | fslock = "0.2" 120 | trybuild = "1.0.96" 121 | which = "6" 122 | home = "0" 123 | rustversion = "1" 124 | bindgen = "0.71" 125 | 126 | [[example]] 127 | name = "hello_world" 128 | 129 | [[example]] 130 | name = "shell" 131 | 132 | [[example]] 133 | name = "process" 134 | 135 | [[test]] 136 | name = "build" 137 | path = "build.rs" 138 | 139 | [[bench]] 140 | name = "function" 141 | path = "benches/function.rs" 142 | harness = false 143 | 144 | [workspace] 145 | members = ["examples/android"] 146 | -------------------------------------------------------------------------------- /Cross.toml: -------------------------------------------------------------------------------- 1 | [build.env] 2 | passthrough = [ 3 | "V8_FROM_SOURCE", 4 | "SCCACHE_ERROR_LOG", 5 | "SCCACHE_LOG", 6 | "SCCACHE_DIR", 7 | "SCCACHE_IDLE_TIMEOUT" 8 | ] 9 | 10 | [target.aarch64-linux-android] 11 | image = "cross-rusty_v8:aarch64-linux-android" 12 | 13 | [target.aarch64-linux-android.env] 14 | passthrough = [ 15 | "CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=./third_party/android_ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang++" 16 | ] 17 | 18 | [target.x86_64-linux-android] 19 | image = "cross-rusty_v8:x86_64-linux-android" 20 | 21 | [target.x86_64-linux-android.env] 22 | passthrough = [ 23 | "CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER=./third_party/android_ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android24-clang++" 24 | ] 25 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG CROSS_BASE_IMAGE 2 | FROM $CROSS_BASE_IMAGE 3 | 4 | RUN apt update && \ 5 | apt install -y curl && \ 6 | curl -L https://github.com/mozilla/sccache/releases/download/v0.7.7/sccache-v0.7.7-x86_64-unknown-linux-musl.tar.gz | tar xzf - 7 | 8 | ENV TZ=Etc/UTC 9 | COPY ./build/*.sh /chromium_build/ 10 | COPY ./build/install-build-deps.py /chromium_build/ 11 | RUN \ 12 | DEBIAN_FRONTEND=noninteractive \ 13 | echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections \ 14 | && ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone \ 15 | && apt-get update && apt-get install -y lsb-release sudo \ 16 | && sed -i 's/snapcraft/snapcraftnoinstall/g' /chromium_build/install-build-deps.sh \ 17 | && /chromium_build/install-build-deps.sh --no-prompt --no-chromeos-fonts \ 18 | && rm -rf /chromium_build \ 19 | && rm -rf /var/lib/apt/lists/* 20 | 21 | RUN chmod +x /sccache-v0.7.7-x86_64-unknown-linux-musl/sccache 22 | 23 | ENV SCCACHE=/sccache-v0.7.7-x86_64-unknown-linux-musl/sccache 24 | ENV SCCACHE_DIR=./target/sccache 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2019 the Deno authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /benches/function.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Skip running benchmarks in debug or CI. 3 | if cfg!(debug_assertions) || std::env::var("CI").is_ok() { 4 | return; 5 | } 6 | v8::V8::set_flags_from_string( 7 | "--turbo_fast_api_calls --allow_natives_syntax", 8 | ); 9 | let platform = v8::new_default_platform(0, false).make_shared(); 10 | v8::V8::initialize_platform(platform); 11 | v8::V8::initialize(); 12 | let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); 13 | let handle_scope = &mut v8::HandleScope::new(isolate); 14 | let context = v8::Context::new(handle_scope, Default::default()); 15 | let scope = &mut v8::ContextScope::new(handle_scope, context); 16 | let global = context.global(scope); 17 | { 18 | let func = v8::Function::new( 19 | scope, 20 | |scope: &mut v8::HandleScope, 21 | _: v8::FunctionCallbackArguments, 22 | mut rv: v8::ReturnValue| { 23 | rv.set(v8::Integer::new(scope, 42).into()); 24 | }, 25 | ) 26 | .unwrap(); 27 | let name = v8::String::new(scope, "new_").unwrap(); 28 | global.set(scope, name.into(), func.into()).unwrap(); 29 | } 30 | { 31 | extern "C" fn callback(info: *const v8::FunctionCallbackInfo) { 32 | let info = unsafe { &*info }; 33 | let scope = unsafe { &mut v8::CallbackScope::new(info) }; 34 | let mut rv = v8::ReturnValue::from_function_callback_info(info); 35 | rv.set(v8::Integer::new(scope, 42).into()); 36 | } 37 | let func = v8::Function::new_raw(scope, callback).unwrap(); 38 | let name = v8::String::new(scope, "new_raw").unwrap(); 39 | global.set(scope, name.into(), func.into()).unwrap(); 40 | } 41 | { 42 | extern "C" fn callback(info: *const v8::FunctionCallbackInfo) { 43 | let info = unsafe { &*info }; 44 | let mut rv = v8::ReturnValue::from_function_callback_info(info); 45 | rv.set_uint32(42); 46 | } 47 | let func = v8::Function::new_raw(scope, callback).unwrap(); 48 | let name = v8::String::new(scope, "new_raw_set_uint32").unwrap(); 49 | global.set(scope, name.into(), func.into()).unwrap(); 50 | } 51 | { 52 | let func = v8::Function::new( 53 | scope, 54 | |_: &mut v8::HandleScope, 55 | _: v8::FunctionCallbackArguments, 56 | mut rv: v8::ReturnValue| { 57 | rv.set_uint32(42); 58 | }, 59 | ) 60 | .unwrap(); 61 | let name = v8::String::new(scope, "new_set_uint32").unwrap(); 62 | global.set(scope, name.into(), func.into()).unwrap(); 63 | } 64 | { 65 | fn fast_fn() -> i32 { 66 | 42 67 | } 68 | const FAST_CALL: v8::fast_api::CFunction = v8::fast_api::CFunction::new( 69 | fast_fn as _, 70 | &v8::fast_api::CFunctionInfo::new( 71 | v8::fast_api::Type::Int32.as_info(), 72 | &[v8::fast_api::Type::V8Value.as_info()], 73 | v8::fast_api::Int64Representation::Number, 74 | ), 75 | ); 76 | let template = v8::FunctionTemplate::builder( 77 | |scope: &mut v8::HandleScope, 78 | _: v8::FunctionCallbackArguments, 79 | mut rv: v8::ReturnValue| { 80 | rv.set(v8::Integer::new(scope, 42).into()); 81 | }, 82 | ) 83 | .build_fast(scope, &[FAST_CALL]); 84 | let name = v8::String::new(scope, "new_fast").unwrap(); 85 | let value = template.get_function(scope).unwrap(); 86 | 87 | global.set(scope, name.into(), value.into()).unwrap(); 88 | } 89 | 90 | { 91 | extern "C" fn callback(info: *const v8::FunctionCallbackInfo) { 92 | let info = unsafe { &*info }; 93 | let scope = unsafe { &mut v8::CallbackScope::new(info) }; 94 | let mut rv = v8::ReturnValue::from_function_callback_info(info); 95 | rv.set(v8::undefined(scope).into()); 96 | } 97 | let func = v8::Function::new_raw(scope, callback).unwrap(); 98 | let name = v8::String::new(scope, "undefined_from_scope").unwrap(); 99 | global.set(scope, name.into(), func.into()).unwrap(); 100 | } 101 | 102 | { 103 | extern "C" fn callback(info: *const v8::FunctionCallbackInfo) { 104 | let info = unsafe { &*info }; 105 | let mut rv = v8::ReturnValue::from_function_callback_info(info); 106 | let mut args = 107 | v8::FunctionCallbackArguments::from_function_callback_info(info); 108 | rv.set(v8::undefined(unsafe { args.get_isolate() }).into()); 109 | } 110 | let func = v8::Function::new_raw(scope, callback).unwrap(); 111 | let name = v8::String::new(scope, "undefined_from_isolate").unwrap(); 112 | global.set(scope, name.into(), func.into()).unwrap(); 113 | } 114 | 115 | let runs = 100_000_000; 116 | 117 | for (group_name, benches) in [ 118 | ( 119 | "function_overhead", 120 | &[ 121 | "new_", 122 | "new_raw", 123 | "new_set_uint32", 124 | "new_raw_set_uint32", 125 | "new_fast", 126 | ][..], 127 | ), 128 | ( 129 | "primitives", 130 | &["undefined_from_scope", "undefined_from_isolate"][..], 131 | ), 132 | ] { 133 | println!("Running {group_name} ..."); 134 | for x in benches { 135 | let code = format!( 136 | " 137 | function bench() {{ return {x}(); }}; 138 | runs = {runs}; 139 | start = Date.now(); 140 | for (i = 0; i < runs; i++) bench(); 141 | Date.now() - start; 142 | ", 143 | ); 144 | 145 | let r = eval(scope, &code).unwrap(); 146 | let number = r.to_number(scope).unwrap(); 147 | let total_ms = number.number_value(scope).unwrap(); 148 | let total_ns = 1e6 * total_ms; 149 | let ns_per_run = total_ns / (runs as f64); 150 | let mops_per_sec = (runs as f64) / (total_ms / 1000.0) / 1e6; 151 | println!( 152 | " {ns_per_run:.1} ns per run {mops_per_sec:.1} million ops/sec → {x}" 153 | ); 154 | } 155 | } 156 | } 157 | 158 | fn eval<'s>( 159 | scope: &mut v8::HandleScope<'s>, 160 | code: &str, 161 | ) -> Option> { 162 | let scope = &mut v8::EscapableHandleScope::new(scope); 163 | let source = v8::String::new(scope, code).unwrap(); 164 | let script = v8::Script::compile(scope, source, None).unwrap(); 165 | let r = script.run(scope); 166 | r.map(|v| scope.escape(v)) 167 | } 168 | -------------------------------------------------------------------------------- /examples/android/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fractal" 3 | version = "0.0.0" 4 | description = "rusty_v8 android example" 5 | authors = ["the Deno authors"] 6 | license = "MIT" 7 | edition = "2021" 8 | repository = "https://github.com/denoland/rusty_v8" 9 | publish = false 10 | 11 | [lib] 12 | path = "lib.rs" 13 | crate-type = ["cdylib"] 14 | 15 | [target.'cfg(target_os = "android")'.dependencies] 16 | v8 = { path = "../../" } 17 | winit = "0.26" 18 | pixels = "0.8.0" 19 | ndk = "0.3.0" 20 | ndk-glue = { version = "0.5.0", features = ["logger"] } 21 | -------------------------------------------------------------------------------- /examples/android/fractal.js: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | function DrawFrame(frameLen) { 3 | const u8 = new Uint8Array(frameLen); 4 | const width = 800; 5 | const height = 800; 6 | 7 | let x = y = 0; 8 | 9 | for (let i = 0; i < u8.byteLength; i += 4) { 10 | if (x == width) { 11 | y++; 12 | x = 0; 13 | } 14 | 15 | x += 1; 16 | let r = Math.floor(0.3 * x); 17 | let b = Math.floor(0.3 * y); 18 | 19 | u8.set([r, 0x00, b, 0xff], i); 20 | } 21 | 22 | let scale_x = 3.0 / width; 23 | let scale_y = 3.0 / height; 24 | 25 | for (let x = 0; x < width; x++) { 26 | for (let y = 0; y < height; y++) { 27 | let cx = y * scale_x - 1.5; 28 | let cy = x * scale_y - 1.5; 29 | 30 | let c = new Complex(-0.4, 0.6); 31 | let z = new Complex(cx, cy); 32 | 33 | let i = 0; 34 | while (i < 100 && z.abs() < 2) { 35 | z = z.mul(z).add(c); 36 | i++; 37 | } 38 | 39 | u8.set([0x00, i, 0x00, 0xff], (y * width + x) * 4); 40 | } 41 | } 42 | 43 | return u8.buffer; 44 | } 45 | 46 | class Complex { 47 | constructor(real, imag) { 48 | this.real = real; 49 | this.imag = imag; 50 | } 51 | 52 | mul(other) { 53 | return new Complex( 54 | this.real * other.real - this.imag * other.imag, 55 | this.real * other.imag + this.imag * other.real, 56 | ); 57 | } 58 | 59 | add(other) { 60 | return new Complex( 61 | this.real + other.real, 62 | this.imag + other.imag, 63 | ); 64 | } 65 | 66 | abs() { 67 | return Math.sqrt(this.real * this.real + this.imag * this.imag); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /examples/android/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | 3 | // Don't run on non-Android targets. 4 | #![cfg(target_os = "android")] 5 | // Don't run this as a test in `--all-targets` mode. 6 | #![cfg(not(test))] 7 | 8 | use pixels::Pixels; 9 | use pixels::SurfaceTexture; 10 | use std::cell::Cell; 11 | use winit::platform::run_return::EventLoopExtRunReturn; 12 | 13 | #[ndk_glue::main( 14 | backtrace = "on", 15 | logger(level = "debug", tag = "android_fractal") 16 | )] 17 | fn main() { 18 | let mut event_loop = winit::event_loop::EventLoop::new(); 19 | let window = winit::window::WindowBuilder::new() 20 | .with_title("rusty_v8 android_fractal") 21 | .build(&event_loop) 22 | .unwrap(); 23 | 24 | // Initialize V8. 25 | let platform = v8::new_default_platform(0, false).make_shared(); 26 | v8::V8::initialize_platform(platform); 27 | v8::V8::initialize(); 28 | 29 | let mut isolate = v8::Isolate::new(v8::CreateParams::default()); 30 | let mut scope = v8::HandleScope::new(&mut isolate); 31 | let source = v8::String::new(&mut scope, include_str!("fractal.js")).unwrap(); 32 | 33 | let context = v8::Context::new(&mut scope); 34 | let mut context_scope = v8::ContextScope::new(&mut scope, context); 35 | 36 | execute_script(&mut context_scope, source); 37 | 38 | let draw_str = v8::String::new(&mut context_scope, "DrawFrame").unwrap(); 39 | let draw_fn = context 40 | .global(&mut context_scope) 41 | .get(&mut context_scope, draw_str.into()) 42 | .expect("missing function DrawFrame"); 43 | 44 | let draw_fn = 45 | v8::Local::::try_from(draw_fn).expect("function expected"); 46 | 47 | let mut allowed = false; 48 | 49 | loop { 50 | event_loop.run_return(|event, _, control_flow| { 51 | *control_flow = winit::event_loop::ControlFlow::Wait; 52 | match event { 53 | winit::event::Event::WindowEvent { 54 | event: winit::event::WindowEvent::CloseRequested, 55 | .. 56 | } => *control_flow = winit::event_loop::ControlFlow::Exit, 57 | // Drawing on android must only happen before Event::Suspended and 58 | // after Event::Resumed. 59 | // 60 | // https://github.com/rust-windowing/winit/issues/1588 61 | winit::event::Event::Resumed => { 62 | allowed = true; 63 | } 64 | winit::event::Event::Suspended => { 65 | allowed = false; 66 | } 67 | winit::event::Event::RedrawRequested(_) => { 68 | if !allowed { 69 | return; 70 | } 71 | let surface_texture = SurfaceTexture::new(800, 800, &window); 72 | let mut pixels = Pixels::new(800, 800, surface_texture).unwrap(); 73 | 74 | draw(&mut context_scope, draw_fn, pixels.get_frame()); 75 | 76 | if pixels.render().is_err() { 77 | *control_flow = winit::event_loop::ControlFlow::Exit; 78 | return; 79 | } 80 | } 81 | _ => {} 82 | } 83 | 84 | window.request_redraw(); 85 | }); 86 | } 87 | } 88 | 89 | fn execute_script( 90 | context_scope: &mut v8::ContextScope, 91 | script: v8::Local, 92 | ) { 93 | let scope = &mut v8::HandleScope::new(context_scope); 94 | let try_catch = &mut v8::TryCatch::new(scope); 95 | 96 | let script = v8::Script::compile(try_catch, script, None) 97 | .expect("failed to compile script"); 98 | 99 | if script.run(try_catch).is_none() { 100 | let exception_string = try_catch 101 | .stack_trace() 102 | .or_else(|| try_catch.exception()) 103 | .map(|value| value.to_rust_string_lossy(try_catch)) 104 | .unwrap_or_else(|| "no stack trace".into()); 105 | 106 | panic!("{exception_string}"); 107 | } 108 | } 109 | 110 | fn draw( 111 | context_scope: &mut v8::ContextScope, 112 | draw_fn: v8::Local, 113 | frame: &mut [u8], 114 | ) { 115 | let scope = &mut v8::HandleScope::new(context_scope); 116 | let recv = v8::undefined(scope); 117 | let try_catch = &mut v8::TryCatch::new(scope); 118 | 119 | let len = frame.len(); 120 | let frame_len = v8::Integer::new(try_catch, len as i32); 121 | 122 | let ab = match draw_fn.call(try_catch, recv.into(), &[frame_len.into()]) { 123 | Some(ab) => ab, 124 | None => { 125 | let exception_string = try_catch 126 | .stack_trace() 127 | .or_else(|| try_catch.exception()) 128 | .map(|value| value.to_rust_string_lossy(try_catch)) 129 | .unwrap_or_else(|| "no stack trace".into()); 130 | 131 | panic!("{exception_string}"); 132 | } 133 | }; 134 | 135 | let ab = 136 | v8::Local::::try_from(ab).expect("array buffer expected"); 137 | let bs = ab.get_backing_store(); 138 | 139 | let js_frame = unsafe { get_backing_store_slice(&bs, 0, len) }; 140 | frame.copy_from_slice(js_frame.as_ref()); 141 | } 142 | 143 | unsafe fn get_backing_store_slice( 144 | backing_store: &v8::SharedRef, 145 | byte_offset: usize, 146 | byte_length: usize, 147 | ) -> &[u8] { 148 | let cells: *const [Cell] = 149 | &backing_store[byte_offset..byte_offset + byte_length]; 150 | let bytes = cells as *const [u8]; 151 | &*bytes 152 | } 153 | -------------------------------------------------------------------------------- /examples/count-hosts.js: -------------------------------------------------------------------------------- 1 | // Copyright 2008 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 | 28 | function Initialize() { } 29 | 30 | function Process(request) { 31 | if (options.verbose) { 32 | log("Processing " + request.host + request.path + 33 | " from " + request.referrer + "@" + request.userAgent); 34 | } 35 | if (!output[request.host]) { 36 | output[request.host] = 1; 37 | } else { 38 | output[request.host]++ 39 | } 40 | } 41 | 42 | Initialize(); 43 | -------------------------------------------------------------------------------- /examples/cppgc-object.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | use std::cell::Cell; 3 | 4 | struct Wrappable { 5 | id: String, 6 | trace_count: Cell, 7 | } 8 | 9 | impl v8::cppgc::GarbageCollected for Wrappable { 10 | fn trace(&self, _visitor: &v8::cppgc::Visitor) { 11 | println!("Wrappable::trace() {}", self.id); 12 | self.trace_count.set(self.trace_count.get() + 1); 13 | } 14 | 15 | fn get_name(&self) -> &'static std::ffi::CStr { 16 | c"Wrappable" 17 | } 18 | } 19 | 20 | impl Drop for Wrappable { 21 | fn drop(&mut self) { 22 | println!("Wrappable::drop() {}", self.id); 23 | } 24 | } 25 | 26 | const TAG: u16 = 1; 27 | 28 | fn main() { 29 | let platform = v8::new_default_platform(0, false).make_shared(); 30 | v8::V8::set_flags_from_string("--no_freeze_flags_after_init --expose-gc"); 31 | v8::V8::initialize_platform(platform.clone()); 32 | v8::V8::initialize(); 33 | 34 | v8::cppgc::initialize_process(platform.clone()); 35 | 36 | { 37 | let heap = 38 | v8::cppgc::Heap::create(platform, v8::cppgc::HeapCreateParams::default()); 39 | let isolate = 40 | &mut v8::Isolate::new(v8::CreateParams::default().cpp_heap(heap)); 41 | 42 | let handle_scope = &mut v8::HandleScope::new(isolate); 43 | let context = v8::Context::new(handle_scope, Default::default()); 44 | let scope = &mut v8::ContextScope::new(handle_scope, context); 45 | let global = context.global(scope); 46 | { 47 | let func = v8::Function::new( 48 | scope, 49 | |scope: &mut v8::HandleScope, 50 | args: v8::FunctionCallbackArguments, 51 | mut rv: v8::ReturnValue| { 52 | let id = args.get(0).to_rust_string_lossy(scope); 53 | 54 | fn empty( 55 | _scope: &mut v8::HandleScope, 56 | _args: v8::FunctionCallbackArguments, 57 | _rv: v8::ReturnValue, 58 | ) { 59 | } 60 | let templ = v8::FunctionTemplate::new(scope, empty); 61 | let func = templ.get_function(scope).unwrap(); 62 | let obj = func.new_instance(scope, &[]).unwrap(); 63 | 64 | assert!(obj.is_api_wrapper()); 65 | 66 | let member = unsafe { 67 | v8::cppgc::make_garbage_collected( 68 | scope.get_cpp_heap().unwrap(), 69 | Wrappable { 70 | trace_count: Cell::new(0), 71 | id, 72 | }, 73 | ) 74 | }; 75 | 76 | unsafe { 77 | v8::Object::wrap::(scope, obj, &member); 78 | } 79 | 80 | rv.set(obj.into()); 81 | }, 82 | ) 83 | .unwrap(); 84 | let name = v8::String::new(scope, "make_wrap").unwrap(); 85 | global.set(scope, name.into(), func.into()).unwrap(); 86 | } 87 | 88 | let source = v8::String::new( 89 | scope, 90 | r#" 91 | make_wrap('gc me pls'); // Inaccessible after scope. 92 | globalThis.wrap = make_wrap('dont gc me'); // Accessible after scope. 93 | "#, 94 | ) 95 | .unwrap(); 96 | execute_script(scope, source); 97 | 98 | scope 99 | .request_garbage_collection_for_testing(v8::GarbageCollectionType::Full); 100 | } 101 | 102 | // Gracefully shutdown the process. 103 | unsafe { 104 | v8::cppgc::shutdown_process(); 105 | v8::V8::dispose(); 106 | } 107 | v8::V8::dispose_platform(); 108 | } 109 | 110 | fn execute_script( 111 | context_scope: &mut v8::ContextScope, 112 | script: v8::Local, 113 | ) { 114 | let scope = &mut v8::HandleScope::new(context_scope); 115 | let try_catch = &mut v8::TryCatch::new(scope); 116 | 117 | let script = v8::Script::compile(try_catch, script, None) 118 | .expect("failed to compile script"); 119 | 120 | if script.run(try_catch).is_none() { 121 | let exception_string = try_catch 122 | .stack_trace() 123 | .or_else(|| try_catch.exception()) 124 | .map_or_else( 125 | || "no stack trace".into(), 126 | |value| value.to_rust_string_lossy(try_catch), 127 | ); 128 | 129 | panic!("{exception_string}"); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /examples/cppgc.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | // 3 | // This sample program shows how to set up a stand-alone cppgc heap. 4 | 5 | // Simple string rope to illustrate allocation and garbage collection below. 6 | // The rope keeps the next parts alive via regular managed reference. 7 | 8 | struct Rope { 9 | part: String, 10 | next: v8::cppgc::Member, 11 | } 12 | 13 | impl std::fmt::Display for Rope { 14 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 15 | write!(f, "{}", self.part)?; 16 | if let Some(next) = self.next.borrow() { 17 | write!(f, "{next}")?; 18 | } 19 | Ok(()) 20 | } 21 | } 22 | 23 | impl Rope { 24 | pub fn new(part: String, next: Option>) -> Rope { 25 | let next = match next { 26 | Some(p) => v8::cppgc::Member::new(&p), 27 | None => v8::cppgc::Member::empty(), 28 | }; 29 | Self { part, next } 30 | } 31 | } 32 | 33 | impl v8::cppgc::GarbageCollected for Rope { 34 | fn trace(&self, visitor: &v8::cppgc::Visitor) { 35 | visitor.trace(&self.next); 36 | } 37 | 38 | fn get_name(&self) -> &'static std::ffi::CStr { 39 | c"Rope" 40 | } 41 | } 42 | 43 | impl Drop for Rope { 44 | fn drop(&mut self) { 45 | println!("Dropping: {}", self.part); 46 | } 47 | } 48 | 49 | fn main() { 50 | let platform = v8::new_default_platform(0, false).make_shared(); 51 | v8::V8::initialize_platform(platform.clone()); 52 | v8::V8::initialize(); 53 | v8::cppgc::initialize_process(platform.clone()); 54 | 55 | { 56 | // Create a managed heap. 57 | let heap = 58 | v8::cppgc::Heap::create(platform, v8::cppgc::HeapCreateParams::default()); 59 | 60 | // Allocate a string rope on the managed heap. 61 | let rope = unsafe { 62 | v8::cppgc::make_garbage_collected( 63 | &heap, 64 | Rope::new( 65 | String::from("Hello "), 66 | Some(v8::cppgc::make_garbage_collected( 67 | &heap, 68 | Rope::new(String::from("World!"), None), 69 | )), 70 | ), 71 | ) 72 | }; 73 | 74 | println!("{rope}"); 75 | 76 | // Manually trigger garbage collection. 77 | heap.enable_detached_garbage_collections_for_testing(); 78 | 79 | println!("Collect: MayContainHeapPointers"); 80 | unsafe { 81 | heap.collect_garbage_for_testing( 82 | v8::cppgc::EmbedderStackState::MayContainHeapPointers, 83 | ); 84 | } 85 | 86 | // Should still be live here: 87 | println!("{rope}"); 88 | 89 | println!("Collect: NoHeapPointers"); 90 | unsafe { 91 | heap.collect_garbage_for_testing( 92 | v8::cppgc::EmbedderStackState::NoHeapPointers, 93 | ); 94 | } 95 | 96 | // Should be dead now. 97 | } 98 | 99 | // Gracefully shutdown the process. 100 | unsafe { 101 | v8::cppgc::shutdown_process(); 102 | v8::V8::dispose(); 103 | } 104 | v8::V8::dispose_platform(); 105 | } 106 | -------------------------------------------------------------------------------- /examples/hello_world.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Initialize V8. 3 | let platform = v8::new_default_platform(0, false).make_shared(); 4 | v8::V8::initialize_platform(platform); 5 | v8::V8::initialize(); 6 | 7 | { 8 | // Create a new Isolate and make it the current one. 9 | let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); 10 | 11 | // Create a stack-allocated handle scope. 12 | let handle_scope = &mut v8::HandleScope::new(isolate); 13 | 14 | // Create a new context. 15 | let context = v8::Context::new(handle_scope, Default::default()); 16 | 17 | // Enter the context for compiling and running the hello world script. 18 | let scope = &mut v8::ContextScope::new(handle_scope, context); 19 | 20 | // Create a string containing the JavaScript source code. 21 | let code = v8::String::new(scope, "'Hello' + ' World!'").unwrap(); 22 | 23 | // Compile the source code. 24 | let script = v8::Script::compile(scope, code, None).unwrap(); 25 | // Run the script to get the result. 26 | let result = script.run(scope).unwrap(); 27 | 28 | // Convert the result to a string and print it. 29 | let result = result.to_string(scope).unwrap(); 30 | println!("{}", result.to_rust_string_lossy(scope)); 31 | 32 | // Use the JavaScript API to generate a WebAssembly module. 33 | // 34 | // |bytes| contains the binary format for the following module: 35 | // 36 | // (func (export "add") (param i32 i32) (result i32) 37 | // get_local 0 38 | // get_local 1 39 | // i32.add) 40 | // 41 | let c_source = r#" 42 | let bytes = new Uint8Array([ 43 | 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 44 | 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 45 | 0x07, 0x01, 0x03, 0x61, 0x64, 0x64, 0x00, 0x00, 0x0a, 0x09, 0x01, 46 | 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b 47 | ]); 48 | let module = new WebAssembly.Module(bytes); 49 | let instance = new WebAssembly.Instance(module); 50 | instance.exports.add(3, 4); 51 | "#; 52 | // Create a string containing the JavaScript source code. 53 | let source = v8::String::new(scope, c_source).unwrap(); 54 | 55 | // Compile the source code. 56 | let script = v8::Script::compile(scope, source, None).unwrap(); 57 | 58 | // Run the script to get the result. 59 | let result = script.run(scope).unwrap(); 60 | 61 | // Print the result. 62 | let result = result.to_uint32(scope).unwrap(); 63 | println!("3 + 4 = {}", result.value()); 64 | } 65 | 66 | unsafe { 67 | v8::V8::dispose(); 68 | } 69 | v8::V8::dispose_platform(); 70 | } 71 | -------------------------------------------------------------------------------- /examples/shell.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | // Initialize V8. 3 | let platform = v8::new_default_platform(0, false).make_shared(); 4 | v8::V8::initialize_platform(platform); 5 | v8::V8::initialize(); 6 | 7 | // Pass command line arguments to V8. 8 | let args: Vec = std::env::args().collect(); 9 | let args = v8::V8::set_flags_from_command_line(args); 10 | 11 | let mut run_shell_flag = args.len() == 1; 12 | let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); 13 | let handle_scope = &mut v8::HandleScope::new(isolate); 14 | 15 | let context = v8::Context::new(handle_scope, Default::default()); 16 | 17 | let context_scope = &mut v8::ContextScope::new(handle_scope, context); 18 | let scope = &mut v8::HandleScope::new(context_scope); 19 | 20 | run_main(scope, &args, &mut run_shell_flag); 21 | 22 | if run_shell_flag { 23 | run_shell(scope); 24 | } 25 | } 26 | 27 | /// Process remaining command line arguments and execute files 28 | fn run_shell(scope: &mut v8::HandleScope) { 29 | use std::io::{self, Write}; 30 | 31 | println!("V8 version {} [sample shell]", v8::V8::get_version()); 32 | 33 | loop { 34 | print!("> "); 35 | io::stdout().flush().unwrap(); 36 | 37 | let mut buf = String::new(); 38 | match io::stdin().read_line(&mut buf) { 39 | Ok(n) => { 40 | if n == 0 { 41 | println!(); 42 | return; 43 | } 44 | 45 | execute_string(scope, &buf, "(shell)", true, true); 46 | } 47 | Err(error) => println!("error: {error}"), 48 | } 49 | } 50 | } 51 | 52 | /// Process remaining command line arguments and execute files 53 | fn run_main( 54 | scope: &mut v8::HandleScope, 55 | args: &[String], 56 | run_shell: &mut bool, 57 | ) { 58 | let mut skip_next = false; 59 | 60 | // Parse command-line arguments. 61 | for (i, arg) in args.iter().enumerate().skip(1) { 62 | if skip_next { 63 | continue; 64 | } 65 | 66 | match &**arg { 67 | "--shell" => { 68 | // Enables the shell. 69 | *run_shell = true; 70 | } 71 | "-f" => { 72 | // Ignore any -f flags for compatibility with the other stand- 73 | // alone JavaScript engines. 74 | } 75 | "-e" => { 76 | // Execute script. 77 | let script: &str = &args[i + 1]; 78 | skip_next = true; 79 | 80 | execute_string(scope, script, "unnamed", false, true); 81 | 82 | while v8::Platform::pump_message_loop( 83 | &v8::V8::get_current_platform(), 84 | scope, 85 | false, 86 | ) { 87 | // do nothing 88 | } 89 | } 90 | arg => { 91 | if arg.starts_with("--") { 92 | eprintln!("Warning: unknown flag {arg}.\nTry --help for options"); 93 | continue; 94 | } 95 | 96 | // Use all other arguments as names of files to load and run. 97 | let script = std::fs::read_to_string(arg).expect("failed to read file"); 98 | execute_string(scope, &script, arg, false, true); 99 | 100 | while v8::Platform::pump_message_loop( 101 | &v8::V8::get_current_platform(), 102 | scope, 103 | false, 104 | ) { 105 | // do nothing 106 | } 107 | } 108 | } 109 | } 110 | } 111 | 112 | fn execute_string( 113 | scope: &mut v8::HandleScope, 114 | script: &str, 115 | filename: &str, 116 | print_result: bool, 117 | report_exceptions_flag: bool, 118 | ) { 119 | let mut scope = v8::TryCatch::new(scope); 120 | 121 | let filename = v8::String::new(&mut scope, filename).unwrap(); 122 | let script = v8::String::new(&mut scope, script).unwrap(); 123 | let origin = v8::ScriptOrigin::new( 124 | &mut scope, 125 | filename.into(), 126 | 0, 127 | 0, 128 | false, 129 | 0, 130 | None, 131 | false, 132 | false, 133 | false, 134 | None, 135 | ); 136 | 137 | let script = if let Some(script) = 138 | v8::Script::compile(&mut scope, script, Some(&origin)) 139 | { 140 | script 141 | } else { 142 | assert!(scope.has_caught()); 143 | 144 | if report_exceptions_flag { 145 | report_exceptions(scope); 146 | } 147 | return; 148 | }; 149 | 150 | if let Some(result) = script.run(&mut scope) { 151 | if print_result { 152 | println!( 153 | "{}", 154 | result 155 | .to_string(&mut scope) 156 | .unwrap() 157 | .to_rust_string_lossy(&mut scope) 158 | ); 159 | } 160 | } else { 161 | assert!(scope.has_caught()); 162 | if report_exceptions_flag { 163 | report_exceptions(scope); 164 | } 165 | } 166 | } 167 | 168 | fn report_exceptions(mut try_catch: v8::TryCatch) { 169 | let exception = try_catch.exception().unwrap(); 170 | let exception_string = exception 171 | .to_string(&mut try_catch) 172 | .unwrap() 173 | .to_rust_string_lossy(&mut try_catch); 174 | let message = if let Some(message) = try_catch.message() { 175 | message 176 | } else { 177 | eprintln!("{exception_string}"); 178 | return; 179 | }; 180 | 181 | // Print (filename):(line number): (message). 182 | let filename = message 183 | .get_script_resource_name(&mut try_catch) 184 | .map_or_else( 185 | || "(unknown)".into(), 186 | |s| { 187 | s.to_string(&mut try_catch) 188 | .unwrap() 189 | .to_rust_string_lossy(&mut try_catch) 190 | }, 191 | ); 192 | let line_number = message.get_line_number(&mut try_catch).unwrap_or_default(); 193 | 194 | eprintln!("{filename}:{line_number}: {exception_string}"); 195 | 196 | // Print line of source code. 197 | let source_line = message 198 | .get_source_line(&mut try_catch) 199 | .map(|s| { 200 | s.to_string(&mut try_catch) 201 | .unwrap() 202 | .to_rust_string_lossy(&mut try_catch) 203 | }) 204 | .unwrap(); 205 | eprintln!("{source_line}"); 206 | 207 | // Print wavy underline (GetUnderline is deprecated). 208 | let start_column = message.get_start_column(); 209 | let end_column = message.get_end_column(); 210 | 211 | for _ in 0..start_column { 212 | eprint!(" "); 213 | } 214 | 215 | for _ in start_column..end_column { 216 | eprint!("^"); 217 | } 218 | 219 | eprintln!(); 220 | 221 | // Print stack trace 222 | let stack_trace = if let Some(stack_trace) = try_catch.stack_trace() { 223 | stack_trace 224 | } else { 225 | return; 226 | }; 227 | let stack_trace = 228 | unsafe { v8::Local::::cast_unchecked(stack_trace) }; 229 | let stack_trace = stack_trace 230 | .to_string(&mut try_catch) 231 | .map(|s| s.to_rust_string_lossy(&mut try_catch)); 232 | 233 | if let Some(stack_trace) = stack_trace { 234 | eprintln!("{stack_trace}"); 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /gen/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denoland/rusty_v8/966903e8845f736f8ba57f0419246bf176e73753/gen/.gitkeep -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.86.0" 3 | components = ["rustfmt", "clippy"] 4 | targets = [ 5 | "x86_64-apple-darwin", 6 | "aarch64-apple-darwin", 7 | "x86_64-unknown-linux-gnu", 8 | "aarch64-unknown-linux-gnu", 9 | "x86_64-pc-windows-msvc", 10 | "aarch64-linux-android", 11 | "x86_64-linux-android", 12 | ] 13 | -------------------------------------------------------------------------------- /src/array_buffer_view.rs: -------------------------------------------------------------------------------- 1 | use crate::ArrayBuffer; 2 | use crate::ArrayBufferView; 3 | use crate::BackingStore; 4 | use crate::HandleScope; 5 | use crate::Local; 6 | use crate::SharedRef; 7 | use crate::binding::memory_span_t; 8 | use crate::support::int; 9 | use std::convert::TryInto; 10 | use std::ffi::c_void; 11 | 12 | unsafe extern "C" { 13 | fn v8__ArrayBufferView__Buffer( 14 | this: *const ArrayBufferView, 15 | ) -> *const ArrayBuffer; 16 | fn v8__ArrayBufferView__Buffer__Data( 17 | this: *const ArrayBufferView, 18 | ) -> *mut c_void; 19 | fn v8__ArrayBufferView__ByteLength(this: *const ArrayBufferView) -> usize; 20 | fn v8__ArrayBufferView__ByteOffset(this: *const ArrayBufferView) -> usize; 21 | fn v8__ArrayBufferView__HasBuffer(this: *const ArrayBufferView) -> bool; 22 | fn v8__ArrayBufferView__CopyContents( 23 | this: *const ArrayBufferView, 24 | dest: *mut c_void, 25 | byte_length: int, 26 | ) -> usize; 27 | fn v8__ArrayBufferView__GetContents( 28 | this: *const ArrayBufferView, 29 | storage: memory_span_t, 30 | ) -> memory_span_t; 31 | } 32 | 33 | impl ArrayBufferView { 34 | /// Returns underlying ArrayBuffer. 35 | #[inline(always)] 36 | pub fn buffer<'s>( 37 | &self, 38 | scope: &mut HandleScope<'s>, 39 | ) -> Option> { 40 | unsafe { scope.cast_local(|_| v8__ArrayBufferView__Buffer(self)) } 41 | } 42 | 43 | /// Returns true if ArrayBufferView's backing ArrayBuffer has already been allocated. 44 | pub fn has_buffer(&self) -> bool { 45 | unsafe { v8__ArrayBufferView__HasBuffer(self) } 46 | } 47 | 48 | /// Get a shared pointer to the backing store of this array buffer. This 49 | /// pointer coordinates the lifetime management of the internal storage 50 | /// with any live ArrayBuffers on the heap, even across isolates. The embedder 51 | /// should not attempt to manage lifetime of the storage through other means. 52 | #[inline(always)] 53 | pub fn get_backing_store(&self) -> Option> { 54 | let buffer = unsafe { v8__ArrayBufferView__Buffer(self) }; 55 | unsafe { buffer.as_ref().map(|buffer| buffer.get_backing_store()) } 56 | } 57 | 58 | /// Returns the underlying storage for this `ArrayBufferView`, including the built-in `byte_offset`. 59 | /// This is a more efficient way of calling `buffer(scope)->data()`, and may be called without a 60 | /// scope. 61 | #[inline(always)] 62 | pub fn data(&self) -> *mut c_void { 63 | unsafe { 64 | v8__ArrayBufferView__Buffer__Data(self) 65 | .add(v8__ArrayBufferView__ByteOffset(self)) 66 | } 67 | } 68 | 69 | /// Size of a view in bytes. 70 | #[inline(always)] 71 | pub fn byte_length(&self) -> usize { 72 | unsafe { v8__ArrayBufferView__ByteLength(self) } 73 | } 74 | 75 | /// Byte offset in |Buffer|. 76 | #[inline(always)] 77 | pub fn byte_offset(&self) -> usize { 78 | unsafe { v8__ArrayBufferView__ByteOffset(self) } 79 | } 80 | 81 | /// Copy the contents of the ArrayBufferView's buffer to an embedder defined 82 | /// memory without additional overhead that calling ArrayBufferView::Buffer 83 | /// might incur. 84 | /// Returns the number of bytes actually written. 85 | #[inline(always)] 86 | pub fn copy_contents(&self, dest: &mut [u8]) -> usize { 87 | unsafe { 88 | v8__ArrayBufferView__CopyContents( 89 | self, 90 | dest.as_mut_ptr() as *mut c_void, 91 | dest.len().try_into().unwrap(), 92 | ) 93 | } 94 | } 95 | 96 | /// Returns the contents of the ArrayBufferView's buffer as a MemorySpan. If 97 | /// the contents are on the V8 heap, they get copied into `storage`. Otherwise 98 | /// a view into the off-heap backing store is returned. The provided storage 99 | /// should be at least as large as the maximum on-heap size of a TypedArray, 100 | /// which is available as `v8::TYPED_ARRAY_MAX_SIZE_IN_HEAP`. 101 | #[inline(always)] 102 | pub unsafe fn get_contents_raw_parts( 103 | &self, 104 | storage: &mut [u8], 105 | ) -> (*mut u8, usize) { 106 | unsafe { 107 | let span = v8__ArrayBufferView__GetContents( 108 | self, 109 | memory_span_t { 110 | data: storage.as_mut_ptr(), 111 | size: storage.len(), 112 | }, 113 | ); 114 | (span.data, span.size) 115 | } 116 | } 117 | 118 | /// Returns the contents of the ArrayBufferView's buffer as a MemorySpan. If 119 | /// the contents are on the V8 heap, they get copied into `storage`. Otherwise 120 | /// a view into the off-heap backing store is returned. The provided storage 121 | /// should be at least as large as the maximum on-heap size of a TypedArray, 122 | /// which is available as `v8::TYPED_ARRAY_MAX_SIZE_IN_HEAP`. 123 | #[inline(always)] 124 | pub fn get_contents<'s, 'a>(&'s self, storage: &'a mut [u8]) -> &'a [u8] 125 | where 126 | 's: 'a, 127 | { 128 | unsafe { 129 | let (data, size) = self.get_contents_raw_parts(storage); 130 | if data.is_null() { 131 | debug_assert_eq!(size, 0); 132 | std::slice::from_raw_parts(std::ptr::dangling(), size) 133 | } else { 134 | std::slice::from_raw_parts(data, size) 135 | } 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/bigint.rs: -------------------------------------------------------------------------------- 1 | use crate::BigInt; 2 | use crate::Context; 3 | use crate::HandleScope; 4 | use crate::Isolate; 5 | use crate::Local; 6 | use crate::support::int; 7 | 8 | use std::mem::MaybeUninit; 9 | 10 | unsafe extern "C" { 11 | fn v8__BigInt__New(isolate: *mut Isolate, value: i64) -> *const BigInt; 12 | fn v8__BigInt__NewFromUnsigned( 13 | isolate: *mut Isolate, 14 | value: u64, 15 | ) -> *const BigInt; 16 | fn v8__BigInt__NewFromWords( 17 | context: *const Context, 18 | sign_bit: int, 19 | word_count: int, 20 | words: *const u64, 21 | ) -> *const BigInt; 22 | fn v8__BigInt__Uint64Value(this: *const BigInt, lossless: *mut bool) -> u64; 23 | fn v8__BigInt__Int64Value(this: *const BigInt, lossless: *mut bool) -> i64; 24 | fn v8__BigInt__WordCount(this: *const BigInt) -> int; 25 | fn v8__BigInt__ToWordsArray( 26 | this: *const BigInt, 27 | sign_bit: *mut int, 28 | word_count: *mut int, 29 | words: *mut u64, 30 | ); 31 | } 32 | 33 | impl BigInt { 34 | #[inline(always)] 35 | pub fn new_from_i64<'s>( 36 | scope: &mut HandleScope<'s>, 37 | value: i64, 38 | ) -> Local<'s, BigInt> { 39 | unsafe { 40 | scope.cast_local(|sd| v8__BigInt__New(sd.get_isolate_ptr(), value)) 41 | } 42 | .unwrap() 43 | } 44 | 45 | #[inline(always)] 46 | pub fn new_from_u64<'s>( 47 | scope: &mut HandleScope<'s>, 48 | value: u64, 49 | ) -> Local<'s, BigInt> { 50 | unsafe { 51 | scope.cast_local(|sd| { 52 | v8__BigInt__NewFromUnsigned(sd.get_isolate_ptr(), value) 53 | }) 54 | } 55 | .unwrap() 56 | } 57 | 58 | /// Creates a new BigInt object using a specified sign bit and a 59 | /// specified list of digits/words. 60 | /// The resulting number is calculated as: 61 | /// 62 | /// (-1)^sign_bit * (words[0] * (2^64)^0 + words[1] * (2^64)^1 + ...) 63 | #[inline(always)] 64 | pub fn new_from_words<'s>( 65 | scope: &mut HandleScope<'s>, 66 | sign_bit: bool, 67 | words: &[u64], 68 | ) -> Option> { 69 | unsafe { 70 | scope.cast_local(|sd| { 71 | v8__BigInt__NewFromWords( 72 | sd.get_current_context(), 73 | sign_bit as int, 74 | words.len() as int, 75 | words.as_ptr(), 76 | ) 77 | }) 78 | } 79 | } 80 | 81 | /// Returns the value of this BigInt as an unsigned 64-bit integer, and a 82 | /// `bool` indicating that the conversion was lossless when `true`. 83 | /// The boolean value will be `false` if the return value was truncated or wrapped around, 84 | /// in particular if the BigInt is negative. 85 | #[inline(always)] 86 | pub fn u64_value(&self) -> (u64, bool) { 87 | let mut lossless = MaybeUninit::uninit(); 88 | let v = unsafe { v8__BigInt__Uint64Value(self, lossless.as_mut_ptr()) }; 89 | let lossless = unsafe { lossless.assume_init() }; 90 | (v, lossless) 91 | } 92 | 93 | /// Returns the value of this BigInt as a signed 64-bit integer, and a `bool` 94 | /// indicating that the conversion was lossless when `true`. 95 | /// The boolean value will be `false` if the return value was truncated or wrapped around. 96 | #[inline(always)] 97 | pub fn i64_value(&self) -> (i64, bool) { 98 | let mut lossless = MaybeUninit::uninit(); 99 | let v = unsafe { v8__BigInt__Int64Value(self, lossless.as_mut_ptr()) }; 100 | let lossless = unsafe { lossless.assume_init() }; 101 | (v, lossless) 102 | } 103 | 104 | /// Returns the number of 64-bit words needed to store the result of 105 | /// `to_words_array`. 106 | #[inline(always)] 107 | pub fn word_count(&self) -> usize { 108 | unsafe { v8__BigInt__WordCount(self) as usize } 109 | } 110 | 111 | /// Converts this BigInt to a (sign_bit, words) pair. `sign_bit` will be true 112 | /// if this BigInt is negative. If `words` has too few elements, the result will 113 | /// be truncated to fit. 114 | #[inline] 115 | pub fn to_words_array<'a>( 116 | &self, 117 | words: &'a mut [u64], 118 | ) -> (bool, &'a mut [u64]) { 119 | let mut sign_bit = MaybeUninit::uninit(); 120 | let mut word_count = words.len() as int; 121 | unsafe { 122 | v8__BigInt__ToWordsArray( 123 | self, 124 | sign_bit.as_mut_ptr(), 125 | &mut word_count, 126 | words.as_mut_ptr(), 127 | ); 128 | } 129 | 130 | let sign_bit = unsafe { sign_bit.assume_init() }; 131 | debug_assert!(sign_bit == 0 || sign_bit == 1); 132 | let word_count = word_count as usize; 133 | 134 | ( 135 | sign_bit == 1, 136 | if word_count < words.len() { 137 | &mut words[..word_count] 138 | } else { 139 | words 140 | }, 141 | ) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/binding.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "support.h" 8 | 9 | /** 10 | * Types defined here will be compiled with bindgen 11 | * and made available in `crate::binding` in rust. 12 | */ 13 | 14 | static size_t v8__ScriptOrigin_SIZE = sizeof(v8::ScriptOrigin); 15 | 16 | static size_t cppgc__Member_SIZE = sizeof(cppgc::Member); 17 | static size_t cppgc__WeakMember_SIZE = sizeof(cppgc::WeakMember); 18 | 19 | static size_t v8__TracedReference_SIZE = sizeof(v8::TracedReference); 20 | 21 | static size_t v8__Eternal_SIZE = sizeof(v8::Eternal); 22 | 23 | static size_t v8__String__ValueView_SIZE = sizeof(v8::String::ValueView); 24 | 25 | static int v8__String__kMaxLength = v8::String::kMaxLength; 26 | 27 | static size_t v8__TypedArray__kMaxByteLength = v8::TypedArray::kMaxByteLength; 28 | 29 | static size_t v8__TYPED_ARRAY_MAX_SIZE_IN_HEAP = 30 | V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP; 31 | 32 | #define TYPED_ARRAY_MAX_LENGTH(name) \ 33 | static size_t v8__##name##__kMaxLength = v8::name::kMaxLength; 34 | EACH_TYPED_ARRAY(TYPED_ARRAY_MAX_LENGTH) 35 | #undef TYPED_ARRAY_MAX_LENGTH 36 | 37 | using v8__CFunction = v8::CFunction; 38 | using v8__CFunctionInfo = v8::CFunctionInfo; 39 | using v8__FastOneByteString = v8::FastOneByteString; 40 | using v8__Isolate__UseCounterFeature = v8::Isolate::UseCounterFeature; 41 | using v8__String__WriteFlags = v8::String::WriteFlags; 42 | using v8__ModuleImportPhase = v8::ModuleImportPhase; 43 | using v8__HeapStatistics = v8::HeapStatistics; 44 | using v8__HeapSpaceStatistics = v8::HeapSpaceStatistics; 45 | using v8__GCType = v8::GCType; 46 | using v8__GCCallbackFlags = v8::GCCallbackFlags; 47 | 48 | static uint32_t v8__MAJOR_VERSION = V8_MAJOR_VERSION; 49 | static uint32_t v8__MINOR_VERSION = V8_MINOR_VERSION; 50 | static uint32_t v8__BUILD_NUMBER = V8_BUILD_NUMBER; 51 | static uint32_t v8__PATCH_LEVEL = V8_PATCH_LEVEL; 52 | static const char* v8__VERSION_STRING = V8_VERSION_STRING; 53 | 54 | // NOTE: This class is never used and only serves as a reference for 55 | // the OneByteConst struct created on Rust-side. 56 | class ExternalConstOneByteStringResource 57 | : public v8::String::ExternalOneByteStringResource { 58 | public: 59 | ExternalConstOneByteStringResource(int length) : _length(length) { 60 | static_assert(offsetof(ExternalConstOneByteStringResource, _length) == 61 | sizeof(size_t) * 2, 62 | "ExternalConstOneByteStringResource's length was not at " 63 | "offset of sizeof(size_t) * 2"); 64 | static_assert( 65 | sizeof(ExternalConstOneByteStringResource) == sizeof(size_t) * 3, 66 | "ExternalConstOneByteStringResource size was not sizeof(size_t) * 3"); 67 | static_assert( 68 | alignof(ExternalConstOneByteStringResource) == sizeof(size_t), 69 | "ExternalConstOneByteStringResource align was not sizeof(size_t)"); 70 | } 71 | const char* data() const override { return nullptr; } 72 | size_t length() const override { return _length; } 73 | void Dispose() override {} 74 | 75 | private: 76 | const int _length; 77 | }; 78 | -------------------------------------------------------------------------------- /src/binding.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | #![allow(non_upper_case_globals)] 3 | #![allow(non_camel_case_types)] 4 | #![allow(non_snake_case)] 5 | #![allow(unsafe_op_in_unsafe_fn)] 6 | include!(env!("RUSTY_V8_SRC_BINDING_PATH")); 7 | -------------------------------------------------------------------------------- /src/date.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | 3 | use crate::Context; 4 | use crate::Date; 5 | use crate::HandleScope; 6 | use crate::Local; 7 | 8 | unsafe extern "C" { 9 | fn v8__Date__New(context: *const Context, value: f64) -> *const Date; 10 | fn v8__Date__ValueOf(this: *const Date) -> f64; 11 | } 12 | 13 | /// An instance of the built-in Date constructor (ECMA-262, 15.9). 14 | impl Date { 15 | #[inline(always)] 16 | pub fn new<'s>( 17 | scope: &mut HandleScope<'s>, 18 | value: f64, 19 | ) -> Option> { 20 | unsafe { 21 | scope.cast_local(|sd| v8__Date__New(sd.get_current_context(), value)) 22 | } 23 | } 24 | 25 | /// A specialization of Value::NumberValue that is more efficient 26 | /// because we know the structure of this object. 27 | #[inline(always)] 28 | pub fn value_of(&self) -> f64 { 29 | unsafe { v8__Date__ValueOf(self) } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/external.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | 3 | use std::ffi::c_void; 4 | 5 | use crate::External; 6 | use crate::HandleScope; 7 | use crate::Isolate; 8 | use crate::Local; 9 | 10 | unsafe extern "C" { 11 | fn v8__External__New( 12 | isolate: *mut Isolate, 13 | value: *mut c_void, 14 | ) -> *const External; 15 | fn v8__External__Value(this: *const External) -> *mut c_void; 16 | } 17 | 18 | impl External { 19 | #[inline(always)] 20 | #[allow(clippy::not_unsafe_ptr_arg_deref)] 21 | pub fn new<'s>( 22 | scope: &mut HandleScope<'s, ()>, 23 | value: *mut c_void, 24 | ) -> Local<'s, Self> { 25 | unsafe { 26 | scope.cast_local(|sd| v8__External__New(sd.get_isolate_ptr(), value)) 27 | } 28 | .unwrap() 29 | } 30 | 31 | #[inline(always)] 32 | pub fn value(&self) -> *mut c_void { 33 | unsafe { v8__External__Value(self) } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/external_references.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | 3 | use crate::FunctionCallback; 4 | use crate::IndexedDefinerCallback; 5 | use crate::IndexedDeleterCallback; 6 | use crate::IndexedGetterCallback; 7 | use crate::IndexedQueryCallback; 8 | use crate::IndexedSetterCallback; 9 | use crate::MessageCallback; 10 | use crate::NamedDefinerCallback; 11 | use crate::NamedDeleterCallback; 12 | use crate::NamedGetterCallback; 13 | use crate::NamedQueryCallback; 14 | use crate::NamedSetterCallback; 15 | use crate::PropertyEnumeratorCallback; 16 | use crate::fast_api::CFunctionInfo; 17 | use std::ffi::c_void; 18 | use std::fmt::Debug; 19 | 20 | #[derive(Clone, Copy)] 21 | pub union ExternalReference { 22 | pub function: FunctionCallback, 23 | pub named_getter: NamedGetterCallback, 24 | pub named_setter: NamedSetterCallback, 25 | pub named_definer: NamedDefinerCallback, 26 | pub named_deleter: NamedDeleterCallback, 27 | pub named_query: NamedQueryCallback, 28 | pub indexed_getter: IndexedGetterCallback, 29 | pub indexed_setter: IndexedSetterCallback, 30 | pub indexed_definer: IndexedDefinerCallback, 31 | pub indexed_deleter: IndexedDeleterCallback, 32 | pub indexed_query: IndexedQueryCallback, 33 | pub enumerator: PropertyEnumeratorCallback, 34 | pub message: MessageCallback, 35 | pub pointer: *mut c_void, 36 | pub type_info: *const CFunctionInfo, 37 | } 38 | 39 | impl Debug for ExternalReference { 40 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 41 | // SAFETY: All union fields are the same size 42 | unsafe { (self.pointer).fmt(f) } 43 | } 44 | } 45 | 46 | impl PartialEq for ExternalReference { 47 | fn eq(&self, other: &Self) -> bool { 48 | unsafe { self.pointer.eq(&other.pointer) } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/fast_api.rs: -------------------------------------------------------------------------------- 1 | use crate::Isolate; 2 | use crate::Local; 3 | use crate::Value; 4 | use crate::binding::*; 5 | use std::ffi::c_void; 6 | 7 | #[derive(Clone, Copy)] 8 | #[repr(transparent)] 9 | pub struct CFunction(v8__CFunction); 10 | 11 | impl CFunction { 12 | pub const fn new(address: *const c_void, type_info: &CFunctionInfo) -> Self { 13 | Self(v8__CFunction { 14 | address_: address, 15 | type_info_: &type_info.0, 16 | }) 17 | } 18 | 19 | pub const fn address(&self) -> *const c_void { 20 | self.0.address_ 21 | } 22 | 23 | pub const fn type_info(&self) -> &CFunctionInfo { 24 | // SAFETY: We initialize this field with a reference. and 25 | // the layout of CFunctionInfo is identical to v8_CFunctionInfo. 26 | unsafe { &*(self.0.type_info_ as *const CFunctionInfo) } 27 | } 28 | } 29 | 30 | #[repr(transparent)] 31 | pub struct CFunctionInfo(v8__CFunctionInfo); 32 | 33 | impl CFunctionInfo { 34 | /// Construct a struct to hold a CFunction's type information. 35 | /// |return_info| describes the function's return type. 36 | /// |arg_info| is an array of |arg_count| CTypeInfos describing the 37 | /// arguments. Only the last argument may be of the special type 38 | /// CTypeInfo::kCallbackOptionsType. 39 | pub const fn new( 40 | return_info: CTypeInfo, 41 | arg_info: &[CTypeInfo], 42 | repr: Int64Representation, 43 | ) -> Self { 44 | Self(v8__CFunctionInfo { 45 | arg_count_: arg_info.len() as _, 46 | arg_info_: arg_info.as_ptr() as _, 47 | repr_: repr as _, 48 | return_info_: return_info.0, 49 | }) 50 | } 51 | } 52 | 53 | #[derive(Clone, Copy)] 54 | #[repr(u8)] 55 | pub enum Int64Representation { 56 | /// Use numbers to represent 64 bit integers. 57 | Number = v8_CFunctionInfo_Int64Representation_kNumber, 58 | /// Use BigInts to represent 64 bit integers. 59 | BigInt = v8_CFunctionInfo_Int64Representation_kBigInt, 60 | } 61 | 62 | #[derive(Clone, Copy)] 63 | #[repr(transparent)] 64 | pub struct CTypeInfo(v8_CTypeInfo); 65 | 66 | impl CTypeInfo { 67 | pub const fn new(r#type: Type, flags: Flags) -> Self { 68 | Self(v8_CTypeInfo { 69 | flags_: flags.bits(), 70 | sequence_type_: v8_CTypeInfo_SequenceType_kScalar, 71 | type_: r#type as _, 72 | }) 73 | } 74 | } 75 | 76 | #[derive(Clone, Copy)] 77 | #[repr(u8)] 78 | pub enum Type { 79 | Void = v8_CTypeInfo_Type_kVoid, 80 | Bool = v8_CTypeInfo_Type_kBool, 81 | Uint8 = v8_CTypeInfo_Type_kUint8, 82 | Int32 = v8_CTypeInfo_Type_kInt32, 83 | Uint32 = v8_CTypeInfo_Type_kUint32, 84 | Int64 = v8_CTypeInfo_Type_kInt64, 85 | Uint64 = v8_CTypeInfo_Type_kUint64, 86 | Float32 = v8_CTypeInfo_Type_kFloat32, 87 | Float64 = v8_CTypeInfo_Type_kFloat64, 88 | Pointer = v8_CTypeInfo_Type_kPointer, 89 | V8Value = v8_CTypeInfo_Type_kV8Value, 90 | SeqOneByteString = v8_CTypeInfo_Type_kSeqOneByteString, 91 | ApiObject = v8_CTypeInfo_Type_kApiObject, 92 | Any = v8_CTypeInfo_Type_kAny, 93 | CallbackOptions = 255, 94 | } 95 | 96 | impl Type { 97 | // const fn since From is not const 98 | pub const fn as_info(self) -> CTypeInfo { 99 | CTypeInfo::new(self, Flags::empty()) 100 | } 101 | } 102 | 103 | impl From for CTypeInfo { 104 | fn from(t: Type) -> Self { 105 | Self::new(t, Flags::empty()) 106 | } 107 | } 108 | 109 | bitflags::bitflags! { 110 | pub struct Flags: u8 { 111 | /// Must be an ArrayBuffer or TypedArray 112 | const AllowShared = v8_CTypeInfo_Flags_kAllowSharedBit; 113 | /// T must be integral 114 | const EnforceRange = v8_CTypeInfo_Flags_kEnforceRangeBit; 115 | /// T must be integral 116 | const Clamp = v8_CTypeInfo_Flags_kClampBit; 117 | /// T must be float or double 118 | const IsRestricted = v8_CTypeInfo_Flags_kIsRestrictedBit; 119 | } 120 | } 121 | 122 | /// A struct which may be passed to a fast call callback, like so 123 | /// ```c 124 | /// void FastMethodWithOptions(int param, FastApiCallbackOptions& options); 125 | /// ``` 126 | #[repr(C)] 127 | pub struct FastApiCallbackOptions<'a> { 128 | pub isolate: *mut Isolate, 129 | /// The `data` passed to the FunctionTemplate constructor, or `undefined`. 130 | pub data: Local<'a, Value>, 131 | } 132 | 133 | pub type FastApiOneByteString = v8__FastOneByteString; 134 | 135 | impl FastApiOneByteString { 136 | #[inline(always)] 137 | pub fn as_bytes(&self) -> &[u8] { 138 | // Ensure that we never create a null-ptr slice (even a zero-length null-ptr slice 139 | // is invalid because of Rust's niche packing). 140 | if self.data.is_null() { 141 | return &mut []; 142 | } 143 | 144 | // SAFETY: The data is guaranteed to be valid for the length of the string. 145 | unsafe { std::slice::from_raw_parts(self.data as _, self.length as usize) } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/fixed_array.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | use crate::Context; 3 | use crate::Data; 4 | use crate::FixedArray; 5 | use crate::HandleScope; 6 | use crate::Local; 7 | use crate::support::int; 8 | 9 | unsafe extern "C" { 10 | fn v8__FixedArray__Length(this: *const FixedArray) -> int; 11 | 12 | fn v8__FixedArray__Get( 13 | this: *const FixedArray, 14 | context: *const Context, 15 | index: int, 16 | ) -> *const Data; 17 | } 18 | 19 | impl FixedArray { 20 | #[inline(always)] 21 | pub fn length(&self) -> usize { 22 | unsafe { v8__FixedArray__Length(self) as usize } 23 | } 24 | 25 | #[inline(always)] 26 | pub fn get<'s>( 27 | &self, 28 | scope: &mut HandleScope<'s>, 29 | index: usize, 30 | ) -> Option> { 31 | if index >= self.length() { 32 | return None; 33 | } 34 | 35 | unsafe { 36 | scope.cast_local(|sd| { 37 | v8__FixedArray__Get(self, &*sd.get_current_context(), index as int) 38 | }) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/gc.rs: -------------------------------------------------------------------------------- 1 | pub use crate::binding::v8__GCCallbackFlags as GCCallbackFlags; 2 | pub use crate::binding::v8__GCType as GCType; 3 | -------------------------------------------------------------------------------- /src/get_property_names_args_builder.rs: -------------------------------------------------------------------------------- 1 | use crate::PropertyFilter; 2 | 3 | #[derive(Debug, Clone, Copy)] 4 | #[repr(C)] 5 | pub enum KeyConversionMode { 6 | /// kConvertToString will convert integer indices to strings. 7 | ConvertToString, 8 | /// kKeepNumbers will return numbers for integer indices. 9 | KeepNumbers, 10 | NoNumbers, 11 | } 12 | 13 | /// Keys/Properties filter enums: 14 | /// 15 | /// KeyCollectionMode limits the range of collected properties. kOwnOnly limits 16 | /// the collected properties to the given Object only. kIncludesPrototypes will 17 | /// include all keys of the objects's prototype chain as well. 18 | #[derive(Debug, Clone, Copy)] 19 | #[repr(C)] 20 | pub enum KeyCollectionMode { 21 | /// OwnOnly limits the collected properties to the given Object only. 22 | OwnOnly, 23 | /// kIncludesPrototypes will include all keys of the objects's prototype chain 24 | /// as well. 25 | IncludePrototypes, 26 | } 27 | 28 | #[derive(Debug, Clone, Copy)] 29 | #[repr(C)] 30 | pub enum IndexFilter { 31 | /// kIncludesIndices allows for integer indices to be collected. 32 | IncludeIndices, 33 | /// kSkipIndices will exclude integer indices from being collected. 34 | SkipIndices, 35 | } 36 | 37 | pub struct GetPropertyNamesArgs { 38 | pub mode: KeyCollectionMode, 39 | pub property_filter: PropertyFilter, 40 | pub index_filter: IndexFilter, 41 | pub key_conversion: KeyConversionMode, 42 | } 43 | 44 | impl Default for GetPropertyNamesArgs { 45 | fn default() -> Self { 46 | GetPropertyNamesArgs { 47 | mode: KeyCollectionMode::IncludePrototypes, 48 | property_filter: PropertyFilter::ONLY_ENUMERABLE 49 | | PropertyFilter::SKIP_SYMBOLS, 50 | index_filter: IndexFilter::IncludeIndices, 51 | key_conversion: KeyConversionMode::KeepNumbers, 52 | } 53 | } 54 | } 55 | 56 | pub struct GetPropertyNamesArgsBuilder { 57 | mode: KeyCollectionMode, 58 | property_filter: PropertyFilter, 59 | index_filter: IndexFilter, 60 | key_conversion: KeyConversionMode, 61 | } 62 | 63 | impl Default for GetPropertyNamesArgsBuilder { 64 | fn default() -> Self { 65 | Self::new() 66 | } 67 | } 68 | 69 | impl GetPropertyNamesArgsBuilder { 70 | #[inline(always)] 71 | pub fn new() -> Self { 72 | Self { 73 | mode: KeyCollectionMode::IncludePrototypes, 74 | property_filter: PropertyFilter::ONLY_ENUMERABLE 75 | | PropertyFilter::SKIP_SYMBOLS, 76 | index_filter: IndexFilter::IncludeIndices, 77 | key_conversion: KeyConversionMode::KeepNumbers, 78 | } 79 | } 80 | 81 | #[inline(always)] 82 | pub fn build(&self) -> GetPropertyNamesArgs { 83 | GetPropertyNamesArgs { 84 | mode: self.mode, 85 | property_filter: self.property_filter, 86 | index_filter: self.index_filter, 87 | key_conversion: self.key_conversion, 88 | } 89 | } 90 | 91 | #[inline(always)] 92 | pub fn mode( 93 | &mut self, 94 | mode: KeyCollectionMode, 95 | ) -> &mut GetPropertyNamesArgsBuilder { 96 | self.mode = mode; 97 | self 98 | } 99 | 100 | #[inline(always)] 101 | pub fn property_filter( 102 | &mut self, 103 | property_filter: PropertyFilter, 104 | ) -> &mut GetPropertyNamesArgsBuilder { 105 | self.property_filter = property_filter; 106 | self 107 | } 108 | 109 | #[inline(always)] 110 | pub fn index_filter( 111 | &mut self, 112 | index_filter: IndexFilter, 113 | ) -> &mut GetPropertyNamesArgsBuilder { 114 | self.index_filter = index_filter; 115 | self 116 | } 117 | 118 | #[inline(always)] 119 | pub fn key_conversion( 120 | &mut self, 121 | key_conversion: KeyConversionMode, 122 | ) -> &mut Self { 123 | self.key_conversion = key_conversion; 124 | self 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/icu.rs: -------------------------------------------------------------------------------- 1 | use crate::support::char; 2 | 3 | use std::ffi::CString; 4 | 5 | unsafe extern "C" { 6 | fn icu_get_default_locale(output: *mut char, output_len: usize) -> usize; 7 | fn icu_set_default_locale(locale: *const char); 8 | fn udata_setCommonData_74(this: *const u8, error_code: *mut i32); 9 | } 10 | 11 | /// This function bypasses the normal ICU data loading process and allows you to force ICU's system 12 | /// data to come out of a user-specified area in memory. 13 | /// 14 | /// ICU data must be at least 8-aligned, and should be 16-aligned. See 15 | /// https://unicode-org.github.io/icu/userguide/icu_data/ 16 | /// 17 | /// The format of this data is that of the icu common data file, as is generated by the pkgdata 18 | /// tool with mode=common or mode=dll. You can read in a whole common mode file and pass the 19 | /// address to the start of the data, or (with the appropriate link options) pass in the pointer to 20 | /// the data that has been loaded from a dll by the operating system, as shown in this code: 21 | /// 22 | /// ```c++ 23 | /// extern const char U_IMPORT U_ICUDATA_ENTRY_POINT []; 24 | /// // U_ICUDATA_ENTRY_POINT is same as entry point specified to pkgdata tool 25 | /// UErrorCode status = U_ZERO_ERROR; 26 | /// 27 | /// udata_setCommonData(&U_ICUDATA_ENTRY_POINT, &status); 28 | /// ``` 29 | /// 30 | /// It is important that the declaration be as above. The entry point must not be declared as an 31 | /// extern void*. 32 | /// 33 | /// Starting with ICU 4.4, it is possible to set several data packages, one per call to this 34 | /// function. udata_open() will look for data in the multiple data packages in the order in which 35 | /// they were set. The position of the linked-in or default-name ICU .data package in the search 36 | /// list depends on when the first data item is loaded that is not contained in the already 37 | /// explicitly set packages. If data was loaded implicitly before the first call to this function 38 | /// (for example, via opening a converter, constructing a UnicodeString from default-codepage data, 39 | /// using formatting or collation APIs, etc.), then the default data will be first in the list. 40 | /// 41 | /// This function has no effect on application (non ICU) data. See udata_setAppData() for similar 42 | /// functionality for application data. 43 | // TODO(ry) Map error code to something useful. 44 | #[inline(always)] 45 | pub fn set_common_data_74(data: &'static [u8]) -> Result<(), i32> { 46 | let mut error_code = 0i32; 47 | unsafe { 48 | udata_setCommonData_74(data.as_ptr(), &mut error_code); 49 | } 50 | if error_code == 0 { 51 | Ok(()) 52 | } else { 53 | Err(error_code) 54 | } 55 | } 56 | 57 | /// Returns BCP47 language tag. 58 | pub fn get_language_tag() -> String { 59 | let mut output = [0u8; 1024]; 60 | let len = unsafe { 61 | icu_get_default_locale(output.as_mut_ptr() as *mut char, output.len()) 62 | }; 63 | std::str::from_utf8(&output[..len]).unwrap().to_owned() 64 | } 65 | 66 | pub fn set_default_locale(locale: &str) { 67 | unsafe { 68 | let c_str = CString::new(locale).expect("Invalid locale"); 69 | icu_set_default_locale(c_str.as_ptr()); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/json.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | //! A JSON Parser and Stringifier. 3 | use crate::Context; 4 | use crate::HandleScope; 5 | use crate::Local; 6 | use crate::String; 7 | use crate::Value; 8 | 9 | unsafe extern "C" { 10 | fn v8__JSON__Parse( 11 | context: *const Context, 12 | json_string: *const String, 13 | ) -> *const Value; 14 | fn v8__JSON__Stringify( 15 | context: *const Context, 16 | json_object: *const Value, 17 | ) -> *const String; 18 | } 19 | 20 | /// Tries to parse the string `json_string` and returns it as value if 21 | /// successful. 22 | #[inline(always)] 23 | pub fn parse<'s>( 24 | scope: &mut HandleScope<'s>, 25 | json_string: Local<'_, String>, 26 | ) -> Option> { 27 | unsafe { 28 | scope 29 | .cast_local(|sd| v8__JSON__Parse(sd.get_current_context(), &*json_string)) 30 | } 31 | } 32 | 33 | /// Tries to stringify the JSON-serializable object `json_object` and returns 34 | /// it as string if successful. 35 | #[inline(always)] 36 | pub fn stringify<'s>( 37 | scope: &mut HandleScope<'s>, 38 | json_object: Local<'_, Value>, 39 | ) -> Option> { 40 | unsafe { 41 | scope.cast_local(|sd| { 42 | v8__JSON__Stringify(sd.get_current_context(), &*json_object) 43 | }) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | 3 | //! # Example 4 | //! 5 | //! ```rust 6 | //! let platform = v8::new_default_platform(0, false).make_shared(); 7 | //! v8::V8::initialize_platform(platform); 8 | //! v8::V8::initialize(); 9 | //! 10 | //! let isolate = &mut v8::Isolate::new(Default::default()); 11 | //! 12 | //! let scope = &mut v8::HandleScope::new(isolate); 13 | //! let context = v8::Context::new(scope, Default::default()); 14 | //! let scope = &mut v8::ContextScope::new(scope, context); 15 | //! 16 | //! let code = v8::String::new(scope, "'Hello' + ' World!'").unwrap(); 17 | //! println!("javascript code: {}", code.to_rust_string_lossy(scope)); 18 | //! 19 | //! let script = v8::Script::compile(scope, code, None).unwrap(); 20 | //! let result = script.run(scope).unwrap(); 21 | //! let result = result.to_string(scope).unwrap(); 22 | //! println!("result: {}", result.to_rust_string_lossy(scope)); 23 | //! ``` 24 | 25 | #![allow(clippy::missing_safety_doc)] 26 | 27 | #[macro_use] 28 | extern crate bitflags; 29 | 30 | mod array_buffer; 31 | mod array_buffer_view; 32 | mod bigint; 33 | mod binding; 34 | mod context; 35 | pub use context::ContextOptions; 36 | pub mod cppgc; 37 | mod data; 38 | mod date; 39 | mod exception; 40 | mod external; 41 | mod external_references; 42 | pub mod fast_api; 43 | mod fixed_array; 44 | mod function; 45 | mod gc; 46 | mod get_property_names_args_builder; 47 | mod handle; 48 | pub mod icu; 49 | mod isolate; 50 | mod isolate_create_params; 51 | mod microtask; 52 | mod module; 53 | mod name; 54 | mod number; 55 | mod object; 56 | mod platform; 57 | mod primitive_array; 58 | mod primitives; 59 | mod private; 60 | mod promise; 61 | mod property_attribute; 62 | mod property_descriptor; 63 | mod property_filter; 64 | mod property_handler_flags; 65 | mod proxy; 66 | mod regexp; 67 | mod scope; 68 | mod script; 69 | mod script_or_module; 70 | mod shared_array_buffer; 71 | mod snapshot; 72 | mod string; 73 | mod support; 74 | mod symbol; 75 | mod template; 76 | mod typed_array; 77 | mod unbound_module_script; 78 | mod unbound_script; 79 | mod value; 80 | mod value_deserializer; 81 | mod value_serializer; 82 | mod wasm; 83 | 84 | pub mod inspector; 85 | pub mod json; 86 | pub mod script_compiler; 87 | // This module is intentionally named "V8" rather than "v8" to match the 88 | // C++ namespace "v8::V8". 89 | #[allow(non_snake_case)] 90 | pub mod V8; 91 | 92 | pub use array_buffer::*; 93 | pub use data::*; 94 | pub use exception::*; 95 | pub use external_references::ExternalReference; 96 | pub use function::*; 97 | pub use gc::*; 98 | pub use get_property_names_args_builder::*; 99 | pub use handle::Eternal; 100 | pub use handle::Global; 101 | pub use handle::Handle; 102 | pub use handle::Local; 103 | pub use handle::SealedLocal; 104 | pub use handle::TracedReference; 105 | pub use handle::Weak; 106 | pub use isolate::GarbageCollectionType; 107 | pub use isolate::HeapSpaceStatistics; 108 | pub use isolate::HeapStatistics; 109 | pub use isolate::HostCreateShadowRealmContextCallback; 110 | pub use isolate::HostImportModuleDynamicallyCallback; 111 | pub use isolate::HostImportModuleWithPhaseDynamicallyCallback; 112 | pub use isolate::HostInitializeImportMetaObjectCallback; 113 | pub use isolate::Isolate; 114 | pub use isolate::IsolateHandle; 115 | pub use isolate::MemoryPressureLevel; 116 | pub use isolate::MessageCallback; 117 | pub use isolate::MessageErrorLevel; 118 | pub use isolate::MicrotasksPolicy; 119 | pub use isolate::ModuleImportPhase; 120 | pub use isolate::NearHeapLimitCallback; 121 | pub use isolate::OomDetails; 122 | pub use isolate::OomErrorCallback; 123 | pub use isolate::OwnedIsolate; 124 | pub use isolate::PromiseHook; 125 | pub use isolate::PromiseHookType; 126 | pub use isolate::PromiseRejectCallback; 127 | pub use isolate::TimeZoneDetection; 128 | pub use isolate::UseCounterCallback; 129 | pub use isolate::UseCounterFeature; 130 | pub use isolate::WasmAsyncSuccess; 131 | pub use isolate_create_params::CreateParams; 132 | pub use microtask::MicrotaskQueue; 133 | pub use module::*; 134 | pub use object::*; 135 | pub use platform::Platform; 136 | pub use platform::new_default_platform; 137 | pub use platform::new_single_threaded_default_platform; 138 | pub use platform::new_unprotected_default_platform; 139 | pub use primitives::*; 140 | pub use promise::{PromiseRejectEvent, PromiseRejectMessage, PromiseState}; 141 | pub use property_attribute::*; 142 | pub use property_descriptor::*; 143 | pub use property_filter::*; 144 | pub use property_handler_flags::*; 145 | pub use regexp::RegExpCreationFlags; 146 | pub use scope::AllowJavascriptExecutionScope; 147 | pub use scope::CallbackScope; 148 | pub use scope::ContextScope; 149 | pub use scope::DisallowJavascriptExecutionScope; 150 | pub use scope::EscapableHandleScope; 151 | pub use scope::HandleScope; 152 | pub use scope::OnFailure; 153 | pub use scope::TryCatch; 154 | pub use script::ScriptOrigin; 155 | pub use script_compiler::CachedData; 156 | pub use snapshot::FunctionCodeHandling; 157 | pub use snapshot::StartupData; 158 | pub use string::Encoding; 159 | pub use string::NewStringType; 160 | pub use string::OneByteConst; 161 | pub use string::ValueView; 162 | pub use string::ValueViewData; 163 | pub use string::WriteFlags; 164 | pub use string::WriteOptions; 165 | pub use support::SharedPtr; 166 | pub use support::SharedRef; 167 | pub use support::UniquePtr; 168 | pub use support::UniqueRef; 169 | pub use template::*; 170 | pub use value_deserializer::ValueDeserializer; 171 | pub use value_deserializer::ValueDeserializerHelper; 172 | pub use value_deserializer::ValueDeserializerImpl; 173 | pub use value_serializer::ValueSerializer; 174 | pub use value_serializer::ValueSerializerHelper; 175 | pub use value_serializer::ValueSerializerImpl; 176 | pub use wasm::CompiledWasmModule; 177 | pub use wasm::WasmStreaming; 178 | 179 | /// https://v8.dev/docs/version-numbers 180 | pub const MAJOR_VERSION: u32 = binding::v8__MAJOR_VERSION; 181 | /// https://v8.dev/docs/version-numbers 182 | pub const MINOR_VERSION: u32 = binding::v8__MINOR_VERSION; 183 | /// https://v8.dev/docs/version-numbers 184 | pub const BUILD_NUMBER: u32 = binding::v8__BUILD_NUMBER; 185 | /// https://v8.dev/docs/version-numbers 186 | pub const PATCH_LEVEL: u32 = binding::v8__PATCH_LEVEL; 187 | /// https://v8.dev/docs/version-numbers 188 | pub const VERSION_STRING: &str = 189 | // TODO: cleanup when Result::unwrap is const stable. 190 | match binding::v8__VERSION_STRING.to_str() { 191 | Ok(v) => v, 192 | Err(_) => panic!("Unable to convert CStr to &str??"), 193 | }; 194 | 195 | // TODO(piscisaureus): Ideally this trait would not be exported. 196 | pub use support::MapFnTo; 197 | 198 | pub const TYPED_ARRAY_MAX_SIZE_IN_HEAP: usize = 199 | binding::v8__TYPED_ARRAY_MAX_SIZE_IN_HEAP as _; 200 | 201 | #[cfg(test)] 202 | pub(crate) fn initialize_v8() { 203 | use std::sync::Once; 204 | 205 | static INIT: Once = Once::new(); 206 | INIT.call_once(|| { 207 | V8::initialize_platform(new_default_platform(0, false).make_shared()); 208 | V8::initialize(); 209 | }); 210 | } 211 | -------------------------------------------------------------------------------- /src/microtask.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | 3 | use crate::Function; 4 | use crate::Isolate; 5 | use crate::Local; 6 | use crate::MicrotasksPolicy; 7 | use crate::UniqueRef; 8 | use crate::support::Opaque; 9 | use crate::support::int; 10 | 11 | unsafe extern "C" { 12 | fn v8__MicrotaskQueue__New( 13 | isolate: *mut Isolate, 14 | policy: MicrotasksPolicy, 15 | ) -> *mut MicrotaskQueue; 16 | fn v8__MicrotaskQueue__DESTRUCT(queue: *mut MicrotaskQueue); 17 | fn v8__MicrotaskQueue__PerformCheckpoint( 18 | isolate: *mut Isolate, 19 | queue: *const MicrotaskQueue, 20 | ); 21 | fn v8__MicrotaskQueue__IsRunningMicrotasks( 22 | queue: *const MicrotaskQueue, 23 | ) -> bool; 24 | fn v8__MicrotaskQueue__GetMicrotasksScopeDepth( 25 | queue: *const MicrotaskQueue, 26 | ) -> int; 27 | fn v8__MicrotaskQueue__EnqueueMicrotask( 28 | isolate: *mut Isolate, 29 | queue: *const MicrotaskQueue, 30 | microtask: *const Function, 31 | ); 32 | } 33 | 34 | /// Represents the microtask queue, where microtasks are stored and processed. 35 | /// https://html.spec.whatwg.org/multipage/webappapis.html#microtask-queue 36 | /// https://html.spec.whatwg.org/multipage/webappapis.html#enqueuejob(queuename,-job,-arguments) 37 | /// https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint 38 | /// 39 | /// A MicrotaskQueue instance may be associated to multiple Contexts by passing 40 | /// it to Context::New(), and they can be detached by Context::DetachGlobal(). 41 | /// The embedder must keep the MicrotaskQueue instance alive until all associated 42 | /// Contexts are gone or detached. 43 | /// 44 | /// Use the same instance of MicrotaskQueue for all Contexts that may access each 45 | /// other synchronously. E.g. for Web embedding, use the same instance for all 46 | /// origins that share the same URL scheme and eTLD+1. 47 | #[repr(C)] 48 | #[derive(Debug)] 49 | pub struct MicrotaskQueue(Opaque); 50 | 51 | impl MicrotaskQueue { 52 | pub fn new( 53 | isolate: &mut Isolate, 54 | policy: MicrotasksPolicy, 55 | ) -> UniqueRef { 56 | unsafe { UniqueRef::from_raw(v8__MicrotaskQueue__New(isolate, policy)) } 57 | } 58 | 59 | pub fn enqueue_microtask( 60 | &self, 61 | isolate: &mut Isolate, 62 | microtask: Local, 63 | ) { 64 | unsafe { v8__MicrotaskQueue__EnqueueMicrotask(isolate, self, &*microtask) } 65 | } 66 | 67 | /// Adds a callback to notify the embedder after microtasks were run. The 68 | /// callback is triggered by explicit RunMicrotasks call or automatic 69 | /// microtasks execution (see Isolate::SetMicrotasksPolicy). 70 | /// 71 | /// Callback will trigger even if microtasks were attempted to run, 72 | /// but the microtasks queue was empty and no single microtask was actually 73 | /// executed. 74 | /// 75 | /// Executing scripts inside the callback will not re-trigger microtasks and 76 | /// the callback. 77 | pub fn perform_checkpoint(&self, isolate: &mut Isolate) { 78 | unsafe { 79 | v8__MicrotaskQueue__PerformCheckpoint(isolate, self); 80 | } 81 | } 82 | 83 | /// Removes callback that was installed by AddMicrotasksCompletedCallback. 84 | pub fn is_running_microtasks(&self) -> bool { 85 | unsafe { v8__MicrotaskQueue__IsRunningMicrotasks(self) } 86 | } 87 | 88 | /// Returns the current depth of nested MicrotasksScope that has kRunMicrotasks. 89 | pub fn get_microtasks_scope_depth(&self) -> i32 { 90 | unsafe { v8__MicrotaskQueue__GetMicrotasksScopeDepth(self) } 91 | } 92 | } 93 | 94 | impl Drop for MicrotaskQueue { 95 | fn drop(&mut self) { 96 | unsafe { v8__MicrotaskQueue__DESTRUCT(self) } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/name.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | 3 | use std::num::NonZeroI32; 4 | 5 | use crate::Name; 6 | use crate::support::int; 7 | 8 | unsafe extern "C" { 9 | fn v8__Name__GetIdentityHash(this: *const Name) -> int; 10 | } 11 | 12 | impl Name { 13 | /// Returns the V8 hash value for this value. The current implementation 14 | /// uses a hidden property to store the identity hash. 15 | /// 16 | /// The return value will never be 0. Also, it is not guaranteed to be 17 | /// unique. 18 | #[inline(always)] 19 | pub fn get_identity_hash(&self) -> NonZeroI32 { 20 | unsafe { NonZeroI32::new_unchecked(v8__Name__GetIdentityHash(self)) } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/number.rs: -------------------------------------------------------------------------------- 1 | use std::alloc::Layout; 2 | use std::ptr::NonNull; 3 | 4 | use crate::HandleScope; 5 | use crate::Int32; 6 | use crate::Integer; 7 | use crate::Isolate; 8 | use crate::Local; 9 | use crate::Number; 10 | use crate::Uint32; 11 | 12 | unsafe extern "C" { 13 | fn v8__Number__New(isolate: *mut Isolate, value: f64) -> *const Number; 14 | fn v8__Number__Value(this: *const Number) -> f64; 15 | fn v8__Integer__New(isolate: *mut Isolate, value: i32) -> *const Integer; 16 | fn v8__Integer__NewFromUnsigned( 17 | isolate: *mut Isolate, 18 | value: u32, 19 | ) -> *const Integer; 20 | fn v8__Integer__Value(this: *const Integer) -> i64; 21 | fn v8__Uint32__Value(this: *const Uint32) -> u32; 22 | fn v8__Int32__Value(this: *const Int32) -> i32; 23 | } 24 | 25 | impl Number { 26 | #[inline(always)] 27 | pub fn new<'s>( 28 | scope: &mut HandleScope<'s, ()>, 29 | value: f64, 30 | ) -> Local<'s, Number> { 31 | unsafe { 32 | scope.cast_local(|sd| v8__Number__New(sd.get_isolate_ptr(), value)) 33 | } 34 | .unwrap() 35 | } 36 | 37 | #[inline(always)] 38 | pub fn value(&self) -> f64 { 39 | unsafe { v8__Number__Value(self) } 40 | } 41 | } 42 | 43 | impl Integer { 44 | #[inline(always)] 45 | pub fn new<'s>( 46 | scope: &mut HandleScope<'s, ()>, 47 | value: i32, 48 | ) -> Local<'s, Integer> { 49 | unsafe { 50 | scope.cast_local(|sd| v8__Integer__New(sd.get_isolate_ptr(), value)) 51 | } 52 | .unwrap() 53 | } 54 | 55 | #[inline(always)] 56 | pub fn new_from_unsigned<'s>( 57 | scope: &mut HandleScope<'s, ()>, 58 | value: u32, 59 | ) -> Local<'s, Integer> { 60 | unsafe { 61 | scope.cast_local(|sd| { 62 | v8__Integer__NewFromUnsigned(sd.get_isolate_ptr(), value) 63 | }) 64 | } 65 | .unwrap() 66 | } 67 | 68 | #[inline(always)] 69 | pub fn value(&self) -> i64 { 70 | unsafe { v8__Integer__Value(self) } 71 | } 72 | 73 | /// Internal helper function to produce a handle containing a SMI zero value, 74 | /// without the need for the caller to provide (or have entered) a 75 | /// `HandleScope`. 76 | #[inline(always)] 77 | pub(crate) fn zero<'s>() -> Local<'s, Integer> { 78 | // The SMI representation of zero is also zero. In debug builds, double 79 | // check this, so in the unlikely event that V8 changes its internal 80 | // representation of SMIs such that this invariant no longer holds, we'd 81 | // catch it. 82 | static ZERO_SMI: usize = 0; 83 | let zero_raw = &ZERO_SMI as *const _ as *mut Self; 84 | let zero_nn = unsafe { NonNull::new_unchecked(zero_raw) }; 85 | let zero_local = unsafe { Local::from_non_null(zero_nn) }; 86 | debug_assert_eq!(Layout::new::(), Layout::new::>()); 87 | debug_assert_eq!(zero_local.value(), 0); 88 | zero_local 89 | } 90 | } 91 | 92 | impl Uint32 { 93 | #[inline(always)] 94 | pub fn value(&self) -> u32 { 95 | unsafe { v8__Uint32__Value(self) } 96 | } 97 | } 98 | 99 | impl Int32 { 100 | #[inline(always)] 101 | pub fn value(&self) -> i32 { 102 | unsafe { v8__Int32__Value(self) } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/primitive_array.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | use crate::HandleScope; 3 | use crate::Isolate; 4 | use crate::Local; 5 | use crate::Primitive; 6 | use crate::PrimitiveArray; 7 | use crate::support::int; 8 | 9 | unsafe extern "C" { 10 | fn v8__PrimitiveArray__New( 11 | isolate: *mut Isolate, 12 | length: int, 13 | ) -> *const PrimitiveArray; 14 | 15 | fn v8__PrimitiveArray__Length(this: *const PrimitiveArray) -> int; 16 | 17 | fn v8__PrimitiveArray__Set( 18 | this: *const PrimitiveArray, 19 | isolate: *mut Isolate, 20 | index: int, 21 | item: *const Primitive, 22 | ); 23 | 24 | fn v8__PrimitiveArray__Get( 25 | this: *const PrimitiveArray, 26 | isolate: *mut Isolate, 27 | index: int, 28 | ) -> *const Primitive; 29 | } 30 | 31 | impl PrimitiveArray { 32 | #[inline(always)] 33 | pub fn new<'s>( 34 | scope: &mut HandleScope<'s>, 35 | length: usize, 36 | ) -> Local<'s, PrimitiveArray> { 37 | unsafe { 38 | scope.cast_local(|sd| { 39 | v8__PrimitiveArray__New(sd.get_isolate_ptr(), length as int) 40 | }) 41 | } 42 | .unwrap() 43 | } 44 | 45 | #[inline(always)] 46 | pub fn length(&self) -> usize { 47 | unsafe { v8__PrimitiveArray__Length(self) as usize } 48 | } 49 | 50 | #[inline(always)] 51 | pub fn set( 52 | &self, 53 | scope: &mut HandleScope, 54 | index: usize, 55 | item: Local<'_, Primitive>, 56 | ) { 57 | unsafe { 58 | v8__PrimitiveArray__Set( 59 | self, 60 | scope.get_isolate_ptr(), 61 | index as int, 62 | &*item, 63 | ); 64 | } 65 | } 66 | 67 | #[inline(always)] 68 | pub fn get<'s>( 69 | &self, 70 | scope: &mut HandleScope<'s>, 71 | index: usize, 72 | ) -> Local<'s, Primitive> { 73 | unsafe { 74 | scope.cast_local(|sd| { 75 | v8__PrimitiveArray__Get(self, sd.get_isolate_ptr(), index as int) 76 | }) 77 | } 78 | .unwrap() 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/primitives.rs: -------------------------------------------------------------------------------- 1 | use crate::Boolean; 2 | use crate::Local; 3 | use crate::Primitive; 4 | use crate::isolate::Isolate; 5 | 6 | unsafe extern "C" { 7 | fn v8__Null(isolate: *mut Isolate) -> *const Primitive; 8 | fn v8__Undefined(isolate: *mut Isolate) -> *const Primitive; 9 | 10 | fn v8__Boolean__New(isolate: *mut Isolate, value: bool) -> *const Boolean; 11 | } 12 | 13 | #[inline(always)] 14 | pub fn null<'a, R>(scope: &mut R) -> Local<'a, Primitive> 15 | where 16 | R: AsMut, 17 | { 18 | unsafe { Local::from_raw_unchecked(v8__Null(scope.as_mut())) } 19 | } 20 | 21 | #[inline(always)] 22 | pub fn undefined<'a, R>(scope: &mut R) -> Local<'a, Primitive> 23 | where 24 | R: AsMut, 25 | { 26 | unsafe { Local::from_raw_unchecked(v8__Undefined(scope.as_mut())) } 27 | } 28 | 29 | impl Boolean { 30 | #[inline(always)] 31 | pub fn new<'a, R>(scope: &mut R, value: bool) -> Local<'a, Boolean> 32 | where 33 | R: AsMut, 34 | { 35 | unsafe { 36 | Local::from_raw_unchecked(v8__Boolean__New(scope.as_mut(), value)) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/private.rs: -------------------------------------------------------------------------------- 1 | use crate::HandleScope; 2 | use crate::Isolate; 3 | use crate::Local; 4 | use crate::Private; 5 | use crate::String; 6 | use crate::Value; 7 | 8 | unsafe extern "C" { 9 | fn v8__Private__New( 10 | isolate: *mut Isolate, 11 | name: *const String, 12 | ) -> *const Private; 13 | fn v8__Private__ForApi( 14 | isolate: *mut Isolate, 15 | name: *const String, 16 | ) -> *const Private; 17 | fn v8__Private__Name(this: *const Private) -> *const Value; 18 | } 19 | 20 | impl Private { 21 | /// Create a private symbol. If name is not empty, it will be the description. 22 | #[inline(always)] 23 | pub fn new<'s>( 24 | scope: &mut HandleScope<'s, ()>, 25 | name: Option>, 26 | ) -> Local<'s, Private> { 27 | unsafe { 28 | scope.cast_local(|sd| { 29 | v8__Private__New( 30 | sd.get_isolate_ptr(), 31 | name.map_or_else(std::ptr::null, |v| &*v), 32 | ) 33 | }) 34 | } 35 | .unwrap() 36 | } 37 | 38 | /// Retrieve a global private symbol. If a symbol with this name has not 39 | /// been retrieved in the same isolate before, it is created. 40 | /// Note that private symbols created this way are never collected, so 41 | /// they should only be used for statically fixed properties. 42 | /// Also, there is only one global name space for the names used as keys. 43 | /// To minimize the potential for clashes, use qualified names as keys, 44 | /// e.g., "Class#property". 45 | #[inline(always)] 46 | pub fn for_api<'s>( 47 | scope: &mut HandleScope<'s, ()>, 48 | name: Option>, 49 | ) -> Local<'s, Private> { 50 | unsafe { 51 | scope.cast_local(|sd| { 52 | v8__Private__ForApi( 53 | sd.get_isolate_ptr(), 54 | name.map_or_else(std::ptr::null, |v| &*v), 55 | ) 56 | }) 57 | } 58 | .unwrap() 59 | } 60 | 61 | /// Returns the print name string of the private symbol, or undefined if none. 62 | #[inline(always)] 63 | pub fn name<'s>(&self, scope: &mut HandleScope<'s, ()>) -> Local<'s, Value> { 64 | unsafe { scope.cast_local(|_| v8__Private__Name(self)) }.unwrap() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/promise.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use crate::Context; 4 | use crate::Function; 5 | use crate::HandleScope; 6 | use crate::Local; 7 | use crate::Promise; 8 | use crate::PromiseResolver; 9 | use crate::Value; 10 | use crate::support::MaybeBool; 11 | 12 | unsafe extern "C" { 13 | fn v8__Promise__Resolver__New( 14 | context: *const Context, 15 | ) -> *const PromiseResolver; 16 | fn v8__Promise__Resolver__GetPromise( 17 | this: *const PromiseResolver, 18 | ) -> *const Promise; 19 | fn v8__Promise__Resolver__Resolve( 20 | this: *const PromiseResolver, 21 | context: *const Context, 22 | value: *const Value, 23 | ) -> MaybeBool; 24 | fn v8__Promise__Resolver__Reject( 25 | this: *const PromiseResolver, 26 | context: *const Context, 27 | value: *const Value, 28 | ) -> MaybeBool; 29 | fn v8__Promise__State(this: *const Promise) -> PromiseState; 30 | fn v8__Promise__HasHandler(this: *const Promise) -> bool; 31 | fn v8__Promise__Result(this: *const Promise) -> *const Value; 32 | fn v8__Promise__Catch( 33 | this: *const Promise, 34 | context: *const Context, 35 | handler: *const Function, 36 | ) -> *const Promise; 37 | fn v8__Promise__Then( 38 | this: *const Promise, 39 | context: *const Context, 40 | handler: *const Function, 41 | ) -> *const Promise; 42 | fn v8__Promise__Then2( 43 | this: *const Promise, 44 | context: *const Context, 45 | on_fulfilled: *const Function, 46 | on_rejected: *const Function, 47 | ) -> *const Promise; 48 | 49 | fn v8__PromiseRejectMessage__GetPromise( 50 | this: *const PromiseRejectMessage, 51 | ) -> *const Promise; 52 | fn v8__PromiseRejectMessage__GetValue( 53 | this: *const PromiseRejectMessage, 54 | ) -> *const Value; 55 | fn v8__PromiseRejectMessage__GetEvent( 56 | this: *const PromiseRejectMessage, 57 | ) -> PromiseRejectEvent; 58 | } 59 | 60 | #[derive(Debug, PartialEq, Eq)] 61 | #[repr(C)] 62 | pub enum PromiseState { 63 | Pending, 64 | Fulfilled, 65 | Rejected, 66 | } 67 | 68 | impl Promise { 69 | /// Returns the value of the [[PromiseState]] field. 70 | #[inline(always)] 71 | pub fn state(&self) -> PromiseState { 72 | unsafe { v8__Promise__State(self) } 73 | } 74 | 75 | /// Returns true if the promise has at least one derived promise, and 76 | /// therefore resolve/reject handlers (including default handler). 77 | #[inline(always)] 78 | pub fn has_handler(&self) -> bool { 79 | unsafe { v8__Promise__HasHandler(self) } 80 | } 81 | 82 | /// Returns the content of the [[PromiseResult]] field. The Promise must not 83 | /// be pending. 84 | #[inline(always)] 85 | pub fn result<'s>(&self, scope: &mut HandleScope<'s>) -> Local<'s, Value> { 86 | unsafe { scope.cast_local(|_| v8__Promise__Result(self)) }.unwrap() 87 | } 88 | 89 | /// Register a rejection handler with a promise. 90 | /// 91 | /// See `Self::then2`. 92 | #[inline(always)] 93 | pub fn catch<'s>( 94 | &self, 95 | scope: &mut HandleScope<'s>, 96 | handler: Local, 97 | ) -> Option> { 98 | unsafe { 99 | scope.cast_local(|sd| { 100 | v8__Promise__Catch(self, sd.get_current_context(), &*handler) 101 | }) 102 | } 103 | } 104 | 105 | /// Register a resolution handler with a promise. 106 | /// 107 | /// See `Self::then2`. 108 | #[inline(always)] 109 | pub fn then<'s>( 110 | &self, 111 | scope: &mut HandleScope<'s>, 112 | handler: Local, 113 | ) -> Option> { 114 | unsafe { 115 | scope.cast_local(|sd| { 116 | v8__Promise__Then(self, sd.get_current_context(), &*handler) 117 | }) 118 | } 119 | } 120 | 121 | /// Register a resolution/rejection handler with a promise. 122 | /// The handler is given the respective resolution/rejection value as 123 | /// an argument. If the promise is already resolved/rejected, the handler is 124 | /// invoked at the end of turn. 125 | #[inline(always)] 126 | pub fn then2<'s>( 127 | &self, 128 | scope: &mut HandleScope<'s>, 129 | on_fulfilled: Local, 130 | on_rejected: Local, 131 | ) -> Option> { 132 | unsafe { 133 | scope.cast_local(|sd| { 134 | v8__Promise__Then2( 135 | self, 136 | sd.get_current_context(), 137 | &*on_fulfilled, 138 | &*on_rejected, 139 | ) 140 | }) 141 | } 142 | } 143 | } 144 | 145 | impl PromiseResolver { 146 | /// Create a new resolver, along with an associated promise in pending state. 147 | #[inline(always)] 148 | pub fn new<'s>( 149 | scope: &mut HandleScope<'s>, 150 | ) -> Option> { 151 | unsafe { 152 | scope 153 | .cast_local(|sd| v8__Promise__Resolver__New(sd.get_current_context())) 154 | } 155 | } 156 | 157 | /// Extract the associated promise. 158 | #[inline(always)] 159 | pub fn get_promise<'s>( 160 | &self, 161 | scope: &mut HandleScope<'s>, 162 | ) -> Local<'s, Promise> { 163 | unsafe { scope.cast_local(|_| v8__Promise__Resolver__GetPromise(self)) } 164 | .unwrap() 165 | } 166 | 167 | /// Resolve the associated promise with a given value. 168 | /// Ignored if the promise is no longer pending. 169 | #[inline(always)] 170 | pub fn resolve( 171 | &self, 172 | scope: &mut HandleScope, 173 | value: Local<'_, Value>, 174 | ) -> Option { 175 | unsafe { 176 | v8__Promise__Resolver__Resolve( 177 | self, 178 | &*scope.get_current_context(), 179 | &*value, 180 | ) 181 | .into() 182 | } 183 | } 184 | 185 | /// Reject the associated promise with a given value. 186 | /// Ignored if the promise is no longer pending. 187 | #[inline(always)] 188 | pub fn reject( 189 | &self, 190 | scope: &mut HandleScope, 191 | value: Local<'_, Value>, 192 | ) -> Option { 193 | unsafe { 194 | v8__Promise__Resolver__Reject( 195 | self, 196 | &*scope.get_current_context(), 197 | &*value, 198 | ) 199 | .into() 200 | } 201 | } 202 | } 203 | 204 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 205 | #[repr(C)] 206 | pub enum PromiseRejectEvent { 207 | PromiseRejectWithNoHandler, 208 | PromiseHandlerAddedAfterReject, 209 | PromiseRejectAfterResolved, 210 | PromiseResolveAfterResolved, 211 | } 212 | 213 | #[derive(Clone, Copy, Debug)] 214 | #[repr(C)] 215 | pub struct PromiseRejectMessage<'msg>([usize; 3], PhantomData<&'msg ()>); 216 | 217 | impl<'msg> PromiseRejectMessage<'msg> { 218 | #[inline(always)] 219 | pub fn get_promise(&self) -> Local<'msg, Promise> { 220 | unsafe { Local::from_raw(v8__PromiseRejectMessage__GetPromise(self)) } 221 | .unwrap() 222 | } 223 | 224 | #[inline(always)] 225 | pub fn get_event(&self) -> PromiseRejectEvent { 226 | unsafe { v8__PromiseRejectMessage__GetEvent(self) } 227 | } 228 | 229 | #[inline(always)] 230 | pub fn get_value(&self) -> Option> { 231 | unsafe { Local::from_raw(v8__PromiseRejectMessage__GetValue(self)) } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/property_attribute.rs: -------------------------------------------------------------------------------- 1 | #[repr(C)] 2 | #[derive(Debug, Eq, PartialEq)] 3 | pub struct PropertyAttribute(u32); 4 | 5 | impl PropertyAttribute { 6 | /// No property attributes. 7 | pub const NONE: Self = Self(0); 8 | 9 | /// Not writable. Corresponds to 10 | /// `Object.defineProperty(o, "p", { writable: false })`. 11 | pub const READ_ONLY: Self = Self(1 << 0); 12 | 13 | /// Not enumerable. Corresponds to 14 | /// `Object.defineProperty(o, "p", { enumerable: false })`. 15 | pub const DONT_ENUM: Self = Self(1 << 1); 16 | 17 | /// Not configurable. Corresponds to 18 | /// `Object.defineProperty(o, "p", { configurable: false })`. 19 | pub const DONT_DELETE: Self = Self(1 << 2); 20 | 21 | /// Test if no property attributes are set. 22 | #[inline(always)] 23 | pub fn is_none(&self) -> bool { 24 | *self == PropertyAttribute::NONE 25 | } 26 | 27 | /// Test if the read-only property attribute is set. 28 | #[inline(always)] 29 | pub fn is_read_only(&self) -> bool { 30 | self.has(Self::READ_ONLY) 31 | } 32 | 33 | /// Test if the non-enumerable property attribute is set. 34 | #[inline(always)] 35 | pub fn is_dont_enum(&self) -> bool { 36 | self.has(Self::DONT_ENUM) 37 | } 38 | 39 | /// Test if the non-configurable property attribute is set. 40 | #[inline(always)] 41 | pub fn is_dont_delete(&self) -> bool { 42 | self.has(Self::DONT_DELETE) 43 | } 44 | 45 | #[inline(always)] 46 | fn has(&self, that: Self) -> bool { 47 | let Self(lhs) = self; 48 | let Self(rhs) = that; 49 | 0 != lhs & rhs 50 | } 51 | 52 | pub fn as_u32(&self) -> u32 { 53 | let Self(bits) = self; 54 | *bits 55 | } 56 | } 57 | 58 | // Identical to #[derive(Default)] but arguably clearer when made explicit. 59 | impl Default for PropertyAttribute { 60 | fn default() -> Self { 61 | Self::NONE 62 | } 63 | } 64 | 65 | impl std::ops::BitOr for PropertyAttribute { 66 | type Output = Self; 67 | 68 | fn bitor(self, Self(rhs): Self) -> Self { 69 | let Self(lhs) = self; 70 | Self(lhs | rhs) 71 | } 72 | } 73 | 74 | #[test] 75 | fn test_attr() { 76 | assert!(PropertyAttribute::NONE.is_none()); 77 | assert!(!PropertyAttribute::NONE.is_read_only()); 78 | assert!(!PropertyAttribute::NONE.is_dont_enum()); 79 | assert!(!PropertyAttribute::NONE.is_dont_delete()); 80 | 81 | assert!(!PropertyAttribute::READ_ONLY.is_none()); 82 | assert!(PropertyAttribute::READ_ONLY.is_read_only()); 83 | assert!(!PropertyAttribute::READ_ONLY.is_dont_enum()); 84 | assert!(!PropertyAttribute::READ_ONLY.is_dont_delete()); 85 | 86 | assert!(!PropertyAttribute::DONT_ENUM.is_none()); 87 | assert!(!PropertyAttribute::DONT_ENUM.is_read_only()); 88 | assert!(PropertyAttribute::DONT_ENUM.is_dont_enum()); 89 | assert!(!PropertyAttribute::DONT_ENUM.is_dont_delete()); 90 | 91 | assert!(!PropertyAttribute::DONT_DELETE.is_none()); 92 | assert!(!PropertyAttribute::DONT_DELETE.is_read_only()); 93 | assert!(!PropertyAttribute::DONT_DELETE.is_dont_enum()); 94 | assert!(PropertyAttribute::DONT_DELETE.is_dont_delete()); 95 | 96 | assert_eq!(PropertyAttribute::NONE, Default::default()); 97 | assert_eq!( 98 | PropertyAttribute::READ_ONLY, 99 | PropertyAttribute::NONE | PropertyAttribute::READ_ONLY 100 | ); 101 | 102 | let attr = PropertyAttribute::READ_ONLY | PropertyAttribute::DONT_ENUM; 103 | assert!(!attr.is_none()); 104 | assert!(attr.is_read_only()); 105 | assert!(attr.is_dont_enum()); 106 | assert!(!attr.is_dont_delete()); 107 | 108 | let attr = PropertyAttribute::READ_ONLY 109 | | PropertyAttribute::READ_ONLY 110 | | PropertyAttribute::DONT_ENUM; 111 | assert!(!attr.is_none()); 112 | assert!(attr.is_read_only()); 113 | assert!(attr.is_dont_enum()); 114 | assert!(!attr.is_dont_delete()); 115 | } 116 | -------------------------------------------------------------------------------- /src/property_descriptor.rs: -------------------------------------------------------------------------------- 1 | use std::mem::MaybeUninit; 2 | use std::mem::size_of; 3 | 4 | use crate::Local; 5 | use crate::Value; 6 | 7 | unsafe extern "C" { 8 | fn v8__PropertyDescriptor__CONSTRUCT(out: *mut PropertyDescriptor); 9 | fn v8__PropertyDescriptor__CONSTRUCT__Value( 10 | this: *const PropertyDescriptor, 11 | value: *const Value, 12 | ); 13 | fn v8__PropertyDescriptor__CONSTRUCT__Value_Writable( 14 | this: *const PropertyDescriptor, 15 | value: *const Value, 16 | writable: bool, 17 | ); 18 | fn v8__PropertyDescriptor__CONSTRUCT__Get_Set( 19 | this: *const PropertyDescriptor, 20 | get: *const Value, 21 | set: *const Value, 22 | ); 23 | fn v8__PropertyDescriptor__DESTRUCT(this: *mut PropertyDescriptor); 24 | fn v8__PropertyDescriptor__configurable( 25 | this: *const PropertyDescriptor, 26 | ) -> bool; 27 | fn v8__PropertyDescriptor__enumerable( 28 | this: *const PropertyDescriptor, 29 | ) -> bool; 30 | fn v8__PropertyDescriptor__writable(this: *const PropertyDescriptor) -> bool; 31 | fn v8__PropertyDescriptor__value( 32 | this: *const PropertyDescriptor, 33 | ) -> *const Value; 34 | fn v8__PropertyDescriptor__get( 35 | this: *const PropertyDescriptor, 36 | ) -> *const Value; 37 | fn v8__PropertyDescriptor__set( 38 | this: *const PropertyDescriptor, 39 | ) -> *const Value; 40 | fn v8__PropertyDescriptor__has_configurable( 41 | this: *const PropertyDescriptor, 42 | ) -> bool; 43 | fn v8__PropertyDescriptor__has_enumerable( 44 | this: *const PropertyDescriptor, 45 | ) -> bool; 46 | fn v8__PropertyDescriptor__has_writable( 47 | this: *const PropertyDescriptor, 48 | ) -> bool; 49 | fn v8__PropertyDescriptor__has_value(this: *const PropertyDescriptor) 50 | -> bool; 51 | fn v8__PropertyDescriptor__has_get(this: *const PropertyDescriptor) -> bool; 52 | fn v8__PropertyDescriptor__has_set(this: *const PropertyDescriptor) -> bool; 53 | fn v8__PropertyDescriptor__set_enumerable( 54 | this: *mut PropertyDescriptor, 55 | enumerable: bool, 56 | ); 57 | fn v8__PropertyDescriptor__set_configurable( 58 | this: *mut PropertyDescriptor, 59 | configurable: bool, 60 | ); 61 | } 62 | 63 | #[repr(transparent)] 64 | pub struct PropertyDescriptor([usize; 1]); 65 | 66 | const _: () = { 67 | assert!( 68 | size_of::() == size_of::(), 69 | "PropertyDescriptor size is not 1 usize" 70 | ); 71 | }; 72 | 73 | impl Default for PropertyDescriptor { 74 | fn default() -> Self { 75 | Self::new() 76 | } 77 | } 78 | 79 | impl PropertyDescriptor { 80 | pub fn new() -> Self { 81 | let mut this = MaybeUninit::::uninit(); 82 | unsafe { 83 | v8__PropertyDescriptor__CONSTRUCT(this.as_mut_ptr()); 84 | this.assume_init() 85 | } 86 | } 87 | 88 | pub fn new_from_value(value: Local) -> Self { 89 | let mut this = MaybeUninit::::uninit(); 90 | unsafe { 91 | v8__PropertyDescriptor__CONSTRUCT__Value(this.as_mut_ptr(), &*value); 92 | this.assume_init() 93 | } 94 | } 95 | 96 | pub fn new_from_value_writable(value: Local, writable: bool) -> Self { 97 | let mut this = MaybeUninit::::uninit(); 98 | unsafe { 99 | v8__PropertyDescriptor__CONSTRUCT__Value_Writable( 100 | this.as_mut_ptr(), 101 | &*value, 102 | writable, 103 | ); 104 | this.assume_init() 105 | } 106 | } 107 | 108 | pub fn new_from_get_set(get: Local, set: Local) -> Self { 109 | let mut this = MaybeUninit::::uninit(); 110 | unsafe { 111 | v8__PropertyDescriptor__CONSTRUCT__Get_Set( 112 | this.as_mut_ptr(), 113 | &*get, 114 | &*set, 115 | ); 116 | this.assume_init() 117 | } 118 | } 119 | 120 | pub fn configurable(&self) -> bool { 121 | unsafe { v8__PropertyDescriptor__configurable(self) } 122 | } 123 | 124 | pub fn enumerable(&self) -> bool { 125 | unsafe { v8__PropertyDescriptor__enumerable(self) } 126 | } 127 | 128 | pub fn writable(&self) -> bool { 129 | unsafe { v8__PropertyDescriptor__writable(self) } 130 | } 131 | 132 | pub fn value(&self) -> Local { 133 | unsafe { Local::from_raw(v8__PropertyDescriptor__value(self)) }.unwrap() 134 | } 135 | 136 | pub fn get(&self) -> Local { 137 | unsafe { Local::from_raw(v8__PropertyDescriptor__get(self)) }.unwrap() 138 | } 139 | 140 | pub fn set(&self) -> Local { 141 | unsafe { Local::from_raw(v8__PropertyDescriptor__set(self)) }.unwrap() 142 | } 143 | 144 | pub fn has_configurable(&self) -> bool { 145 | unsafe { v8__PropertyDescriptor__has_configurable(self) } 146 | } 147 | 148 | pub fn has_enumerable(&self) -> bool { 149 | unsafe { v8__PropertyDescriptor__has_enumerable(self) } 150 | } 151 | 152 | pub fn has_writable(&self) -> bool { 153 | unsafe { v8__PropertyDescriptor__has_writable(self) } 154 | } 155 | 156 | pub fn has_value(&self) -> bool { 157 | unsafe { v8__PropertyDescriptor__has_value(self) } 158 | } 159 | 160 | pub fn has_get(&self) -> bool { 161 | unsafe { v8__PropertyDescriptor__has_get(self) } 162 | } 163 | 164 | pub fn has_set(&self) -> bool { 165 | unsafe { v8__PropertyDescriptor__has_set(self) } 166 | } 167 | 168 | pub fn set_enumerable(&mut self, enumerable: bool) { 169 | unsafe { v8__PropertyDescriptor__set_enumerable(self, enumerable) } 170 | } 171 | 172 | pub fn set_configurable(&mut self, configurable: bool) { 173 | unsafe { v8__PropertyDescriptor__set_configurable(self, configurable) } 174 | } 175 | } 176 | 177 | impl Drop for PropertyDescriptor { 178 | fn drop(&mut self) { 179 | unsafe { v8__PropertyDescriptor__DESTRUCT(self) } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/property_filter.rs: -------------------------------------------------------------------------------- 1 | #[repr(C)] 2 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] 3 | pub struct PropertyFilter(u32); 4 | 5 | impl PropertyFilter { 6 | pub const ALL_PROPERTIES: PropertyFilter = PropertyFilter(0); 7 | 8 | pub const ONLY_WRITABLE: PropertyFilter = PropertyFilter(1 << 0); 9 | 10 | pub const ONLY_ENUMERABLE: PropertyFilter = PropertyFilter(1 << 1); 11 | 12 | pub const ONLY_CONFIGURABLE: PropertyFilter = PropertyFilter(1 << 2); 13 | 14 | pub const SKIP_STRINGS: PropertyFilter = PropertyFilter(1 << 3); 15 | 16 | pub const SKIP_SYMBOLS: PropertyFilter = PropertyFilter(1 << 4); 17 | 18 | /// Test if all property filters are set. 19 | #[inline(always)] 20 | pub fn is_all_properties(&self) -> bool { 21 | *self == Self::ALL_PROPERTIES 22 | } 23 | 24 | /// Test if the only-writable property filter is set. 25 | #[inline(always)] 26 | pub fn is_only_writable(&self) -> bool { 27 | self.has(Self::ONLY_WRITABLE) 28 | } 29 | 30 | /// Test if the only-enumerable property filter is set. 31 | #[inline(always)] 32 | pub fn is_only_enumerable(&self) -> bool { 33 | self.has(Self::ONLY_ENUMERABLE) 34 | } 35 | 36 | /// Test if the only-configurable property filter is set. 37 | #[inline(always)] 38 | pub fn is_only_configurable(&self) -> bool { 39 | self.has(Self::ONLY_CONFIGURABLE) 40 | } 41 | 42 | /// Test if the skip-strings property filter is set. 43 | #[inline(always)] 44 | pub fn is_skip_strings(&self) -> bool { 45 | self.has(Self::SKIP_STRINGS) 46 | } 47 | 48 | /// Test if the skip-symbols property filter is set. 49 | #[inline(always)] 50 | pub fn is_skip_symbols(&self) -> bool { 51 | self.has(Self::SKIP_SYMBOLS) 52 | } 53 | 54 | #[inline(always)] 55 | fn has(&self, that: Self) -> bool { 56 | let Self(lhs) = self; 57 | let Self(rhs) = that; 58 | 0 != lhs & rhs 59 | } 60 | } 61 | 62 | // Identical to #[derive(Default)] but arguably clearer when made explicit. 63 | impl Default for PropertyFilter { 64 | fn default() -> Self { 65 | Self::ALL_PROPERTIES 66 | } 67 | } 68 | 69 | impl std::ops::BitOr for PropertyFilter { 70 | type Output = Self; 71 | 72 | fn bitor(self, Self(rhs): Self) -> Self { 73 | let Self(lhs) = self; 74 | Self(lhs | rhs) 75 | } 76 | } 77 | 78 | #[test] 79 | fn test_attr() { 80 | assert!(PropertyFilter::ALL_PROPERTIES.is_all_properties()); 81 | assert!(!PropertyFilter::ALL_PROPERTIES.is_only_writable()); 82 | assert!(!PropertyFilter::ALL_PROPERTIES.is_only_enumerable()); 83 | assert!(!PropertyFilter::ALL_PROPERTIES.is_only_configurable()); 84 | assert!(!PropertyFilter::ALL_PROPERTIES.is_skip_strings()); 85 | assert!(!PropertyFilter::ALL_PROPERTIES.is_skip_symbols()); 86 | 87 | assert!(!PropertyFilter::ONLY_WRITABLE.is_all_properties()); 88 | assert!(PropertyFilter::ONLY_WRITABLE.is_only_writable()); 89 | assert!(!PropertyFilter::ONLY_WRITABLE.is_only_enumerable()); 90 | assert!(!PropertyFilter::ONLY_WRITABLE.is_only_configurable()); 91 | assert!(!PropertyFilter::ONLY_WRITABLE.is_skip_strings()); 92 | assert!(!PropertyFilter::ONLY_WRITABLE.is_skip_symbols()); 93 | 94 | assert!(!PropertyFilter::ONLY_ENUMERABLE.is_all_properties()); 95 | assert!(!PropertyFilter::ONLY_ENUMERABLE.is_only_writable()); 96 | assert!(PropertyFilter::ONLY_ENUMERABLE.is_only_enumerable()); 97 | assert!(!PropertyFilter::ONLY_ENUMERABLE.is_only_configurable()); 98 | assert!(!PropertyFilter::ONLY_ENUMERABLE.is_skip_strings()); 99 | assert!(!PropertyFilter::ONLY_ENUMERABLE.is_skip_symbols()); 100 | 101 | assert!(!PropertyFilter::ONLY_CONFIGURABLE.is_all_properties()); 102 | assert!(!PropertyFilter::ONLY_CONFIGURABLE.is_only_writable()); 103 | assert!(!PropertyFilter::ONLY_CONFIGURABLE.is_only_enumerable()); 104 | assert!(PropertyFilter::ONLY_CONFIGURABLE.is_only_configurable()); 105 | assert!(!PropertyFilter::ONLY_CONFIGURABLE.is_skip_strings()); 106 | assert!(!PropertyFilter::ONLY_CONFIGURABLE.is_skip_symbols()); 107 | 108 | assert!(!PropertyFilter::SKIP_STRINGS.is_all_properties()); 109 | assert!(!PropertyFilter::SKIP_STRINGS.is_only_writable()); 110 | assert!(!PropertyFilter::SKIP_STRINGS.is_only_enumerable()); 111 | assert!(!PropertyFilter::SKIP_STRINGS.is_only_configurable()); 112 | assert!(PropertyFilter::SKIP_STRINGS.is_skip_strings()); 113 | assert!(!PropertyFilter::SKIP_STRINGS.is_skip_symbols()); 114 | 115 | assert!(!PropertyFilter::SKIP_SYMBOLS.is_all_properties()); 116 | assert!(!PropertyFilter::SKIP_SYMBOLS.is_only_writable()); 117 | assert!(!PropertyFilter::SKIP_SYMBOLS.is_only_enumerable()); 118 | assert!(!PropertyFilter::SKIP_SYMBOLS.is_only_configurable()); 119 | assert!(!PropertyFilter::SKIP_SYMBOLS.is_skip_strings()); 120 | assert!(PropertyFilter::SKIP_SYMBOLS.is_skip_symbols()); 121 | 122 | assert_eq!(PropertyFilter::ALL_PROPERTIES, Default::default()); 123 | assert_eq!( 124 | PropertyFilter::ONLY_WRITABLE, 125 | PropertyFilter::ALL_PROPERTIES | PropertyFilter::ONLY_WRITABLE 126 | ); 127 | 128 | let attr = PropertyFilter::ONLY_WRITABLE 129 | | PropertyFilter::ONLY_WRITABLE 130 | | PropertyFilter::SKIP_STRINGS; 131 | assert!(!attr.is_all_properties()); 132 | assert!(attr.is_only_writable()); 133 | assert!(!attr.is_only_enumerable()); 134 | assert!(!attr.is_only_configurable()); 135 | assert!(attr.is_skip_strings()); 136 | assert!(!attr.is_skip_symbols()); 137 | } 138 | -------------------------------------------------------------------------------- /src/property_handler_flags.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | 3 | #[repr(C)] 4 | #[derive(Debug, Eq, PartialEq)] 5 | pub struct PropertyHandlerFlags(u32); 6 | 7 | impl PropertyHandlerFlags { 8 | /// None. 9 | pub const NONE: Self = Self(0); 10 | 11 | /// Will not call into interceptor for properties on the receiver or prototype 12 | /// chain, i.e., only call into interceptor for properties that do not exist. 13 | /// Currently only valid for named interceptors. 14 | pub const NON_MASKING: Self = Self(1 << 0); 15 | 16 | /// Will not call into interceptor for symbol lookup. Only meaningful for 17 | /// named interceptors. 18 | pub const ONLY_INTERCEPT_STRINGS: Self = Self(1 << 1); 19 | 20 | /// The getter, query, enumerator callbacks do not produce side effects. 21 | pub const HAS_NO_SIDE_EFFECT: Self = Self(1 << 2); 22 | 23 | /// Test if no property handler flags are set. 24 | #[inline(always)] 25 | pub fn is_none(&self) -> bool { 26 | *self == Self::NONE 27 | } 28 | 29 | /// Test if the non-masking property handler flag is set. 30 | #[inline(always)] 31 | pub fn is_non_masking(&self) -> bool { 32 | self.has(Self::NON_MASKING) 33 | } 34 | 35 | /// Test if the only-intercept-strings property handler flag is set. 36 | #[inline(always)] 37 | pub fn is_only_intercept_strings(&self) -> bool { 38 | self.has(Self::ONLY_INTERCEPT_STRINGS) 39 | } 40 | 41 | /// Test if the has-no-side-effect property handler flag is set. 42 | #[inline(always)] 43 | pub fn is_has_no_side_effect(&self) -> bool { 44 | self.has(Self::HAS_NO_SIDE_EFFECT) 45 | } 46 | 47 | #[inline(always)] 48 | fn has(&self, that: Self) -> bool { 49 | let Self(lhs) = self; 50 | let Self(rhs) = that; 51 | 0 != lhs & rhs 52 | } 53 | } 54 | 55 | // Identical to #[derive(Default)] but arguably clearer when made explicit. 56 | impl Default for PropertyHandlerFlags { 57 | fn default() -> Self { 58 | Self::NONE 59 | } 60 | } 61 | 62 | impl std::ops::BitOr for PropertyHandlerFlags { 63 | type Output = Self; 64 | 65 | fn bitor(self, Self(rhs): Self) -> Self { 66 | let Self(lhs) = self; 67 | Self(lhs | rhs) 68 | } 69 | } 70 | 71 | #[test] 72 | fn test_attr() { 73 | assert!(PropertyHandlerFlags::NONE.is_none()); 74 | assert!(!PropertyHandlerFlags::NONE.is_non_masking()); 75 | assert!(!PropertyHandlerFlags::NONE.is_only_intercept_strings()); 76 | assert!(!PropertyHandlerFlags::NONE.is_has_no_side_effect()); 77 | 78 | assert!(!PropertyHandlerFlags::NON_MASKING.is_none()); 79 | assert!(PropertyHandlerFlags::NON_MASKING.is_non_masking()); 80 | assert!(!PropertyHandlerFlags::NON_MASKING.is_only_intercept_strings()); 81 | assert!(!PropertyHandlerFlags::NON_MASKING.is_has_no_side_effect()); 82 | 83 | assert!(!PropertyHandlerFlags::ONLY_INTERCEPT_STRINGS.is_none()); 84 | assert!(!PropertyHandlerFlags::ONLY_INTERCEPT_STRINGS.is_non_masking()); 85 | assert!( 86 | PropertyHandlerFlags::ONLY_INTERCEPT_STRINGS.is_only_intercept_strings() 87 | ); 88 | assert!( 89 | !PropertyHandlerFlags::ONLY_INTERCEPT_STRINGS.is_has_no_side_effect() 90 | ); 91 | 92 | assert!(!PropertyHandlerFlags::HAS_NO_SIDE_EFFECT.is_none()); 93 | assert!(!PropertyHandlerFlags::HAS_NO_SIDE_EFFECT.is_non_masking()); 94 | assert!( 95 | !PropertyHandlerFlags::HAS_NO_SIDE_EFFECT.is_only_intercept_strings() 96 | ); 97 | assert!(PropertyHandlerFlags::HAS_NO_SIDE_EFFECT.is_has_no_side_effect()); 98 | 99 | assert_eq!(PropertyHandlerFlags::NONE, Default::default()); 100 | 101 | let attr = PropertyHandlerFlags::ONLY_INTERCEPT_STRINGS 102 | | PropertyHandlerFlags::HAS_NO_SIDE_EFFECT 103 | | PropertyHandlerFlags::NON_MASKING; 104 | assert!(!attr.is_none()); 105 | assert!(attr.is_non_masking()); 106 | assert!(attr.is_only_intercept_strings()); 107 | assert!(attr.is_has_no_side_effect()); 108 | } 109 | -------------------------------------------------------------------------------- /src/proxy.rs: -------------------------------------------------------------------------------- 1 | use crate::Context; 2 | use crate::HandleScope; 3 | use crate::Local; 4 | use crate::Object; 5 | use crate::Proxy; 6 | use crate::Value; 7 | 8 | unsafe extern "C" { 9 | fn v8__Proxy__New( 10 | context: *const Context, 11 | target: *const Object, 12 | handler: *const Object, 13 | ) -> *const Proxy; 14 | fn v8__Proxy__GetHandler(this: *const Proxy) -> *const Value; 15 | fn v8__Proxy__GetTarget(this: *const Proxy) -> *const Value; 16 | fn v8__Proxy__IsRevoked(this: *const Proxy) -> bool; 17 | fn v8__Proxy__Revoke(this: *const Proxy); 18 | } 19 | 20 | impl Proxy { 21 | #[inline(always)] 22 | pub fn new<'s>( 23 | scope: &mut HandleScope<'s>, 24 | target: Local, 25 | handler: Local, 26 | ) -> Option> { 27 | unsafe { 28 | scope.cast_local(|sd| { 29 | v8__Proxy__New(sd.get_current_context(), &*target, &*handler) 30 | }) 31 | } 32 | } 33 | 34 | #[inline(always)] 35 | pub fn get_handler<'s>( 36 | &self, 37 | scope: &mut HandleScope<'s>, 38 | ) -> Local<'s, Value> { 39 | unsafe { scope.cast_local(|_| v8__Proxy__GetHandler(self)) }.unwrap() 40 | } 41 | 42 | #[inline(always)] 43 | pub fn get_target<'s>( 44 | &self, 45 | scope: &mut HandleScope<'s>, 46 | ) -> Local<'s, Value> { 47 | unsafe { scope.cast_local(|_| v8__Proxy__GetTarget(self)) }.unwrap() 48 | } 49 | 50 | #[inline(always)] 51 | pub fn is_revoked(&self) -> bool { 52 | unsafe { v8__Proxy__IsRevoked(self) } 53 | } 54 | 55 | #[inline(always)] 56 | pub fn revoke(&self) { 57 | unsafe { v8__Proxy__Revoke(self) }; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/regexp.rs: -------------------------------------------------------------------------------- 1 | use crate::Context; 2 | use crate::HandleScope; 3 | use crate::Local; 4 | use crate::Object; 5 | use crate::RegExp; 6 | use crate::String; 7 | use crate::support::int; 8 | 9 | bitflags! { 10 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 11 | #[repr(transparent)] 12 | pub struct RegExpCreationFlags: int { 13 | const GLOBAL = 1 << 0; 14 | const IGNORE_CASE = 1 << 1; 15 | const MULTILINE = 1 << 2; 16 | const STICKY = 1 << 3; 17 | const UNICODE = 1 << 4; 18 | const DOT_ALL = 1 << 5; 19 | const LINEAR = 1 << 6; 20 | const HAS_INDICES = 1 << 7; 21 | const UNICODE_SETS = 1 << 8; 22 | } 23 | } 24 | 25 | unsafe extern "C" { 26 | fn v8__RegExp__New( 27 | context: *const Context, 28 | pattern: *const String, 29 | flags: RegExpCreationFlags, 30 | ) -> *const RegExp; 31 | fn v8__RegExp__Exec( 32 | this: *const RegExp, 33 | context: *const Context, 34 | subject: *const String, 35 | ) -> *const Object; 36 | fn v8__RegExp__GetSource(this: *const RegExp) -> *const String; 37 | } 38 | 39 | impl RegExp { 40 | #[inline(always)] 41 | pub fn new<'s>( 42 | scope: &mut HandleScope<'s>, 43 | pattern: Local, 44 | flags: RegExpCreationFlags, 45 | ) -> Option> { 46 | unsafe { 47 | scope.cast_local(|sd| { 48 | v8__RegExp__New(sd.get_current_context(), &*pattern, flags) 49 | }) 50 | } 51 | } 52 | 53 | #[inline(always)] 54 | pub fn exec<'s>( 55 | &self, 56 | scope: &mut HandleScope<'s>, 57 | subject: Local, 58 | ) -> Option> { 59 | unsafe { 60 | scope.cast_local(|sd| { 61 | v8__RegExp__Exec(self, sd.get_current_context(), &*subject) 62 | }) 63 | } 64 | } 65 | 66 | #[inline(always)] 67 | pub fn get_source<'s>( 68 | &self, 69 | scope: &mut HandleScope<'s>, 70 | ) -> Local<'s, String> { 71 | unsafe { scope.cast_local(|_| v8__RegExp__GetSource(self)) }.unwrap() 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/script.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::mem::MaybeUninit; 3 | use std::ptr::null; 4 | 5 | use crate::Context; 6 | use crate::Data; 7 | use crate::HandleScope; 8 | use crate::Local; 9 | use crate::Script; 10 | use crate::String; 11 | use crate::UnboundScript; 12 | use crate::Value; 13 | 14 | /// The origin, within a file, of a script. 15 | #[repr(C)] 16 | #[derive(Debug)] 17 | pub struct ScriptOrigin<'s>( 18 | [u8; crate::binding::v8__ScriptOrigin_SIZE], 19 | PhantomData<&'s ()>, 20 | ); 21 | 22 | unsafe extern "C" { 23 | fn v8__Script__Compile( 24 | context: *const Context, 25 | source: *const String, 26 | origin: *const ScriptOrigin, 27 | ) -> *const Script; 28 | fn v8__Script__GetUnboundScript( 29 | script: *const Script, 30 | ) -> *const UnboundScript; 31 | fn v8__Script__Run( 32 | script: *const Script, 33 | context: *const Context, 34 | ) -> *const Value; 35 | 36 | fn v8__ScriptOrigin__CONSTRUCT( 37 | buf: *mut MaybeUninit, 38 | resource_name: *const Value, 39 | resource_line_offset: i32, 40 | resource_column_offset: i32, 41 | resource_is_shared_cross_origin: bool, 42 | script_id: i32, 43 | source_map_url: *const Value, 44 | resource_is_opaque: bool, 45 | is_wasm: bool, 46 | is_module: bool, 47 | host_defined_options: *const Data, 48 | ); 49 | fn v8__ScriptOrigin__ScriptId(origin: *const ScriptOrigin) -> i32; 50 | fn v8__ScriptOrigin__ResourceName( 51 | origin: *const ScriptOrigin, 52 | ) -> *const Value; 53 | fn v8__ScriptOrigin__SourceMapUrl( 54 | origin: *const ScriptOrigin, 55 | ) -> *const Value; 56 | } 57 | 58 | impl Script { 59 | /// A shorthand for ScriptCompiler::Compile(). 60 | #[inline(always)] 61 | pub fn compile<'s>( 62 | scope: &mut HandleScope<'s>, 63 | source: Local, 64 | origin: Option<&ScriptOrigin>, 65 | ) -> Option> { 66 | unsafe { 67 | scope.cast_local(|sd| { 68 | v8__Script__Compile( 69 | sd.get_current_context(), 70 | &*source, 71 | origin.map_or_else(null, |r| r as *const _), 72 | ) 73 | }) 74 | } 75 | } 76 | 77 | /// Returns the corresponding context-unbound script. 78 | #[inline(always)] 79 | pub fn get_unbound_script<'s>( 80 | &self, 81 | scope: &mut HandleScope<'s>, 82 | ) -> Local<'s, UnboundScript> { 83 | unsafe { 84 | scope 85 | .cast_local(|_| v8__Script__GetUnboundScript(self)) 86 | .unwrap() 87 | } 88 | } 89 | 90 | /// Runs the script returning the resulting value. It will be run in the 91 | /// context in which it was created (ScriptCompiler::CompileBound or 92 | /// UnboundScript::BindToCurrentContext()). 93 | #[inline] 94 | pub fn run<'s>( 95 | &self, 96 | scope: &mut HandleScope<'s>, 97 | ) -> Option> { 98 | unsafe { 99 | scope.cast_local(|sd| v8__Script__Run(self, sd.get_current_context())) 100 | } 101 | } 102 | } 103 | 104 | /// The origin, within a file, of a script. 105 | impl<'s> ScriptOrigin<'s> { 106 | #[allow(clippy::too_many_arguments)] 107 | #[inline(always)] 108 | pub fn new( 109 | // TODO(littledivy): remove 110 | _scope: &mut HandleScope<'s, ()>, 111 | resource_name: Local<'s, Value>, 112 | resource_line_offset: i32, 113 | resource_column_offset: i32, 114 | resource_is_shared_cross_origin: bool, 115 | script_id: i32, 116 | source_map_url: Option>, 117 | resource_is_opaque: bool, 118 | is_wasm: bool, 119 | is_module: bool, 120 | host_defined_options: Option>, 121 | ) -> Self { 122 | unsafe { 123 | let mut buf = std::mem::MaybeUninit::::uninit(); 124 | v8__ScriptOrigin__CONSTRUCT( 125 | &mut buf, 126 | &*resource_name, 127 | resource_line_offset, 128 | resource_column_offset, 129 | resource_is_shared_cross_origin, 130 | script_id, 131 | source_map_url.map_or_else(null, |l| &*l as *const Value), 132 | resource_is_opaque, 133 | is_wasm, 134 | is_module, 135 | host_defined_options.map_or_else(null, |l| &*l as *const Data), 136 | ); 137 | buf.assume_init() 138 | } 139 | } 140 | 141 | #[inline(always)] 142 | pub fn script_id(&self) -> i32 { 143 | unsafe { v8__ScriptOrigin__ScriptId(self as *const _) } 144 | } 145 | 146 | #[inline(always)] 147 | pub fn resource_name(&self) -> Option> { 148 | unsafe { 149 | let ptr = v8__ScriptOrigin__ResourceName(self); 150 | Local::from_raw(ptr) 151 | } 152 | } 153 | 154 | #[inline(always)] 155 | pub fn source_map_url(&self) -> Option> { 156 | unsafe { 157 | let ptr = v8__ScriptOrigin__SourceMapUrl(self); 158 | Local::from_raw(ptr) 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/script_or_module.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | use crate::Data; 3 | use crate::Local; 4 | use crate::ScriptOrModule; 5 | use crate::Value; 6 | 7 | unsafe extern "C" { 8 | fn v8__ScriptOrModule__GetResourceName( 9 | this: *const ScriptOrModule, 10 | ) -> *const Value; 11 | 12 | fn v8__ScriptOrModule__HostDefinedOptions( 13 | this: *const ScriptOrModule, 14 | ) -> *const Data; 15 | } 16 | 17 | impl ScriptOrModule { 18 | /// The name that was passed by the embedder as ResourceName to the 19 | /// ScriptOrigin. This can be either a v8::String or v8::Undefined. 20 | #[inline(always)] 21 | pub fn get_resource_name(&self) -> Local { 22 | // Note: the C++ `v8::ScriptOrModule::GetResourceName()` does not actually 23 | // return a local handle, but rather a handle whose lifetime is bound to 24 | // the related `ScriptOrModule` object. 25 | unsafe { 26 | let ptr = v8__ScriptOrModule__GetResourceName(self); 27 | Local::from_raw(ptr).unwrap() 28 | } 29 | } 30 | 31 | /// The options that were passed by the embedder as HostDefinedOptions to the 32 | /// ScriptOrigin. 33 | #[inline(always)] 34 | pub fn host_defined_options(&self) -> Local { 35 | // Note: the C++ `v8::ScriptOrModule::HostDefinedOptions()` does not 36 | // actually return a local handle, but rather a handle whose lifetime is 37 | // bound to the related `ScriptOrModule` object. 38 | unsafe { 39 | let ptr = v8__ScriptOrModule__HostDefinedOptions(self); 40 | Local::from_raw(ptr).unwrap() 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/shared_array_buffer.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | 3 | use std::ffi::c_void; 4 | 5 | use crate::BackingStore; 6 | use crate::BackingStoreDeleterCallback; 7 | use crate::HandleScope; 8 | use crate::Isolate; 9 | use crate::Local; 10 | use crate::SharedArrayBuffer; 11 | use crate::support::SharedRef; 12 | use crate::support::UniqueRef; 13 | 14 | unsafe extern "C" { 15 | fn v8__SharedArrayBuffer__New__with_byte_length( 16 | isolate: *mut Isolate, 17 | byte_length: usize, 18 | ) -> *const SharedArrayBuffer; 19 | fn v8__SharedArrayBuffer__New__with_backing_store( 20 | isolate: *mut Isolate, 21 | backing_store: *const SharedRef, 22 | ) -> *const SharedArrayBuffer; 23 | fn v8__SharedArrayBuffer__ByteLength(this: *const SharedArrayBuffer) 24 | -> usize; 25 | fn v8__SharedArrayBuffer__GetBackingStore( 26 | this: *const SharedArrayBuffer, 27 | ) -> SharedRef; 28 | fn v8__SharedArrayBuffer__NewBackingStore__with_byte_length( 29 | isolate: *mut Isolate, 30 | byte_length: usize, 31 | ) -> *mut BackingStore; 32 | fn v8__SharedArrayBuffer__NewBackingStore__with_data( 33 | data: *mut c_void, 34 | byte_length: usize, 35 | deleter: BackingStoreDeleterCallback, 36 | deleter_data: *mut c_void, 37 | ) -> *mut BackingStore; 38 | } 39 | 40 | impl SharedArrayBuffer { 41 | /// Create a new SharedArrayBuffer. Allocate |byte_length| bytes. 42 | /// Allocated memory will be owned by a created SharedArrayBuffer and 43 | /// will be deallocated when it is garbage-collected, 44 | /// unless the object is externalized. 45 | #[inline(always)] 46 | pub fn new<'s>( 47 | scope: &mut HandleScope<'s>, 48 | byte_length: usize, 49 | ) -> Option> { 50 | unsafe { 51 | scope.cast_local(|sd| { 52 | v8__SharedArrayBuffer__New__with_byte_length( 53 | sd.get_isolate_ptr(), 54 | byte_length, 55 | ) 56 | }) 57 | } 58 | } 59 | 60 | #[inline(always)] 61 | pub fn with_backing_store<'s>( 62 | scope: &mut HandleScope<'s>, 63 | backing_store: &SharedRef, 64 | ) -> Local<'s, SharedArrayBuffer> { 65 | unsafe { 66 | scope.cast_local(|sd| { 67 | v8__SharedArrayBuffer__New__with_backing_store( 68 | sd.get_isolate_ptr(), 69 | backing_store, 70 | ) 71 | }) 72 | } 73 | .unwrap() 74 | } 75 | 76 | /// Data length in bytes. 77 | #[inline(always)] 78 | pub fn byte_length(&self) -> usize { 79 | unsafe { v8__SharedArrayBuffer__ByteLength(self) } 80 | } 81 | 82 | /// Get a shared pointer to the backing store of this array buffer. This 83 | /// pointer coordinates the lifetime management of the internal storage 84 | /// with any live ArrayBuffers on the heap, even across isolates. The embedder 85 | /// should not attempt to manage lifetime of the storage through other means. 86 | #[inline(always)] 87 | pub fn get_backing_store(&self) -> SharedRef { 88 | unsafe { v8__SharedArrayBuffer__GetBackingStore(self) } 89 | } 90 | 91 | /// Returns a new standalone BackingStore that is allocated using the array 92 | /// buffer allocator of the isolate. The result can be later passed to 93 | /// ArrayBuffer::New. 94 | /// 95 | /// If the allocator returns nullptr, then the function may cause GCs in the 96 | /// given isolate and re-try the allocation. If GCs do not help, then the 97 | /// function will crash with an out-of-memory error. 98 | #[inline(always)] 99 | pub fn new_backing_store( 100 | scope: &mut Isolate, 101 | byte_length: usize, 102 | ) -> UniqueRef { 103 | unsafe { 104 | UniqueRef::from_raw( 105 | v8__SharedArrayBuffer__NewBackingStore__with_byte_length( 106 | scope, 107 | byte_length, 108 | ), 109 | ) 110 | } 111 | } 112 | 113 | /// Returns a new standalone BackingStore that takes over the ownership of 114 | /// the given buffer. 115 | /// 116 | /// The destructor of the BackingStore frees owned buffer memory. 117 | /// 118 | /// The result can be later passed to SharedArrayBuffer::New. The raw pointer 119 | /// to the buffer must not be passed again to any V8 API function. 120 | #[inline(always)] 121 | pub fn new_backing_store_from_boxed_slice( 122 | data: Box<[u8]>, 123 | ) -> UniqueRef { 124 | Self::new_backing_store_from_bytes(data) 125 | } 126 | 127 | /// Returns a new standalone BackingStore that takes over the ownership of 128 | /// the given buffer. 129 | /// 130 | /// The destructor of the BackingStore frees owned buffer memory. 131 | /// 132 | /// The result can be later passed to SharedArrayBuffer::New. The raw pointer 133 | /// to the buffer must not be passed again to any V8 API function. 134 | #[inline(always)] 135 | pub fn new_backing_store_from_vec(data: Vec) -> UniqueRef { 136 | Self::new_backing_store_from_bytes(data) 137 | } 138 | 139 | /// Returns a new standalone BackingStore backed by a container that dereferences 140 | /// to a mutable slice of bytes. The object is dereferenced once, and the resulting slice's 141 | /// memory is used for the lifetime of the buffer. 142 | /// 143 | /// This method may be called with most single-ownership containers that implement `AsMut<[u8]>`, including 144 | /// `Box<[u8]>`, and `Vec`. This will also support most other mutable bytes containers (including `bytes::BytesMut`), 145 | /// though these buffers will need to be boxed to manage ownership of memory. 146 | /// 147 | /// ``` 148 | /// // Vector of bytes 149 | /// let backing_store = v8::ArrayBuffer::new_backing_store_from_bytes(vec![1, 2, 3]); 150 | /// // Boxes slice of bytes 151 | /// let boxed_slice: Box<[u8]> = vec![1, 2, 3].into_boxed_slice(); 152 | /// let backing_store = v8::ArrayBuffer::new_backing_store_from_bytes(boxed_slice); 153 | /// // BytesMut from bytes crate 154 | /// let backing_store = v8::ArrayBuffer::new_backing_store_from_bytes(Box::new(bytes::BytesMut::new())); 155 | /// ``` 156 | #[inline(always)] 157 | pub fn new_backing_store_from_bytes( 158 | mut bytes: T, 159 | ) -> UniqueRef 160 | where 161 | T: crate::array_buffer::sealed::Rawable, 162 | { 163 | let len = bytes.byte_len(); 164 | 165 | let (ptr, slice) = T::into_raw(bytes); 166 | 167 | unsafe extern "C" fn drop_rawable< 168 | T: crate::array_buffer::sealed::Rawable, 169 | >( 170 | _ptr: *mut c_void, 171 | len: usize, 172 | data: *mut c_void, 173 | ) { 174 | // SAFETY: We know that data is a raw T from above 175 | unsafe { 176 | ::drop_raw(data as _, len); 177 | } 178 | } 179 | 180 | // SAFETY: We are extending the lifetime of a slice, but we're locking away the box that we 181 | // derefed from so there's no way to get another mutable reference. 182 | unsafe { 183 | Self::new_backing_store_from_ptr( 184 | slice as _, 185 | len, 186 | drop_rawable::, 187 | ptr as _, 188 | ) 189 | } 190 | } 191 | 192 | /// Returns a new standalone shared BackingStore backed by given ptr. 193 | /// 194 | /// SAFETY: This API consumes raw pointers so is inherently 195 | /// unsafe. Usually you should use new_backing_store_from_boxed_slice. 196 | #[inline(always)] 197 | pub unsafe fn new_backing_store_from_ptr( 198 | data_ptr: *mut c_void, 199 | byte_length: usize, 200 | deleter_callback: BackingStoreDeleterCallback, 201 | deleter_data: *mut c_void, 202 | ) -> UniqueRef { 203 | unsafe { 204 | UniqueRef::from_raw(v8__SharedArrayBuffer__NewBackingStore__with_data( 205 | data_ptr, 206 | byte_length, 207 | deleter_callback, 208 | deleter_data, 209 | )) 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/support.h: -------------------------------------------------------------------------------- 1 | #ifndef SUPPORT_H_ 2 | #define SUPPORT_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "cppgc/name-provider.h" 13 | #include "v8-cppgc.h" 14 | #include "v8.h" 15 | 16 | // Check assumptions made in binding code. 17 | static_assert(sizeof(bool) == sizeof(uint8_t), ""); 18 | static_assert(sizeof(std::unique_ptr) == sizeof(void*), ""); 19 | 20 | namespace support { 21 | template 22 | using uninit_t = typename std::aligned_storage::type; 23 | 24 | template 25 | class construct_in_place_helper { 26 | public: 27 | construct_in_place_helper(uninit_t* buf, Args... args) 28 | : inner_(std::forward(args)...) {} 29 | 30 | private: 31 | T inner_; 32 | }; 33 | 34 | template 35 | void construct_in_place(uninit_t* buf, Args... args) { 36 | new (buf) 37 | construct_in_place_helper(buf, std::forward(args)...); 38 | } 39 | 40 | // Rust's FFI only supports returning normal C data structures (AKA plain old 41 | // data). There are some situations in the V8 API where functions return non-POD 42 | // data. We use make_pod to carefully adjust the return values so they can be 43 | // passed into Rust. 44 | // 45 | // The destructor of V is never called. 46 | // P is not allowed to have a destructor. 47 | template 48 | struct make_pod { 49 | template 50 | inline make_pod(V&& value) : pod_(helper(std::move(value))) {} 51 | template 52 | inline make_pod(const V& value) : pod_(helper(value)) {} 53 | inline operator P() { return pod_; } 54 | 55 | private: 56 | P pod_; 57 | 58 | // This helper exists to avoid calling the destructor. 59 | // Using a union is a C++ trick to achieve this. 60 | template 61 | union helper { 62 | static_assert(std::is_trivial

::value && 63 | std::is_standard_layout

::value, 64 | "type P must a pod type"); 65 | static_assert(sizeof(V) == sizeof(P), "type P must be same size as type V"); 66 | static_assert(alignof(V) == alignof(P), 67 | "alignment of type P must be compatible with that of type V"); 68 | 69 | inline helper(V&& value) : value_(std::move(value)) {} 70 | inline helper(const V& value) : value_(value) {} 71 | inline ~helper() {} 72 | 73 | inline operator P() { 74 | // Do a memcpy here avoid undefined behavior. 75 | P result; 76 | memcpy(&result, this, sizeof result); 77 | return result; 78 | } 79 | 80 | private: 81 | V value_; 82 | }; 83 | }; 84 | 85 | // The C-ABI compatible equivalent of V8's Maybe. 86 | enum class MaybeBool { JustFalse = 0, JustTrue = 1, Nothing = 2 }; 87 | 88 | inline static MaybeBool maybe_to_maybe_bool(v8::Maybe maybe) { 89 | if (maybe.IsNothing()) { 90 | return MaybeBool::Nothing; 91 | } else if (maybe.FromJust()) { 92 | return MaybeBool::JustTrue; 93 | } else { 94 | return MaybeBool::JustFalse; 95 | } 96 | } 97 | 98 | inline static v8::Maybe maybe_bool_to_maybe(MaybeBool maybe) { 99 | switch (maybe) { 100 | case MaybeBool::JustTrue: 101 | case MaybeBool::JustFalse: 102 | return v8::Just(maybe == MaybeBool::JustTrue); 103 | default: 104 | return v8::Nothing(); 105 | } 106 | } 107 | 108 | template 109 | inline static T* local_to_ptr(v8::Local local) { 110 | return *local; 111 | } 112 | 113 | template 114 | inline static const v8::Local ptr_to_local(const T* ptr) { 115 | static_assert(sizeof(v8::Local) == sizeof(T*), ""); 116 | auto local = *reinterpret_cast*>(&ptr); 117 | assert(*local == ptr); 118 | return local; 119 | } 120 | 121 | template 122 | inline static v8::Local* const_ptr_array_to_local_array( 123 | const T* const ptr_array[]) { 124 | static_assert(sizeof(v8::Local[42]) == sizeof(T* [42]), ""); 125 | auto mut_ptr_array = const_cast(ptr_array); 126 | auto mut_local_array = reinterpret_cast*>(mut_ptr_array); 127 | return mut_local_array; 128 | } 129 | 130 | template 131 | inline static T* maybe_local_to_ptr(v8::MaybeLocal local) { 132 | return *local.FromMaybe(v8::Local()); 133 | } 134 | 135 | template 136 | inline static const v8::MaybeLocal ptr_to_maybe_local(const T* ptr) { 137 | static_assert(sizeof(v8::MaybeLocal) == sizeof(T*), ""); 138 | return *reinterpret_cast*>(&ptr); 139 | } 140 | 141 | template 142 | inline static v8::Global ptr_to_global(const T* ptr) { 143 | v8::Global global; 144 | std::swap(ptr, *reinterpret_cast(&global)); 145 | return global; 146 | } 147 | 148 | // Because, for some reason, Clang complains that `std::array` is an 149 | // incomplete type, incompatible with C linkage. 150 | struct two_pointers_t { 151 | void* a; 152 | void* b; 153 | }; 154 | 155 | struct three_pointers_t { 156 | void* a; 157 | void* b; 158 | void* c; 159 | }; 160 | 161 | } // namespace support 162 | 163 | struct memory_span_t { 164 | uint8_t* data; 165 | size_t size; 166 | }; 167 | 168 | #define EACH_TYPED_ARRAY(V) \ 169 | V(Uint8Array) \ 170 | V(Uint8ClampedArray) \ 171 | V(Int8Array) \ 172 | V(Uint16Array) \ 173 | V(Int16Array) \ 174 | V(Uint32Array) \ 175 | V(Int32Array) \ 176 | V(Float32Array) \ 177 | V(Float64Array) \ 178 | V(BigUint64Array) \ 179 | V(BigInt64Array) 180 | 181 | #endif // SUPPORT_H_ 182 | 183 | class RustObj : public cppgc::GarbageCollected, 184 | public cppgc::NameProvider { 185 | public: 186 | ~RustObj(); 187 | void Trace(cppgc::Visitor* visitor) const; 188 | const char* GetHumanReadableName() const final; 189 | uintptr_t data[2]; 190 | }; 191 | -------------------------------------------------------------------------------- /src/symbol.rs: -------------------------------------------------------------------------------- 1 | use crate::HandleScope; 2 | use crate::Isolate; 3 | use crate::Local; 4 | use crate::String; 5 | use crate::Symbol; 6 | use crate::Value; 7 | 8 | unsafe extern "C" { 9 | fn v8__Symbol__New( 10 | isolate: *mut Isolate, 11 | description: *const String, 12 | ) -> *const Symbol; 13 | fn v8__Symbol__For( 14 | isolate: *mut Isolate, 15 | description: *const String, 16 | ) -> *const Symbol; 17 | fn v8__Symbol__ForApi( 18 | isolate: *mut Isolate, 19 | description: *const String, 20 | ) -> *const Symbol; 21 | fn v8__Symbol__Description( 22 | this: *const Symbol, 23 | isolate: *mut Isolate, 24 | ) -> *const Value; 25 | } 26 | 27 | macro_rules! well_known { 28 | ($name:ident, $binding:ident) => { 29 | pub fn $name<'s>(scope: &mut HandleScope<'s, ()>) -> Local<'s, Symbol> { 30 | unsafe extern "C" { 31 | fn $binding(isolate: *mut Isolate) -> *const Symbol; 32 | } 33 | unsafe { scope.cast_local(|sd| $binding(sd.get_isolate_ptr())) }.unwrap() 34 | } 35 | }; 36 | } 37 | 38 | impl Symbol { 39 | /// Create a symbol. If description is not empty, it will be used as the 40 | /// description. 41 | #[inline(always)] 42 | pub fn new<'s>( 43 | scope: &mut HandleScope<'s, ()>, 44 | description: Option>, 45 | ) -> Local<'s, Symbol> { 46 | unsafe { 47 | scope.cast_local(|sd| { 48 | v8__Symbol__New( 49 | sd.get_isolate_ptr(), 50 | description.map_or_else(std::ptr::null, |v| &*v), 51 | ) 52 | }) 53 | } 54 | .unwrap() 55 | } 56 | 57 | /// Access global symbol registry. 58 | /// Note that symbols created this way are never collected, so 59 | /// they should only be used for statically fixed properties. 60 | /// Also, there is only one global description space for the descriptions used as 61 | /// keys. 62 | /// To minimize the potential for clashes, use qualified descriptions as keys. 63 | /// Corresponds to v8::Symbol::For() in C++. 64 | #[inline(always)] 65 | pub fn for_key<'s>( 66 | scope: &mut HandleScope<'s, ()>, 67 | description: Local, 68 | ) -> Local<'s, Symbol> { 69 | unsafe { 70 | scope 71 | .cast_local(|sd| v8__Symbol__For(sd.get_isolate_ptr(), &*description)) 72 | } 73 | .unwrap() 74 | } 75 | 76 | /// Retrieve a global symbol. Similar to `for_key`, but using a separate 77 | /// registry that is not accessible by (and cannot clash with) JavaScript code. 78 | /// Corresponds to v8::Symbol::ForApi() in C++. 79 | #[inline(always)] 80 | pub fn for_api<'s>( 81 | scope: &mut HandleScope<'s, ()>, 82 | description: Local, 83 | ) -> Local<'s, Symbol> { 84 | unsafe { 85 | scope.cast_local(|sd| { 86 | v8__Symbol__ForApi(sd.get_isolate_ptr(), &*description) 87 | }) 88 | } 89 | .unwrap() 90 | } 91 | 92 | /// Returns the description string of the symbol, or undefined if none. 93 | #[inline(always)] 94 | pub fn description<'s>( 95 | &self, 96 | scope: &mut HandleScope<'s, ()>, 97 | ) -> Local<'s, Value> { 98 | unsafe { 99 | scope.cast_local(|sd| v8__Symbol__Description(self, sd.get_isolate_ptr())) 100 | } 101 | .unwrap() 102 | } 103 | 104 | well_known!(get_async_iterator, v8__Symbol__GetAsyncIterator); 105 | well_known!(get_has_instance, v8__Symbol__GetHasInstance); 106 | well_known!(get_is_concat_spreadable, v8__Symbol__GetIsConcatSpreadable); 107 | well_known!(get_iterator, v8__Symbol__GetIterator); 108 | well_known!(get_match, v8__Symbol__GetMatch); 109 | well_known!(get_replace, v8__Symbol__GetReplace); 110 | well_known!(get_search, v8__Symbol__GetSearch); 111 | well_known!(get_split, v8__Symbol__GetSplit); 112 | well_known!(get_to_primitive, v8__Symbol__GetToPrimitive); 113 | well_known!(get_to_string_tag, v8__Symbol__GetToStringTag); 114 | well_known!(get_unscopables, v8__Symbol__GetUnscopables); 115 | } 116 | -------------------------------------------------------------------------------- /src/typed_array.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | use crate::ArrayBuffer; 3 | use crate::HandleScope; 4 | use crate::Local; 5 | use crate::TypedArray; 6 | use crate::binding::v8__TypedArray__kMaxByteLength; 7 | use crate::support::size_t; 8 | use paste::paste; 9 | 10 | unsafe extern "C" { 11 | fn v8__TypedArray__Length(this: *const TypedArray) -> size_t; 12 | } 13 | 14 | impl TypedArray { 15 | /// The largest supported typed array byte size. Each subclass defines a 16 | /// type-specific max_length for the maximum length that can be passed to new. 17 | pub const MAX_BYTE_LENGTH: usize = v8__TypedArray__kMaxByteLength; 18 | 19 | /// Number of elements in this typed array 20 | /// (e.g. for Int16Array, |ByteLength|/2). 21 | #[inline(always)] 22 | pub fn length(&self) -> usize { 23 | unsafe { v8__TypedArray__Length(self) } 24 | } 25 | } 26 | 27 | macro_rules! typed_array { 28 | ($name:ident) => { 29 | paste! { 30 | use crate::$name; 31 | 32 | unsafe extern "C" { 33 | fn [< v8__ $name __New >]( 34 | buf_ptr: *const ArrayBuffer, 35 | byte_offset: usize, 36 | length: usize, 37 | ) -> *const $name; 38 | } 39 | 40 | impl $name { 41 | #[inline(always)] 42 | pub fn new<'s>( 43 | scope: &mut HandleScope<'s>, 44 | buf: Local, 45 | byte_offset: usize, 46 | length: usize, 47 | ) -> Option> { 48 | unsafe { scope.cast_local(|_| [< v8__ $name __New >](&*buf, byte_offset, length)) } 49 | } 50 | 51 | #[doc = concat!("The largest ", stringify!($name), " size that can be constructed using `new`.")] 52 | pub const MAX_LENGTH: usize = crate::binding::[< v8__ $name __kMaxLength >]; 53 | } 54 | } 55 | }; 56 | } 57 | 58 | typed_array!(Uint8Array); 59 | typed_array!(Uint8ClampedArray); 60 | typed_array!(Int8Array); 61 | typed_array!(Uint16Array); 62 | typed_array!(Int16Array); 63 | typed_array!(Uint32Array); 64 | typed_array!(Int32Array); 65 | typed_array!(Float32Array); 66 | typed_array!(Float64Array); 67 | typed_array!(BigUint64Array); 68 | typed_array!(BigInt64Array); 69 | -------------------------------------------------------------------------------- /src/unbound_module_script.rs: -------------------------------------------------------------------------------- 1 | use crate::CachedData; 2 | use crate::HandleScope; 3 | use crate::Local; 4 | use crate::UnboundModuleScript; 5 | use crate::UniqueRef; 6 | use crate::Value; 7 | 8 | unsafe extern "C" { 9 | fn v8__UnboundModuleScript__CreateCodeCache( 10 | script: *const UnboundModuleScript, 11 | ) -> *mut CachedData<'static>; 12 | 13 | fn v8__UnboundModuleScript__GetSourceMappingURL( 14 | script: *const UnboundModuleScript, 15 | ) -> *const Value; 16 | 17 | fn v8__UnboundModuleScript__GetSourceURL( 18 | script: *const UnboundModuleScript, 19 | ) -> *const Value; 20 | } 21 | 22 | impl UnboundModuleScript { 23 | /// Creates and returns code cache for the specified unbound_module_script. 24 | /// This will return nullptr if the script cannot be serialized. The 25 | /// CachedData returned by this function should be owned by the caller. 26 | #[inline(always)] 27 | pub fn create_code_cache(&self) -> Option>> { 28 | let code_cache = unsafe { 29 | UniqueRef::try_from_raw(v8__UnboundModuleScript__CreateCodeCache(self)) 30 | }; 31 | if let Some(code_cache) = &code_cache { 32 | debug_assert_eq!( 33 | code_cache.buffer_policy(), 34 | crate::script_compiler::BufferPolicy::BufferOwned 35 | ); 36 | } 37 | code_cache 38 | } 39 | 40 | pub fn get_source_mapping_url<'s>( 41 | &self, 42 | scope: &mut HandleScope<'s>, 43 | ) -> Local<'s, Value> { 44 | unsafe { 45 | scope 46 | .cast_local(|_| v8__UnboundModuleScript__GetSourceMappingURL(self)) 47 | .unwrap() 48 | } 49 | } 50 | 51 | pub fn get_source_url<'s>( 52 | &self, 53 | scope: &mut HandleScope<'s>, 54 | ) -> Local<'s, Value> { 55 | unsafe { 56 | scope 57 | .cast_local(|_| v8__UnboundModuleScript__GetSourceURL(self)) 58 | .unwrap() 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/unbound_script.rs: -------------------------------------------------------------------------------- 1 | use crate::CachedData; 2 | use crate::HandleScope; 3 | use crate::Local; 4 | use crate::Script; 5 | use crate::UnboundScript; 6 | use crate::UniqueRef; 7 | use crate::Value; 8 | 9 | unsafe extern "C" { 10 | fn v8__UnboundScript__BindToCurrentContext( 11 | script: *const UnboundScript, 12 | ) -> *const Script; 13 | fn v8__UnboundScript__CreateCodeCache( 14 | script: *const UnboundScript, 15 | ) -> *mut CachedData<'static>; 16 | 17 | fn v8__UnboundScript__GetSourceMappingURL( 18 | script: *const UnboundScript, 19 | ) -> *const Value; 20 | 21 | fn v8__UnboundScript__GetSourceURL( 22 | script: *const UnboundScript, 23 | ) -> *const Value; 24 | } 25 | 26 | impl UnboundScript { 27 | /// Binds the script to the currently entered context. 28 | #[inline(always)] 29 | pub fn bind_to_current_context<'s>( 30 | &self, 31 | scope: &mut HandleScope<'s>, 32 | ) -> Local<'s, Script> { 33 | unsafe { 34 | scope.cast_local(|_| v8__UnboundScript__BindToCurrentContext(self)) 35 | } 36 | .unwrap() 37 | } 38 | 39 | /// Creates and returns code cache for the specified unbound_script. 40 | /// This will return nullptr if the script cannot be serialized. The 41 | /// CachedData returned by this function should be owned by the caller. 42 | #[inline(always)] 43 | pub fn create_code_cache(&self) -> Option>> { 44 | let code_cache = unsafe { 45 | UniqueRef::try_from_raw(v8__UnboundScript__CreateCodeCache(self)) 46 | }; 47 | if let Some(code_cache) = &code_cache { 48 | debug_assert_eq!( 49 | code_cache.buffer_policy(), 50 | crate::script_compiler::BufferPolicy::BufferOwned 51 | ); 52 | } 53 | code_cache 54 | } 55 | 56 | #[inline(always)] 57 | pub fn get_source_mapping_url<'s>( 58 | &self, 59 | scope: &mut HandleScope<'s>, 60 | ) -> Local<'s, Value> { 61 | unsafe { 62 | scope 63 | .cast_local(|_| v8__UnboundScript__GetSourceMappingURL(self)) 64 | .unwrap() 65 | } 66 | } 67 | 68 | #[inline(always)] 69 | pub fn get_source_url<'s>( 70 | &self, 71 | scope: &mut HandleScope<'s>, 72 | ) -> Local<'s, Value> { 73 | unsafe { 74 | scope 75 | .cast_local(|_| v8__UnboundScript__GetSourceURL(self)) 76 | .unwrap() 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/wasm.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | 3 | use crate::ArrayBuffer; 4 | use crate::Isolate; 5 | use crate::Local; 6 | use crate::Value; 7 | use crate::WasmMemoryObject; 8 | use crate::WasmModuleObject; 9 | use crate::function::FunctionCallbackArguments; 10 | use crate::function::FunctionCallbackInfo; 11 | use crate::scope::CallbackScope; 12 | use crate::scope::HandleScope; 13 | use crate::support::Opaque; 14 | use crate::support::UnitType; 15 | use crate::support::char; 16 | use std::ptr::null; 17 | use std::ptr::null_mut; 18 | 19 | // Type-erased std::shared_ptr. Assumes it's safe 20 | // to move around (no backlinks). Not generally true for shared_ptrs 21 | // but it is in this case - other shared_ptrs that point to the same 22 | // v8::WasmStreaming exist but are managed by V8 and don't leak out. 23 | // 24 | // We don't use crate::support::SharedPtr because it only allows 25 | // immutable borrows and derefs to avoid aliasing but that's not 26 | // a problem here, only a single instance outside V8 exists. 27 | // 28 | // Note: uses *mut u8 rather than e.g. usize to enforce !Send and !Sync. 29 | #[repr(C)] 30 | struct WasmStreamingSharedPtr([*mut u8; 2]); 31 | 32 | /// The V8 interface for WebAssembly streaming compilation. 33 | /// When streaming compilation is initiated, V8 passes a [Self] 34 | /// object to the embedder such that the embedder can pass the 35 | /// input bytes for streaming compilation to V8. 36 | #[repr(C)] 37 | pub struct WasmStreaming(WasmStreamingSharedPtr); 38 | 39 | impl WasmStreaming { 40 | /// Pass a new chunk of bytes to WebAssembly streaming compilation. 41 | #[inline(always)] 42 | pub fn on_bytes_received(&mut self, data: &[u8]) { 43 | unsafe { 44 | v8__WasmStreaming__OnBytesReceived( 45 | &mut self.0, 46 | data.as_ptr(), 47 | data.len(), 48 | ); 49 | } 50 | } 51 | 52 | /// Should be called after all received bytes where passed to 53 | /// [`Self::on_bytes_received()`] to tell V8 that there will be no 54 | /// more bytes. Does not have to be called after [`Self::abort()`] 55 | /// has been called already. 56 | #[inline(always)] 57 | pub fn finish(mut self) { 58 | unsafe { v8__WasmStreaming__Finish(&mut self.0) } 59 | } 60 | 61 | /// Abort streaming compilation. If {exception} has a value, then the promise 62 | /// associated with streaming compilation is rejected with that value. If 63 | /// {exception} does not have value, the promise does not get rejected. 64 | #[inline(always)] 65 | pub fn abort(mut self, exception: Option>) { 66 | let exception = exception.map_or(null(), |v| &*v as *const Value); 67 | unsafe { v8__WasmStreaming__Abort(&mut self.0, exception) } 68 | } 69 | 70 | /// Sets the UTF-8 encoded source URL for the `Script` object. This must be 71 | /// called before [`Self::finish()`]. 72 | #[inline(always)] 73 | pub fn set_url(&mut self, url: &str) { 74 | // Although not documented, V8 requires the url to be null terminated. 75 | // See https://chromium-review.googlesource.com/c/v8/v8/+/3289148. 76 | let null_terminated_url = format!("{url}\0"); 77 | unsafe { 78 | v8__WasmStreaming__SetUrl( 79 | &mut self.0, 80 | null_terminated_url.as_ptr() as *const char, 81 | url.len(), 82 | ); 83 | } 84 | } 85 | } 86 | 87 | impl Drop for WasmStreaming { 88 | fn drop(&mut self) { 89 | unsafe { v8__WasmStreaming__shared_ptr_DESTRUCT(&mut self.0) } 90 | } 91 | } 92 | 93 | impl WasmModuleObject { 94 | /// Efficiently re-create a WasmModuleObject, without recompiling, from 95 | /// a CompiledWasmModule. 96 | #[inline(always)] 97 | pub fn from_compiled_module<'s>( 98 | scope: &mut HandleScope<'s>, 99 | compiled_module: &CompiledWasmModule, 100 | ) -> Option> { 101 | unsafe { 102 | scope.cast_local(|sd| { 103 | v8__WasmModuleObject__FromCompiledModule( 104 | sd.get_isolate_ptr(), 105 | compiled_module.0, 106 | ) 107 | }) 108 | } 109 | } 110 | 111 | /// Get the compiled module for this module object. The compiled module can be 112 | /// shared by several module objects. 113 | #[inline(always)] 114 | pub fn get_compiled_module(&self) -> CompiledWasmModule { 115 | let ptr = unsafe { v8__WasmModuleObject__GetCompiledModule(self) }; 116 | CompiledWasmModule(ptr) 117 | } 118 | 119 | /// Compile a Wasm module from the provided uncompiled bytes. 120 | #[inline(always)] 121 | pub fn compile<'s>( 122 | scope: &mut HandleScope<'s>, 123 | wire_bytes: &[u8], 124 | ) -> Option> { 125 | unsafe { 126 | scope.cast_local(|sd| { 127 | v8__WasmModuleObject__Compile( 128 | sd.get_isolate_ptr(), 129 | wire_bytes.as_ptr(), 130 | wire_bytes.len(), 131 | ) 132 | }) 133 | } 134 | } 135 | } 136 | 137 | // Type-erased v8::CompiledWasmModule. We need this because the C++ 138 | // v8::CompiledWasmModule must be destructed because its private fields hold 139 | // pointers that must be freed, but v8::CompiledWasmModule itself doesn't have 140 | // a destructor. Therefore, in order to avoid memory leaks, the Rust-side 141 | // CompiledWasmModule must be a pointer to a C++ allocation of 142 | // v8::CompiledWasmModule. 143 | #[repr(C)] 144 | struct InternalCompiledWasmModule(Opaque); 145 | 146 | /// Wrapper around a compiled WebAssembly module, which is potentially shared by 147 | /// different WasmModuleObjects. 148 | pub struct CompiledWasmModule(*mut InternalCompiledWasmModule); 149 | 150 | impl CompiledWasmModule { 151 | /// Get the (wasm-encoded) wire bytes that were used to compile this module. 152 | #[inline(always)] 153 | pub fn get_wire_bytes_ref(&self) -> &[u8] { 154 | let mut len = 0isize; 155 | unsafe { 156 | let ptr = v8__CompiledWasmModule__GetWireBytesRef(self.0, &mut len); 157 | std::slice::from_raw_parts(ptr, len.try_into().unwrap()) 158 | } 159 | } 160 | 161 | #[inline(always)] 162 | pub fn source_url(&self) -> &str { 163 | let mut len = 0; 164 | unsafe { 165 | let ptr = v8__CompiledWasmModule__SourceUrl(self.0, &mut len); 166 | let bytes = std::slice::from_raw_parts(ptr as _, len); 167 | std::str::from_utf8_unchecked(bytes) 168 | } 169 | } 170 | } 171 | 172 | // TODO(andreubotella): Safety??? 173 | unsafe impl Send for CompiledWasmModule {} 174 | unsafe impl Sync for CompiledWasmModule {} 175 | 176 | impl Drop for CompiledWasmModule { 177 | fn drop(&mut self) { 178 | unsafe { v8__CompiledWasmModule__DELETE(self.0) } 179 | } 180 | } 181 | 182 | impl WasmMemoryObject { 183 | /// Returns underlying ArrayBuffer. 184 | #[inline(always)] 185 | pub fn buffer(&self) -> Local { 186 | unsafe { Local::from_raw(v8__WasmMemoryObject__Buffer(self)) }.unwrap() 187 | } 188 | } 189 | 190 | pub(crate) fn trampoline() 191 | -> unsafe extern "C" fn(*const FunctionCallbackInfo) 192 | where 193 | F: UnitType + Fn(&mut HandleScope, Local, WasmStreaming), 194 | { 195 | unsafe extern "C" fn c_fn(info: *const FunctionCallbackInfo) 196 | where 197 | F: UnitType + Fn(&mut HandleScope, Local, WasmStreaming), 198 | { 199 | let info = unsafe { &*info }; 200 | let scope = &mut unsafe { CallbackScope::new(info) }; 201 | let args = FunctionCallbackArguments::from_function_callback_info(info); 202 | let data = args.data(); 203 | let zero = null_mut(); 204 | let mut that = WasmStreamingSharedPtr([zero, zero]); 205 | unsafe { 206 | v8__WasmStreaming__Unpack(scope.get_isolate_ptr(), &*data, &mut that); 207 | }; 208 | let source = args.get(0); 209 | (F::get())(scope, source, WasmStreaming(that)); 210 | } 211 | c_fn:: 212 | } 213 | 214 | unsafe extern "C" { 215 | fn v8__WasmStreaming__Unpack( 216 | isolate: *mut Isolate, 217 | value: *const Value, 218 | that: *mut WasmStreamingSharedPtr, // Out parameter. 219 | ); 220 | fn v8__WasmStreaming__shared_ptr_DESTRUCT(this: *mut WasmStreamingSharedPtr); 221 | fn v8__WasmStreaming__OnBytesReceived( 222 | this: *mut WasmStreamingSharedPtr, 223 | data: *const u8, 224 | len: usize, 225 | ); 226 | fn v8__WasmStreaming__Finish(this: *mut WasmStreamingSharedPtr); 227 | fn v8__WasmStreaming__Abort( 228 | this: *mut WasmStreamingSharedPtr, 229 | exception: *const Value, 230 | ); 231 | fn v8__WasmStreaming__SetUrl( 232 | this: *mut WasmStreamingSharedPtr, 233 | url: *const char, 234 | len: usize, 235 | ); 236 | 237 | fn v8__WasmModuleObject__FromCompiledModule( 238 | isolate: *mut Isolate, 239 | compiled_module: *const InternalCompiledWasmModule, 240 | ) -> *const WasmModuleObject; 241 | fn v8__WasmModuleObject__GetCompiledModule( 242 | this: *const WasmModuleObject, 243 | ) -> *mut InternalCompiledWasmModule; 244 | fn v8__WasmModuleObject__Compile( 245 | isolate: *mut Isolate, 246 | wire_bytes_data: *const u8, 247 | length: usize, 248 | ) -> *mut WasmModuleObject; 249 | 250 | fn v8__CompiledWasmModule__GetWireBytesRef( 251 | this: *mut InternalCompiledWasmModule, 252 | length: *mut isize, 253 | ) -> *const u8; 254 | fn v8__CompiledWasmModule__SourceUrl( 255 | this: *mut InternalCompiledWasmModule, 256 | length: *mut usize, 257 | ) -> *const char; 258 | fn v8__CompiledWasmModule__DELETE(this: *mut InternalCompiledWasmModule); 259 | 260 | fn v8__WasmMemoryObject__Buffer( 261 | this: *const WasmMemoryObject, 262 | ) -> *mut ArrayBuffer; 263 | } 264 | -------------------------------------------------------------------------------- /tests/compile_fail/boxed_local.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. 2 | 3 | pub fn main() { 4 | let mut isolate = v8::Isolate::new(mock()); 5 | let mut scope1 = v8::HandleScope::new(&mut isolate); 6 | 7 | let _boxed_local = { 8 | let mut scope2 = v8::HandleScope::new(&mut scope1); 9 | let mut scope3 = v8::HandleScope::new(&mut scope2); 10 | Box::new(v8::Integer::new(&mut scope3, 123)) 11 | }; 12 | } 13 | 14 | fn mock() -> T { 15 | unimplemented!() 16 | } 17 | -------------------------------------------------------------------------------- /tests/compile_fail/boxed_local.stderr: -------------------------------------------------------------------------------- 1 | error[E0597]: `scope2` does not live long enough 2 | --> tests/compile_fail/boxed_local.rs:9:43 3 | | 4 | 7 | let _boxed_local = { 5 | | ------------ borrow later stored here 6 | 8 | let mut scope2 = v8::HandleScope::new(&mut scope1); 7 | | ---------- binding `scope2` declared here 8 | 9 | let mut scope3 = v8::HandleScope::new(&mut scope2); 9 | | ^^^^^^^^^^^ borrowed value does not live long enough 10 | 10 | Box::new(v8::Integer::new(&mut scope3, 123)) 11 | 11 | }; 12 | | - `scope2` dropped here while still borrowed 13 | -------------------------------------------------------------------------------- /tests/compile_fail/handle_scope_escape_lifetime.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. 2 | 3 | pub fn main() { 4 | let mut isolate = v8::Isolate::new(mock()); 5 | let mut scope1 = v8::HandleScope::new(&mut isolate); 6 | 7 | let _local = { 8 | let mut scope2 = v8::HandleScope::new(&mut scope1); 9 | let mut scope3 = v8::HandleScope::new(&mut scope2); 10 | let mut scope4 = v8::EscapableHandleScope::new(&mut scope3); 11 | let value = v8::Integer::new(&mut scope4, 42); 12 | scope4.escape(value) 13 | }; 14 | } 15 | 16 | fn mock() -> T { 17 | unimplemented!() 18 | } 19 | -------------------------------------------------------------------------------- /tests/compile_fail/handle_scope_escape_lifetime.stderr: -------------------------------------------------------------------------------- 1 | error[E0597]: `scope2` does not live long enough 2 | --> tests/compile_fail/handle_scope_escape_lifetime.rs:9:43 3 | | 4 | 7 | let _local = { 5 | | ------ borrow later stored here 6 | 8 | let mut scope2 = v8::HandleScope::new(&mut scope1); 7 | | ---------- binding `scope2` declared here 8 | 9 | let mut scope3 = v8::HandleScope::new(&mut scope2); 9 | | ^^^^^^^^^^^ borrowed value does not live long enough 10 | ... 11 | 13 | }; 12 | | - `scope2` dropped here while still borrowed 13 | -------------------------------------------------------------------------------- /tests/compile_fail/handle_scope_escape_to_nowhere.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. 2 | 3 | pub fn main() { 4 | let mut isolate = v8::Isolate::new(mock()); 5 | let mut _scope = v8::EscapableHandleScope::new(&mut isolate); 6 | } 7 | 8 | fn mock() -> T { 9 | unimplemented!() 10 | } 11 | -------------------------------------------------------------------------------- /tests/compile_fail/handle_scope_escape_to_nowhere.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `OwnedIsolate: v8::scope::param::NewEscapableHandleScope<'_, '_>` is not satisfied 2 | --> tests/compile_fail/handle_scope_escape_to_nowhere.rs:5:50 3 | | 4 | 5 | let mut _scope = v8::EscapableHandleScope::new(&mut isolate); 5 | | ----------------------------- ^^^^^^^^^^^^ the trait `v8::scope::param::NewEscapableHandleScope<'_, '_>` is not implemented for `OwnedIsolate` 6 | | | 7 | | required by a bound introduced by this call 8 | | 9 | = help: the following other types implement trait `v8::scope::param::NewEscapableHandleScope<'s, 'e>`: 10 | `AllowJavascriptExecutionScope<'p, P>` implements `v8::scope::param::NewEscapableHandleScope<'s, 'e>` 11 | `CallbackScope<'p, C>` implements `v8::scope::param::NewEscapableHandleScope<'s, 'p>` 12 | `ContextScope<'p, P>` implements `v8::scope::param::NewEscapableHandleScope<'s, 'e>` 13 | `DisallowJavascriptExecutionScope<'p, P>` implements `v8::scope::param::NewEscapableHandleScope<'s, 'e>` 14 | `EscapableHandleScope<'p, 'e, C>` implements `v8::scope::param::NewEscapableHandleScope<'s, 'p>` 15 | `HandleScope<'p, C>` implements `v8::scope::param::NewEscapableHandleScope<'s, 'p>` 16 | `TryCatch<'p, P>` implements `v8::scope::param::NewEscapableHandleScope<'s, 'e>` 17 | note: required by a bound in `EscapableHandleScope::<'s, 'e>::new` 18 | --> src/scope.rs 19 | | 20 | | pub fn new>( 21 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `EscapableHandleScope::<'s, 'e>::new` 22 | 23 | error[E0277]: the trait bound `OwnedIsolate: v8::scope::param::NewEscapableHandleScope<'_, '_>` is not satisfied 24 | --> tests/compile_fail/handle_scope_escape_to_nowhere.rs:5:20 25 | | 26 | 5 | let mut _scope = v8::EscapableHandleScope::new(&mut isolate); 27 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `v8::scope::param::NewEscapableHandleScope<'_, '_>` is not implemented for `OwnedIsolate` 28 | | 29 | = help: the following other types implement trait `v8::scope::param::NewEscapableHandleScope<'s, 'e>`: 30 | `AllowJavascriptExecutionScope<'p, P>` implements `v8::scope::param::NewEscapableHandleScope<'s, 'e>` 31 | `CallbackScope<'p, C>` implements `v8::scope::param::NewEscapableHandleScope<'s, 'p>` 32 | `ContextScope<'p, P>` implements `v8::scope::param::NewEscapableHandleScope<'s, 'e>` 33 | `DisallowJavascriptExecutionScope<'p, P>` implements `v8::scope::param::NewEscapableHandleScope<'s, 'e>` 34 | `EscapableHandleScope<'p, 'e, C>` implements `v8::scope::param::NewEscapableHandleScope<'s, 'p>` 35 | `HandleScope<'p, C>` implements `v8::scope::param::NewEscapableHandleScope<'s, 'p>` 36 | `TryCatch<'p, P>` implements `v8::scope::param::NewEscapableHandleScope<'s, 'e>` 37 | -------------------------------------------------------------------------------- /tests/compile_fail/handle_scope_lifetime_1.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. 2 | 3 | pub fn main() { 4 | let mut isolate = v8::Isolate::new(mock()); 5 | let mut scope1 = v8::HandleScope::new(&mut isolate); 6 | let mut _scope2 = v8::EscapableHandleScope::new(&mut scope1); 7 | let _local = v8::Integer::new(&mut scope1, 123); 8 | } 9 | 10 | fn mock() -> T { 11 | unimplemented!() 12 | } 13 | -------------------------------------------------------------------------------- /tests/compile_fail/handle_scope_lifetime_1.stderr: -------------------------------------------------------------------------------- 1 | error[E0499]: cannot borrow `scope1` as mutable more than once at a time 2 | --> $DIR/handle_scope_lifetime_1.rs:7:33 3 | | 4 | 6 | let mut _scope2 = v8::EscapableHandleScope::new(&mut scope1); 5 | | ----------- first mutable borrow occurs here 6 | 7 | let _local = v8::Integer::new(&mut scope1, 123); 7 | | ^^^^^^^^^^^ second mutable borrow occurs here 8 | 8 | } 9 | | - first borrow might be used here, when `_scope2` is dropped and runs the `Drop` code for type `EscapableHandleScope` 10 | -------------------------------------------------------------------------------- /tests/compile_fail/handle_scope_lifetime_2.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. 2 | 3 | pub fn main() { 4 | let mut isolate = v8::Isolate::new(mock()); 5 | let mut scope1 = v8::HandleScope::new(&mut isolate); 6 | let mut scope2 = v8::EscapableHandleScope::new(&mut scope1); 7 | let _local1 = v8::Integer::new(&mut scope1, 123); 8 | let _local2 = v8::Integer::new(&mut scope2, 123); 9 | } 10 | 11 | fn mock() -> T { 12 | unimplemented!() 13 | } 14 | -------------------------------------------------------------------------------- /tests/compile_fail/handle_scope_lifetime_2.stderr: -------------------------------------------------------------------------------- 1 | error[E0499]: cannot borrow `scope1` as mutable more than once at a time 2 | --> $DIR/handle_scope_lifetime_2.rs:7:34 3 | | 4 | 6 | let mut scope2 = v8::EscapableHandleScope::new(&mut scope1); 5 | | ----------- first mutable borrow occurs here 6 | 7 | let _local1 = v8::Integer::new(&mut scope1, 123); 7 | | ^^^^^^^^^^^ second mutable borrow occurs here 8 | 8 | let _local2 = v8::Integer::new(&mut scope2, 123); 9 | | ----------- first borrow later used here 10 | -------------------------------------------------------------------------------- /tests/compile_fail/handle_scope_lifetime_3.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. 2 | pub fn main() { 3 | let mut isolate = v8::Isolate::new(mock()); 4 | let mut scope1 = v8::HandleScope::new(&mut isolate); 5 | 6 | let _local = { 7 | let mut _scope2 = v8::EscapableHandleScope::new(&mut scope1); 8 | v8::Integer::new(&mut scope1, 123) 9 | }; 10 | } 11 | 12 | fn mock() -> T { 13 | unimplemented!() 14 | } 15 | -------------------------------------------------------------------------------- /tests/compile_fail/handle_scope_lifetime_3.stderr: -------------------------------------------------------------------------------- 1 | error[E0499]: cannot borrow `scope1` as mutable more than once at a time 2 | --> $DIR/handle_scope_lifetime_3.rs:8:22 3 | | 4 | 7 | let mut _scope2 = v8::EscapableHandleScope::new(&mut scope1); 5 | | ----------- first mutable borrow occurs here 6 | 8 | v8::Integer::new(&mut scope1, 123) 7 | | ^^^^^^^^^^^ second mutable borrow occurs here 8 | 9 | }; 9 | | - first borrow might be used here, when `_scope2` is dropped and runs the `Drop` code for type `EscapableHandleScope` 10 | -------------------------------------------------------------------------------- /tests/compile_fail/handle_scope_lifetime_4.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. 2 | 3 | pub fn main() { 4 | let mut isolate = v8::Isolate::new(mock()); 5 | let mut scope1 = v8::HandleScope::new(&mut isolate); 6 | 7 | let mut _scope3 = { 8 | let mut scope2 = v8::HandleScope::new(&mut scope1); 9 | v8::EscapableHandleScope::new(&mut scope2) 10 | }; 11 | } 12 | 13 | fn mock() -> T { 14 | unimplemented!() 15 | } 16 | -------------------------------------------------------------------------------- /tests/compile_fail/handle_scope_lifetime_4.stderr: -------------------------------------------------------------------------------- 1 | error[E0597]: `scope2` does not live long enough 2 | --> tests/compile_fail/handle_scope_lifetime_4.rs:9:35 3 | | 4 | 7 | let mut _scope3 = { 5 | | ----------- borrow later stored here 6 | 8 | let mut scope2 = v8::HandleScope::new(&mut scope1); 7 | | ---------- binding `scope2` declared here 8 | 9 | v8::EscapableHandleScope::new(&mut scope2) 9 | | ^^^^^^^^^^^ borrowed value does not live long enough 10 | 10 | }; 11 | | - `scope2` dropped here while still borrowed 12 | -------------------------------------------------------------------------------- /tests/compile_fail/object_without_context_scope.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. 2 | 3 | pub fn main() { 4 | let mut isolate = v8::Isolate::new(mock()); 5 | let mut scope = v8::HandleScope::new(&mut isolate); 6 | let _object = v8::Object::new(&mut scope); 7 | } 8 | 9 | fn mock() -> T { 10 | unimplemented!() 11 | } 12 | -------------------------------------------------------------------------------- /tests/compile_fail/object_without_context_scope.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/compile_fail/object_without_context_scope.rs:6:33 3 | | 4 | 6 | let _object = v8::Object::new(&mut scope); 5 | | --------------- ^^^^^^^^^^ expected `&mut HandleScope<'_>`, found `&mut HandleScope<'_, ()>` 6 | | | 7 | | arguments to this function are incorrect 8 | | 9 | = note: expected mutable reference `&mut HandleScope<'_, v8::Context>` 10 | found mutable reference `&mut HandleScope<'_, ()>` 11 | note: associated function defined here 12 | --> src/object.rs 13 | | 14 | | pub fn new<'s>(scope: &mut HandleScope<'s>) -> Local<'s, Object> { 15 | | ^^^ 16 | -------------------------------------------------------------------------------- /tests/compile_fail/try_catch_exception_lifetime.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. 2 | 3 | pub fn main() { 4 | let mut isolate = v8::Isolate::new(mock()); 5 | let mut scope1 = v8::HandleScope::new(&mut isolate); 6 | let context = v8::Context::new(&mut scope1, Default::default()); 7 | let mut scope2 = v8::ContextScope::new(&mut scope1, context); 8 | 9 | let _exception = { 10 | let mut scope3 = v8::HandleScope::new(&mut scope2); 11 | let mut scope4 = v8::HandleScope::new(&mut scope3); 12 | let mut try_catch = v8::TryCatch::new(&mut scope4); 13 | try_catch.exception().unwrap() 14 | }; 15 | } 16 | 17 | fn mock() -> T { 18 | unimplemented!() 19 | } 20 | -------------------------------------------------------------------------------- /tests/compile_fail/try_catch_exception_lifetime.stderr: -------------------------------------------------------------------------------- 1 | error[E0597]: `scope3` does not live long enough 2 | --> tests/compile_fail/try_catch_exception_lifetime.rs:11:43 3 | | 4 | 9 | let _exception = { 5 | | ---------- borrow later stored here 6 | 10 | let mut scope3 = v8::HandleScope::new(&mut scope2); 7 | | ---------- binding `scope3` declared here 8 | 11 | let mut scope4 = v8::HandleScope::new(&mut scope3); 9 | | ^^^^^^^^^^^ borrowed value does not live long enough 10 | ... 11 | 14 | }; 12 | | - `scope3` dropped here while still borrowed 13 | -------------------------------------------------------------------------------- /tests/compile_fail/try_catch_message_lifetime.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. 2 | 3 | pub fn main() { 4 | let mut isolate = v8::Isolate::new(mock()); 5 | let mut scope1 = v8::HandleScope::new(&mut isolate); 6 | let context = v8::Context::new(&mut scope1, Default::default()); 7 | let mut scope2 = v8::ContextScope::new(&mut scope1, context); 8 | 9 | let _message = { 10 | let mut scope3 = v8::HandleScope::new(&mut scope2); 11 | let mut scope4 = v8::HandleScope::new(&mut scope3); 12 | let mut try_catch = v8::TryCatch::new(&mut scope4); 13 | try_catch.message().unwrap() 14 | }; 15 | } 16 | 17 | fn mock() -> T { 18 | unimplemented!() 19 | } 20 | -------------------------------------------------------------------------------- /tests/compile_fail/try_catch_message_lifetime.stderr: -------------------------------------------------------------------------------- 1 | error[E0597]: `scope3` does not live long enough 2 | --> tests/compile_fail/try_catch_message_lifetime.rs:11:43 3 | | 4 | 9 | let _message = { 5 | | -------- borrow later stored here 6 | 10 | let mut scope3 = v8::HandleScope::new(&mut scope2); 7 | | ---------- binding `scope3` declared here 8 | 11 | let mut scope4 = v8::HandleScope::new(&mut scope3); 9 | | ^^^^^^^^^^^ borrowed value does not live long enough 10 | ... 11 | 14 | }; 12 | | - `scope3` dropped here while still borrowed 13 | -------------------------------------------------------------------------------- /tests/test_api_entropy_source.rs: -------------------------------------------------------------------------------- 1 | // Tests from the same file run in a single process. That's why this test 2 | // is in its own file, because changing the entropy source affects the 3 | // whole process. 4 | use std::sync::atomic::AtomicUsize; 5 | use std::sync::atomic::Ordering; 6 | 7 | static CALLS: AtomicUsize = AtomicUsize::new(0); 8 | 9 | #[test] 10 | fn set_entropy_source() { 11 | const N: usize = 3; 12 | 13 | v8::V8::set_entropy_source(|buf| { 14 | CALLS.fetch_add(1, Ordering::SeqCst); 15 | for c in buf { 16 | *c = 42; 17 | } 18 | true 19 | }); 20 | 21 | v8::V8::initialize_platform( 22 | v8::new_unprotected_default_platform(0, false).make_shared(), 23 | ); 24 | v8::V8::initialize(); 25 | 26 | // Assumes that every isolate creates a PRNG from scratch, which is currently true. 27 | let mut results = vec![]; 28 | for _ in 0..N { 29 | let isolate = &mut v8::Isolate::new(Default::default()); 30 | let scope = &mut v8::HandleScope::new(isolate); 31 | let context = v8::Context::new(scope, Default::default()); 32 | let scope = &mut v8::ContextScope::new(scope, context); 33 | let source = v8::String::new(scope, "Math.random()").unwrap(); 34 | let script = v8::Script::compile(scope, source, None).unwrap(); 35 | let result = script.run(scope).unwrap(); 36 | let result = result.to_string(scope).unwrap(); 37 | let result = result.to_rust_string_lossy(scope); 38 | results.push(result); 39 | } 40 | 41 | unsafe { 42 | v8::V8::dispose(); 43 | } 44 | v8::V8::dispose_platform(); 45 | 46 | // All runs should have produced the same value. 47 | assert_eq!(results.len(), N); 48 | results.dedup(); 49 | assert_eq!(results.len(), 1); 50 | 51 | // Doesn't have to be exactly N because V8 also calls 52 | // the EntropySource for things like hash seeds and ASLR. 53 | assert!(CALLS.load(Ordering::SeqCst) >= N); 54 | } 55 | -------------------------------------------------------------------------------- /tests/test_api_flags.rs: -------------------------------------------------------------------------------- 1 | // Tests from the same file run in a single process. That's why this test 2 | // is in its own file, because changing flags affects the whole process. 3 | 4 | #[test] 5 | fn set_flags_from_string() { 6 | v8::V8::set_flags_from_string("--use_strict"); 7 | v8::V8::initialize_platform( 8 | v8::new_unprotected_default_platform(0, false).make_shared(), 9 | ); 10 | v8::V8::initialize(); 11 | let isolate = &mut v8::Isolate::new(Default::default()); 12 | let scope = &mut v8::HandleScope::new(isolate); 13 | let context = v8::Context::new(scope, Default::default()); 14 | let scope = &mut v8::ContextScope::new(scope, context); 15 | let source = "(function() { return this })()"; 16 | let source = v8::String::new(scope, source).unwrap(); 17 | let script = v8::Script::compile(scope, source, None).unwrap(); 18 | let result = script.run(scope).unwrap(); 19 | assert!(result.is_undefined()); // Because of --use_strict. 20 | } 21 | -------------------------------------------------------------------------------- /tests/test_concurrent_isolate_creation_and_disposal.rs: -------------------------------------------------------------------------------- 1 | // This is flaky on cross (QEMU bug) 2 | // but otherwise works fine on real device. 3 | #![cfg(not(target_arch = "aarch64"))] 4 | 5 | use std::iter::repeat_with; 6 | use std::thread; 7 | 8 | #[test] 9 | fn concurrent_isolate_creation_and_disposal() { 10 | let platform = v8::new_single_threaded_default_platform(false).make_shared(); 11 | v8::V8::initialize_platform(platform); 12 | v8::V8::initialize(); 13 | 14 | for round in 0..1000 { 15 | eprintln!("round {round}"); 16 | 17 | let threads = repeat_with(|| { 18 | thread::spawn(|| { 19 | v8::Isolate::new(Default::default()); 20 | }) 21 | }) 22 | .take(16) 23 | .collect::>(); 24 | 25 | for join_handle in threads { 26 | join_handle.join().unwrap(); 27 | } 28 | } 29 | 30 | unsafe { v8::V8::dispose() }; 31 | v8::V8::dispose_platform(); 32 | } 33 | -------------------------------------------------------------------------------- /tests/test_cppgc.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the Deno authors. All rights reserved. MIT license. 2 | use std::sync::atomic::{AtomicUsize, Ordering}; 3 | use v8::cppgc::{GarbageCollected, Visitor}; 4 | 5 | mod setup { 6 | use std::sync::Once; 7 | use std::sync::RwLock; 8 | // use std::sync::RwLockReadGuard; 9 | use std::sync::RwLockWriteGuard; 10 | 11 | static PROCESS_LOCK: RwLock<()> = RwLock::new(()); 12 | 13 | /* 14 | /// Set up global state for a test that can run in parallel with other tests. 15 | pub(super) fn parallel_test() -> SetupGuard> { 16 | initialize_once(); 17 | SetupGuard::new(PROCESS_LOCK.read().unwrap()) 18 | } 19 | */ 20 | 21 | /// Set up global state for a test that must be the only test running. 22 | pub(super) fn sequential_test() -> SetupGuard> { 23 | initialize_once(); 24 | SetupGuard::new(PROCESS_LOCK.write().unwrap()) 25 | } 26 | 27 | fn initialize_once() { 28 | static START: Once = Once::new(); 29 | START.call_once(|| { 30 | assert!(v8::icu::set_common_data_74(align_data::include_aligned!( 31 | align_data::Align16, 32 | "../third_party/icu/common/icudtl.dat" 33 | )) 34 | .is_ok()); 35 | v8::V8::set_flags_from_string( 36 | "--no_freeze_flags_after_init --expose_gc --harmony-import-assertions --harmony-shadow-realm --allow_natives_syntax --turbo_fast_api_calls", 37 | ); 38 | 39 | let platform = v8::new_unprotected_default_platform(0, false).make_shared(); 40 | v8::V8::initialize_platform(platform.clone()); 41 | v8::V8::initialize(); 42 | }); 43 | } 44 | 45 | #[must_use] 46 | pub(super) struct SetupGuard { 47 | _inner: G, 48 | } 49 | 50 | impl SetupGuard { 51 | fn new(inner: G) -> Self { 52 | Self { _inner: inner } 53 | } 54 | } 55 | } 56 | 57 | const TAG: u16 = 1; 58 | 59 | macro_rules! test { 60 | ( $( $decln:ident : $declt:ty )?, $( $initn:ident : $inite:expr )? ) => {{ 61 | let _guard = setup::sequential_test(); 62 | 63 | static TRACE_COUNT: AtomicUsize = AtomicUsize::new(0); 64 | static DROP_COUNT: AtomicUsize = AtomicUsize::new(0); 65 | 66 | struct Wrap { 67 | $( #[allow(unused)] $decln: $declt , )? 68 | value: v8::TracedReference, 69 | } 70 | 71 | impl GarbageCollected for Wrap { 72 | fn trace(&self, visitor: &Visitor) { 73 | TRACE_COUNT.fetch_add(1, Ordering::SeqCst); 74 | visitor.trace(&self.value); 75 | } 76 | 77 | fn get_name(&self) -> &'static std::ffi::CStr { 78 | c"Eyecatcher" 79 | } 80 | } 81 | 82 | impl Drop for Wrap { 83 | fn drop(&mut self) { 84 | DROP_COUNT.fetch_add(1, Ordering::SeqCst); 85 | } 86 | } 87 | 88 | fn op_wrap( 89 | scope: &mut v8::HandleScope, 90 | args: v8::FunctionCallbackArguments, 91 | mut rv: v8::ReturnValue, 92 | ) { 93 | fn empty( 94 | _scope: &mut v8::HandleScope, 95 | _args: v8::FunctionCallbackArguments, 96 | _rv: v8::ReturnValue, 97 | ) { 98 | } 99 | let templ = v8::FunctionTemplate::new(scope, empty); 100 | let func = templ.get_function(scope).unwrap(); 101 | let obj = func.new_instance(scope, &[]).unwrap(); 102 | assert!(obj.is_api_wrapper()); 103 | 104 | let wrap = Wrap { 105 | $( $initn: $inite , )? 106 | value: v8::TracedReference::new(scope, args.get(0)), 107 | }; 108 | let member = unsafe { 109 | v8::cppgc::make_garbage_collected(scope.get_cpp_heap().unwrap(), wrap) 110 | }; 111 | 112 | unsafe { 113 | v8::Object::wrap::(scope, obj, &member); 114 | } 115 | 116 | rv.set(obj.into()); 117 | } 118 | 119 | fn op_unwrap( 120 | scope: &mut v8::HandleScope, 121 | args: v8::FunctionCallbackArguments, 122 | mut rv: v8::ReturnValue, 123 | ) { 124 | let obj = args.get(0).try_into().unwrap(); 125 | let member = unsafe { v8::Object::unwrap::(scope, obj) }; 126 | rv.set(member.unwrap().value.get(scope).unwrap()); 127 | } 128 | 129 | { 130 | let isolate = &mut v8::Isolate::new(v8::CreateParams::default()); 131 | 132 | { 133 | let handle_scope = &mut v8::HandleScope::new(isolate); 134 | let context = v8::Context::new(handle_scope, Default::default()); 135 | let scope = &mut v8::ContextScope::new(handle_scope, context); 136 | let global = context.global(scope); 137 | { 138 | let func = v8::Function::new(scope, op_wrap).unwrap(); 139 | let name = v8::String::new(scope, "wrap").unwrap(); 140 | global.set(scope, name.into(), func.into()).unwrap(); 141 | } 142 | { 143 | let func = v8::Function::new(scope, op_unwrap).unwrap(); 144 | let name = v8::String::new(scope, "unwrap").unwrap(); 145 | global.set(scope, name.into(), func.into()).unwrap(); 146 | } 147 | 148 | execute_script( 149 | scope, 150 | r#" 151 | { 152 | const x = {}; 153 | const y = unwrap(wrap(x)); // collected 154 | if (x !== y) { 155 | throw new Error('mismatch'); 156 | } 157 | } 158 | 159 | globalThis.wrapped = wrap(wrap({})); // not collected 160 | "#, 161 | ); 162 | 163 | assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 0); 164 | 165 | { 166 | let mut vec = Vec::::new(); 167 | scope.take_heap_snapshot(|chunk| { 168 | vec.extend_from_slice(chunk); 169 | true 170 | }); 171 | let s = std::str::from_utf8(&vec).unwrap(); 172 | assert!(s.contains("Eyecatcher")); 173 | } 174 | 175 | scope.request_garbage_collection_for_testing( 176 | v8::GarbageCollectionType::Full, 177 | ); 178 | 179 | assert!(TRACE_COUNT.load(Ordering::SeqCst) > 0); 180 | assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 1); 181 | 182 | execute_script( 183 | scope, 184 | r#" 185 | globalThis.wrapped = undefined; 186 | "#, 187 | ); 188 | 189 | scope.request_garbage_collection_for_testing( 190 | v8::GarbageCollectionType::Full, 191 | ); 192 | 193 | assert_eq!(DROP_COUNT.load(Ordering::SeqCst), 3); 194 | } 195 | } 196 | }} 197 | } 198 | 199 | #[test] 200 | fn cppgc_object_wrap8() { 201 | test!(,); 202 | } 203 | 204 | #[test] 205 | fn cppgc_object_wrap16() { 206 | test!(big: u128, big: 0); 207 | } 208 | 209 | fn execute_script( 210 | context_scope: &mut v8::ContextScope, 211 | source: &str, 212 | ) { 213 | let scope = &mut v8::HandleScope::new(context_scope); 214 | let scope = &mut v8::TryCatch::new(scope); 215 | 216 | let source = v8::String::new(scope, source).unwrap(); 217 | 218 | let script = 219 | v8::Script::compile(scope, source, None).expect("failed to compile script"); 220 | 221 | if script.run(scope).is_none() { 222 | let exception_string = scope 223 | .stack_trace() 224 | .or_else(|| scope.exception()) 225 | .map_or_else( 226 | || "no stack trace".into(), 227 | |value| value.to_rust_string_lossy(scope), 228 | ); 229 | 230 | panic!("{exception_string}"); 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /tests/test_external_deserialize.rs: -------------------------------------------------------------------------------- 1 | use v8::MapFnTo; 2 | 3 | fn callback( 4 | scope: &mut v8::HandleScope, 5 | args: v8::FunctionCallbackArguments, 6 | mut rv: v8::ReturnValue, 7 | ) { 8 | let data = args.data().cast::().value(); 9 | let data = data as u64; 10 | rv.set(v8::BigInt::new_from_u64(scope, data).into()); 11 | } 12 | 13 | #[test] 14 | fn external_deserialize() { 15 | let platform = v8::new_default_platform(0, false).make_shared(); 16 | v8::V8::initialize_platform(platform); 17 | v8::V8::initialize(); 18 | 19 | let blob = { 20 | let external_references = [ 21 | v8::ExternalReference { 22 | function: callback.map_fn_to(), 23 | }, 24 | v8::ExternalReference { pointer: 1 as _ }, 25 | v8::ExternalReference { 26 | pointer: std::ptr::null_mut(), 27 | }, 28 | ]; 29 | 30 | let mut isolate = v8::Isolate::snapshot_creator( 31 | Some(external_references.to_vec().into()), 32 | Some(v8::CreateParams::default()), 33 | ); 34 | 35 | { 36 | let scope = &mut v8::HandleScope::new(&mut isolate); 37 | let context = v8::Context::new(scope, Default::default()); 38 | scope.set_default_context(context); 39 | 40 | let scope = &mut v8::ContextScope::new(scope, context); 41 | let data = v8::External::new(scope, 1 as _); 42 | let ft = v8::FunctionTemplate::builder(callback) 43 | .data(data.into()) 44 | .build(scope); 45 | let f = ft.get_function(scope).unwrap(); 46 | 47 | let global = context.global(scope); 48 | let key = v8::String::new(scope, "f").unwrap(); 49 | global.set(scope, key.into(), f.into()).unwrap(); 50 | } 51 | 52 | isolate.create_blob(v8::FunctionCodeHandling::Keep).unwrap() 53 | }; 54 | 55 | { 56 | let external_references = [ 57 | v8::ExternalReference { 58 | function: callback.map_fn_to(), 59 | }, 60 | v8::ExternalReference { pointer: 2 as _ }, 61 | v8::ExternalReference { 62 | pointer: std::ptr::null_mut(), 63 | }, 64 | ]; 65 | 66 | let mut _isolate_a = v8::Isolate::new( 67 | v8::CreateParams::default() 68 | .snapshot_blob(blob.clone()) 69 | .external_references(external_references.to_vec().into()), 70 | ); 71 | 72 | let external_references = [ 73 | v8::ExternalReference { 74 | function: callback.map_fn_to(), 75 | }, 76 | v8::ExternalReference { pointer: 3 as _ }, 77 | v8::ExternalReference { 78 | pointer: std::ptr::null_mut(), 79 | }, 80 | ]; 81 | 82 | let mut isolate_b = v8::Isolate::new( 83 | v8::CreateParams::default() 84 | .snapshot_blob(blob) 85 | .external_references(external_references.to_vec().into()), 86 | ); 87 | 88 | { 89 | let scope = &mut v8::HandleScope::new(&mut isolate_b); 90 | let context = v8::Context::new(scope, Default::default()); 91 | let scope = &mut v8::ContextScope::new(scope, context); 92 | 93 | let global = context.global(scope); 94 | let key = v8::String::new(scope, "f").unwrap(); 95 | let f = global 96 | .get(scope, key.into()) 97 | .unwrap() 98 | .cast::(); 99 | let null = v8::null(scope); 100 | let result = f.call(scope, null.into(), &[]); 101 | assert_eq!(result.unwrap().to_rust_string_lossy(scope), "3"); 102 | } 103 | } 104 | 105 | unsafe { 106 | v8::V8::dispose(); 107 | } 108 | v8::V8::dispose_platform(); 109 | } 110 | -------------------------------------------------------------------------------- /tests/test_platform_atomics_pump_message_loop.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn atomics_pump_message_loop() { 3 | v8::V8::set_flags_from_string("--allow-natives-syntax"); 4 | v8::V8::initialize_platform( 5 | v8::new_unprotected_default_platform(0, false).make_shared(), 6 | ); 7 | v8::V8::initialize(); 8 | let isolate = &mut v8::Isolate::new(Default::default()); 9 | let scope = &mut v8::HandleScope::new(isolate); 10 | let context = v8::Context::new(scope, Default::default()); 11 | let scope = &mut v8::ContextScope::new(scope, context); 12 | let source = r#" 13 | function assertEquals(a, b) { 14 | if (a === b) return; 15 | throw a + " does not equal " + b; 16 | } 17 | 18 | const sab = new SharedArrayBuffer(16); 19 | const i32a = new Int32Array(sab); 20 | 21 | let resolved = false; 22 | (function() { 23 | const result = Atomics.waitAsync(i32a, 0, 0); 24 | result.value.then( 25 | (value) => { assertEquals("ok", value); resolved = true; }, 26 | () => { assertUnreachable(); 27 | }); 28 | })(); 29 | 30 | const notify_return_value = Atomics.notify(i32a, 0, 1); 31 | assertEquals(1, notify_return_value); 32 | assertEquals(0, %AtomicsNumWaitersForTesting(i32a, 0)); 33 | assertEquals(1, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0)); 34 | "#; 35 | let source = v8::String::new(scope, source).unwrap(); 36 | let script = v8::Script::compile(scope, source, None).unwrap(); 37 | script.run(scope).unwrap(); 38 | 39 | while v8::Platform::pump_message_loop( 40 | &v8::V8::get_current_platform(), 41 | scope, 42 | false, 43 | ) { 44 | // do nothing 45 | } 46 | 47 | let source2 = r#" 48 | assertEquals(0, %AtomicsNumUnresolvedAsyncPromisesForTesting(i32a, 0)); 49 | "#; 50 | let source2 = v8::String::new(scope, source2).unwrap(); 51 | let script2 = v8::Script::compile(scope, source2, None).unwrap(); 52 | script2.run(scope).unwrap(); 53 | } 54 | -------------------------------------------------------------------------------- /tests/test_single_threaded_default_platform.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn single_threaded_default_platform() { 3 | v8::V8::set_flags_from_string("--single_threaded"); 4 | v8::V8::initialize_platform( 5 | v8::new_single_threaded_default_platform(false).make_shared(), 6 | ); 7 | v8::V8::initialize(); 8 | 9 | { 10 | let isolate = &mut v8::Isolate::new(Default::default()); 11 | let scope = &mut v8::HandleScope::new(isolate); 12 | let context = v8::Context::new(scope, Default::default()); 13 | let scope = &mut v8::ContextScope::new(scope, context); 14 | let source = v8::String::new(scope, "Math.random()").unwrap(); 15 | let script = v8::Script::compile(scope, source, None).unwrap(); 16 | let result = script.run(scope).unwrap(); 17 | let _ = result.to_string(scope).unwrap(); 18 | } 19 | 20 | unsafe { v8::V8::dispose() }; 21 | v8::V8::dispose_platform(); 22 | } 23 | -------------------------------------------------------------------------------- /tests/test_ui.rs: -------------------------------------------------------------------------------- 1 | // Don't run UI tests on emulated environment or nightly build. 2 | #[cfg(not(target_os = "android"))] 3 | #[rustversion::attr(not(nightly), test)] 4 | fn ui() { 5 | // This environment variable tells build.rs that we're running trybuild tests, 6 | // so it won't rebuild V8. 7 | unsafe { 8 | std::env::set_var("DENO_TRYBUILD", "1"); 9 | std::env::set_var( 10 | "RUSTY_V8_SRC_BINDING_PATH", 11 | env!("RUSTY_V8_SRC_BINDING_PATH"), 12 | ); 13 | } 14 | 15 | let t = trybuild::TestCases::new(); 16 | t.compile_fail("tests/compile_fail/*.rs"); 17 | } 18 | -------------------------------------------------------------------------------- /third_party/googletest/src/googletest/include/gtest/gtest_prod.h: -------------------------------------------------------------------------------- 1 | // Copyright 2006, Google Inc. 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are 6 | // met: 7 | // 8 | // * Redistributions of source code must retain the above copyright 9 | // notice, this list of conditions and the following disclaimer. 10 | // * Redistributions in binary form must reproduce the above 11 | // copyright notice, this list of conditions and the following disclaimer 12 | // in the documentation and/or other materials provided with the 13 | // distribution. 14 | // * Neither the name of Google Inc. nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | 30 | // 31 | // Google C++ Testing and Mocking Framework definitions useful in production code. 32 | // GOOGLETEST_CM0003 DO NOT DELETE 33 | 34 | #ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_ 35 | #define GTEST_INCLUDE_GTEST_GTEST_PROD_H_ 36 | 37 | // When you need to test the private or protected members of a class, 38 | // use the FRIEND_TEST macro to declare your tests as friends of the 39 | // class. For example: 40 | // 41 | // class MyClass { 42 | // private: 43 | // void PrivateMethod(); 44 | // FRIEND_TEST(MyClassTest, PrivateMethodWorks); 45 | // }; 46 | // 47 | // class MyClassTest : public testing::Test { 48 | // // ... 49 | // }; 50 | // 51 | // TEST_F(MyClassTest, PrivateMethodWorks) { 52 | // // Can call MyClass::PrivateMethod() here. 53 | // } 54 | // 55 | // Note: The test class must be in the same namespace as the class being tested. 56 | // For example, putting MyClassTest in an anonymous namespace will not work. 57 | 58 | #define FRIEND_TEST(test_case_name, test_name)\ 59 | friend class test_case_name##_##test_name##_Test 60 | 61 | #endif // GTEST_INCLUDE_GTEST_GTEST_PROD_H_ 62 | -------------------------------------------------------------------------------- /tools/auto_update_v8.ts: -------------------------------------------------------------------------------- 1 | const V8_TRACKING_BRANCH = "13.7-lkgr-denoland"; 2 | const AUTOROLL_BRANCH = "autoroll"; 3 | 4 | function extractVersion() { 5 | const MAJOR_PREFIX = "#define V8_MAJOR_VERSION "; 6 | const MINOR_PREFIX = "#define V8_MINOR_VERSION "; 7 | const BUILD_PREFIX = "#define V8_BUILD_NUMBER "; 8 | const PATCH_PREFIX = "#define V8_PATCH_LEVEL "; 9 | 10 | const versionDotH = Deno.readTextFileSync("./v8/include/v8-version.h"); 11 | const lines = versionDotH.split("\n"); 12 | const major = parseInt(lines.find((s) => s.startsWith(MAJOR_PREFIX))! 13 | .substring(MAJOR_PREFIX.length)); 14 | const minor = parseInt(lines.find((s) => s.startsWith(MINOR_PREFIX))! 15 | .substring(MINOR_PREFIX.length)); 16 | const build = parseInt(lines.find((s) => s.startsWith(BUILD_PREFIX))! 17 | .substring(BUILD_PREFIX.length)); 18 | const patch = parseInt(lines.find((s) => s.startsWith(PATCH_PREFIX))! 19 | .substring(PATCH_PREFIX.length)); 20 | 21 | return `${major}.${minor}.${build}.${patch}`; 22 | } 23 | 24 | await run("git", ["checkout", "origin/main"]); 25 | await run("git", ["submodule", "update", "--init", "--recursive", "v8"]); 26 | 27 | const currentVersion = extractVersion(); 28 | console.log(`Starting auto update. Currently on ${currentVersion}`); 29 | 30 | async function run( 31 | cmd: string, 32 | args: string[], 33 | cwd?: string, 34 | ): Promise { 35 | console.log("$", cmd, ...args); 36 | const proc = new Deno.Command(cmd, { args, cwd }); 37 | const output = await proc.output(); 38 | if (!output.success) { 39 | console.error(`Failed to run ${cmd} ${args.join(" ")}`); 40 | Deno.exit(1); 41 | } 42 | return output.stdout; 43 | } 44 | 45 | // Update v8 submodule 46 | await run("git", ["fetch", `origin`, V8_TRACKING_BRANCH], "./v8"); 47 | await run("git", ["checkout", `origin/${V8_TRACKING_BRANCH}`], "./v8"); 48 | 49 | const newVersion = extractVersion(); 50 | if (currentVersion == newVersion) { 51 | console.log(`No new version available. Staying on ${newVersion}`); 52 | Deno.exit(0); 53 | } 54 | 55 | console.log(`Updated to version ${newVersion}`); 56 | 57 | // Update V8 dependencies 58 | const depsOutput = await run("python", ["tools/update_deps.py"]); 59 | const depNames = new TextDecoder().decode(depsOutput).split("\n").filter((x) => 60 | x.length > 0 61 | ).at(-1)!.split( 62 | ",", 63 | ); 64 | 65 | // Update version in readme 66 | let readme = Deno.readTextFileSync("README.md"); 67 | readme = readme.replace( 68 | /V8 Version: \S+/, 69 | `V8 Version: ${newVersion}`, 70 | ); 71 | Deno.writeTextFileSync("README.md", readme); 72 | 73 | // Stage the changes 74 | await run("git", ["add", "v8", "README.md", ...depNames]); 75 | 76 | // Commit the changes 77 | await run("git", ["commit", "-m", `Rolling to V8 ${newVersion}`]); 78 | 79 | // Push to the `denoland/rusty_v8#autoroll` 80 | await run("git", ["push", "origin", `+HEAD:refs/heads/${AUTOROLL_BRANCH}`]); 81 | 82 | // Fetch the remote branch so `gh` cli can find it 83 | await run("git", ["fetch", "origin", AUTOROLL_BRANCH]); 84 | 85 | const proc = new Deno.Command("gh", { 86 | args: ["pr", "view", AUTOROLL_BRANCH, "--json", "state"], 87 | stdout: "piped", 88 | }); 89 | const output = await proc.output(); 90 | const isPrOpen = output.success 91 | ? JSON.parse(new TextDecoder().decode(output.stdout)).state === "OPEN" 92 | : false; 93 | 94 | if (isPrOpen) { 95 | console.log("Already open PR. Editing existing PR."); 96 | await run("gh", [ 97 | "pr", 98 | "edit", 99 | AUTOROLL_BRANCH, 100 | "--title", 101 | `Rolling to V8 ${newVersion}`, 102 | ]); 103 | } else { 104 | console.log("No PR open. Creating a new PR."); 105 | await run("gh", [ 106 | "pr", 107 | "create", 108 | "--title", 109 | `Rolling to V8 ${newVersion}`, 110 | "--body", 111 | "", 112 | "--head", 113 | `denoland:${AUTOROLL_BRANCH}`, 114 | ]); 115 | } 116 | -------------------------------------------------------------------------------- /tools/download_file.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2012 The Chromium 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 | from __future__ import print_function 7 | import argparse 8 | import os 9 | import sys 10 | import time 11 | 12 | try: 13 | from urllib2 import HTTPError, URLError, urlopen 14 | except ImportError: # For Py3 compatibility 15 | from urllib.error import HTTPError, URLError 16 | from urllib.request import urlopen 17 | 18 | 19 | def DownloadUrl(url, output_file): 20 | """Download url into output_file.""" 21 | CHUNK_SIZE = 4096 22 | num_retries = 3 23 | retry_wait_s = 5 # Doubled at each retry. 24 | 25 | while True: 26 | try: 27 | sys.stdout.write('Downloading %s...' % url) 28 | sys.stdout.flush() 29 | response = urlopen(url) 30 | bytes_done = 0 31 | while True: 32 | chunk = response.read(CHUNK_SIZE) 33 | if not chunk: 34 | break 35 | output_file.write(chunk) 36 | bytes_done += len(chunk) 37 | if bytes_done == 0: 38 | raise URLError("empty response") 39 | print(' Done.') 40 | return 41 | except URLError as e: 42 | sys.stdout.write('\n') 43 | print(e) 44 | if num_retries == 0 or isinstance(e, HTTPError) and e.code == 404: 45 | raise e 46 | num_retries -= 1 47 | print('Retrying in %d s ...' % retry_wait_s) 48 | sys.stdout.flush() 49 | time.sleep(retry_wait_s) 50 | retry_wait_s *= 2 51 | 52 | 53 | def main(): 54 | parser = argparse.ArgumentParser(description='Download a file') 55 | parser.add_argument('--filename', help='where to put the file') 56 | parser.add_argument('--url', help='what url to download') 57 | args = parser.parse_args() 58 | with open(args.filename, "wb") as f: 59 | DownloadUrl(args.url, f) 60 | return 0 61 | 62 | 63 | if __name__ == '__main__': 64 | sys.exit(main()) 65 | -------------------------------------------------------------------------------- /tools/get_bindgen_args.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import json 3 | import os 4 | 5 | parser = argparse.ArgumentParser(description='Generate args for bindgen') 6 | parser.add_argument('--gn-out', help='GN out directory') 7 | args = parser.parse_args() 8 | 9 | with open(os.path.join(args.gn_out, 'project.json')) as project_json: 10 | project = json.load(project_json) 11 | 12 | target = project['targets']['//v8:v8_headers'] 13 | 14 | assert '//v8:cppgc_headers' in target['deps'] 15 | 16 | args = [] 17 | 18 | for define in target['defines']: 19 | args.append(f'-D{define}') 20 | 21 | print('\0'.join(args), end="") 22 | -------------------------------------------------------------------------------- /tools/ninja_gn_binaries.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2012 The Chromium 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 | """This script is used to download prebuilt gn/ninja binaries.""" 6 | 7 | import platform 8 | import json 9 | import argparse 10 | import os 11 | import sys 12 | import zipfile 13 | import tempfile 14 | import time 15 | import http.client 16 | from v8_deps import Var 17 | from urllib.error import HTTPError, URLError 18 | from stat import ST_MODE 19 | from urllib.request import urlopen 20 | from urllib.parse import urlparse 21 | 22 | 23 | def get_platform(): 24 | system = platform.system().lower() 25 | if system == 'darwin': 26 | system = 'mac' 27 | machine = platform.machine().lower() 28 | if machine == 'x86_64': 29 | machine = 'amd64' 30 | elif machine == 'aarch64': 31 | machine = 'arm64' 32 | 33 | return f'{system}-{machine}' 34 | 35 | 36 | PLATFORM = get_platform() 37 | is_windows = PLATFORM.startswith('windows') 38 | 39 | RESOLVE_URL = 'https://chrome-infra-packages.appspot.com/_ah/api/repo/v1/instance/resolve?package_name={}&version={}' 40 | INSTANCE_URL = 'https://chrome-infra-packages.appspot.com/_ah/api/repo/v1/instance?package_name={}&instance_id={}' 41 | 42 | NINJA_VERSION = Var('ninja_version') 43 | GN_VERSION = Var('gn_version') 44 | 45 | NINJA_PACKAGE = f'infra/3pp/tools/ninja/{PLATFORM}' 46 | GN_PACKAGE = f'gn/gn/{PLATFORM}' 47 | 48 | 49 | def DownloadUrl(url, output_file): 50 | """Download url into output_file.""" 51 | CHUNK_SIZE = 4096 52 | num_retries = 3 53 | retry_wait_s = 5 # Doubled at each retry. 54 | 55 | while True: 56 | try: 57 | sys.stdout.write('Downloading %s...' % url) 58 | sys.stdout.flush() 59 | response = urlopen(url) 60 | bytes_done = 0 61 | while True: 62 | chunk = response.read(CHUNK_SIZE) 63 | if not chunk: 64 | break 65 | output_file.write(chunk) 66 | bytes_done += len(chunk) 67 | if bytes_done == 0: 68 | raise URLError("empty response") 69 | print(' Done.') 70 | return 71 | except URLError as e: 72 | sys.stdout.write('\n') 73 | print(e) 74 | if num_retries == 0 or isinstance(e, HTTPError) and e.code == 404: 75 | raise e 76 | num_retries -= 1 77 | print('Retrying in %d s ...' % retry_wait_s) 78 | sys.stdout.flush() 79 | time.sleep(retry_wait_s) 80 | retry_wait_s *= 2 81 | 82 | 83 | def EnsureDirExists(path): 84 | if not os.path.exists(path): 85 | os.makedirs(path) 86 | 87 | 88 | def DownloadAndUnpack(url, output_dir): 89 | """Download an archive from url and extract into output_dir.""" 90 | with tempfile.TemporaryFile() as f: 91 | DownloadUrl(url, f) 92 | f.seek(0) 93 | EnsureDirExists(output_dir) 94 | with zipfile.ZipFile(f, 'r') as z: 95 | z.extractall(path=output_dir) 96 | if not is_windows: 97 | for info in z.infolist(): 98 | if info.is_dir(): 99 | continue 100 | file = os.path.join(output_dir, info.filename) 101 | hi = info.external_attr >> 16 102 | if hi: 103 | mode = os.stat(file)[ST_MODE] 104 | mode |= hi 105 | os.chmod(file, mode) 106 | 107 | 108 | def DownloadCIPD(package, tag, output_dir): 109 | def get(url): 110 | parsed = urlparse(url) 111 | conn = http.client.HTTPSConnection(parsed.netloc) 112 | conn.request("GET", parsed.path + (f'?{parsed.query}' if parsed.query else ''), headers={"Host": parsed.netloc}) 113 | response = conn.getresponse() 114 | if response.status != 200: 115 | raise Exception(f'GET {url} returned {response.status} {response.reason}') 116 | data = response.read().decode() 117 | return json.loads(data) 118 | 119 | resolved = get(RESOLVE_URL.format(package, tag)) 120 | instance_id = resolved['instance_id'] 121 | instance = get(INSTANCE_URL.format(package, instance_id)) 122 | DownloadAndUnpack(instance['fetch_url'], output_dir) 123 | 124 | 125 | def main(): 126 | parser = argparse.ArgumentParser(description='Download ninja/gn binaries.') 127 | parser.add_argument('--dir', help='Where to extract the package.') 128 | args = parser.parse_args() 129 | 130 | output_dir = os.path.abspath(args.dir) 131 | 132 | DownloadCIPD(GN_PACKAGE, GN_VERSION, os.path.join(output_dir, 'gn')) 133 | DownloadCIPD(NINJA_PACKAGE, NINJA_VERSION, os.path.join(output_dir, 'ninja')) 134 | 135 | 136 | 137 | if __name__ == '__main__': 138 | sys.exit(main()) 139 | -------------------------------------------------------------------------------- /tools/setup_rbe.py: -------------------------------------------------------------------------------- 1 | """ 2 | This script sets up re_client sort of like https://chromium.googlesource.com/chromium/src/+/main/docs/linux/build_instructions.md#use-reclient 3 | 4 | You will need to set these gn args: 5 | ``` 6 | use_remoteexec=true 7 | reclient_cfg_dir="../../buildtools/reclient_cfgs/linux" 8 | cc_wrapper="" 9 | ``` 10 | 11 | and set these env vars: 12 | ``` 13 | NINJA=autoninja 14 | ``` 15 | """ 16 | 17 | from v8_deps import Var, hooks 18 | 19 | import subprocess 20 | import os 21 | 22 | def run(name): 23 | hook = next(h for h in hooks if h['name'] == name) 24 | print(subprocess.run(hook['action'])) 25 | 26 | run('configure_reclient_cfgs') 27 | run('configure_siso') 28 | 29 | rbe_version = Var('reclient_version') 30 | 31 | ensure_file = f''' 32 | $ParanoidMode CheckPresence 33 | @Subdir buildtools/reclient 34 | infra/rbe/client/linux-amd64 {rbe_version} 35 | ''' 36 | print(ensure_file) 37 | with open("./cipd.ensure", "w") as f: 38 | f.write(ensure_file) 39 | print(subprocess.run(['cipd', 'ensure', '-root', '.', '-ensure-file', 'cipd.ensure'])) 40 | os.remove('./cipd.ensure') 41 | -------------------------------------------------------------------------------- /tools/update_deps.py: -------------------------------------------------------------------------------- 1 | from v8_deps import deps 2 | import subprocess 3 | 4 | def process(name, dep): 5 | if name == 'build': 6 | # We have our own fork of this 7 | return 8 | 9 | url = dep if isinstance(dep, str) else dep['url'] 10 | rev = url.split('@')[1] 11 | print(name, rev) 12 | subprocess.run(['git', 'fetch', 'origin'], cwd=name) 13 | subprocess.run(['git', 'checkout', rev], cwd=name) 14 | 15 | failed = False 16 | 17 | names = [] 18 | 19 | with open('.gitmodules') as f: 20 | for line in f.readlines(): 21 | if line.startswith('['): 22 | name = line.split(" ")[1][1:-3] 23 | if name in deps: 24 | names.append(name) 25 | try: 26 | process(name, deps[name]) 27 | except: 28 | failed = True 29 | 30 | if failed: 31 | import sys 32 | sys.exit(1) 33 | 34 | print(','.join(names)) 35 | -------------------------------------------------------------------------------- /tools/upgrade_v8.sh: -------------------------------------------------------------------------------- 1 | # WARNING: This is not an automated tool! This is just some commands to copy and 2 | # paste manually to upgrade V8. 3 | 4 | export BRANCH=20210101_rusty_v8 5 | export COMMITDATE=2020-11-12T08:56:11.000Z 6 | 7 | git submodule update -f 8 | git submodule foreach 'git remote rm upstream; true' && 9 | git -C build remote add upstream https://chromium.googlesource.com/chromium/src/build && 10 | git -C buildtools remote add upstream https://chromium.googlesource.com/chromium/src/buildtools && 11 | git submodule foreach '[ -f ./include/v8.h ] || (git remote add upstream `git remote get-url origin`; true)' && 12 | git submodule foreach '[ -f ./include/v8.h ] || git remote update' && 13 | git submodule foreach '[ -f ./include/v8.h ] || (export SHA=`git log upstream/master -n1 --until=$COMMITDATE --pretty=%H` && git merge $SHA -m "Merge commit $SHA from `git remote get-url upstream`")' 14 | 15 | git -C build push git@github.com:denoland/chromium_build HEAD:refs/heads/$BRANCH 16 | git -C buildtools push git@github.com:denoland/chromium_buildtools HEAD:refs/heads/$BRANCH 17 | 18 | git -C build push git@github.com:denoland/chromium_build upstream/master:refs/heads/upstream 19 | git -C buildtools push git@github.com:denoland/chromium_buildtools upstream/master:refs/heads/upstream 20 | -------------------------------------------------------------------------------- /tools/v8_deps.py: -------------------------------------------------------------------------------- 1 | Str = str 2 | def Var(name): 3 | if name == 'rbe_instance': 4 | return 'projects/rbe-chromium-untrusted/instances/default_instance' 5 | return vars[name] 6 | with open('./v8/DEPS') as f: 7 | exec(f.read()) 8 | 9 | --------------------------------------------------------------------------------