diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..32ac413 --- /dev/null +++ b/.gitignore @@ -0,0 +1,101 @@ +# Created by https://www.toptal.com/developers/gitignore/api/go,macos,linux,windows +# Edit at https://www.toptal.com/developers/gitignore?templates=go,macos,linux,windows + +### Go ### +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/go,macos,linux,windows \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100755 index 0000000..ab1b80b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,127 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Fixed +- Use string length to ensure null character-containing strings in Go/JS are not terminated early. +- Object.Set with an empty key string is now supported + +## [v0.7.0] - 2021-12-09 + +### Added +- Support for calling constructors functions with NewInstance on Function +- Access "this" from function callback +- value.SameValue(otherValue) function to compare values for sameness +- Undefined, Null functions to get these constant values for the isolate +- Support for calling a method on an object. +- Support for calling `IsExecutionTerminating` on isolate to check if execution is still terminating. +- Support for setting and getting internal fields for template object instances +- Support for CPU profiling +- Add V8 build for Apple Silicon +- Add support for throwing an exception directly via the isolate's ThrowException function. +- Support for compiling a context-dependent UnboundScript which can be run in any context of the isolate it was compiled in. +- Support for creating a code cache from an UnboundScript which can be used to create an UnboundScript in other isolates +to run a pre-compiled script in new contexts. +- Included compile error location in `%+v` formatting of JSError +- Enable i18n support + +### Changed +- Removed error return value from NewIsolate which never fails +- Removed error return value from NewContext which never fails +- Removed error return value from Context.Isolate() which never fails +- Removed error return value from NewObjectTemplate and NewFunctionTemplate. Panic if given a nil argument. +- Function Call accepts receiver as first argument. This **subtle breaking change** will compile old code but interpret the first argument as the receiver. Use `Undefined` to prepend an argument to fix old Call use. +- Removed Windows support until its build issues are addressed. +- Upgrade to V8 9.6.180.12 + +### Fixed +- Add some missing error propagation +- Fix crash from template finalizer releasing V8 data, let it be disposed with the isolate +- Fix crash by keeping alive the template while its C++ pointer is still being used +- Fix crash from accessing function template callbacks outside of `RunScript`, such as in `JSONStringify` + +## [v0.6.0] - 2021-05-11 + +### Added +- Promise resolver and promise result +- Convert a Value to a Function and invoke it. Thanks to [@robfig](https://github.com/robfig) +- Windows static binary. Thanks to [@cleiner](https://github.com/cleiner) +- Setting/unsetting of V8 feature flags +- Register promise callbacks in Go. Thanks to [@robfig](https://github.com/robfig) +- Get Function from a template for a given context. Thanks to [@robfig](https://github.com/robfig) + +### Changed +- Upgrade to V8 9.0.257.18 + +### Fixed +- Go GC attempting to free C memory (via finalizer) of values after an Isolate is disposed causes a panic + +## [v0.5.1] - 2021-02-19 + +### Fixed +- Memory being held by Values after the associated Context is closed + +## [v0.5.0] - 2021-02-08 + +### Added +- Support for the BigInt value to the big.Int Go type +- Create Object Templates with primitive values, including other Object Templates +- Configure Object Template as the global object of any new Context +- Function Templates with callbacks to Go +- Value to Object type, including Get/Set/Has/Delete methods +- Get Global Object from the Context +- Convert an Object Template to an instance of an Object + +### Changed +- NewContext() API has been improved to handle optional global object, as well as optional Isolate +- Package error messages are now prefixed with `v8go` rather than the struct name +- Deprecated `iso.Close()` in favor of `iso.Dispose()` to keep consistancy with the C++ API +- Upgraded V8 to 8.8.278.14 +- Licence BSD 3-Clause (same as V8 and Go) + +## [v0.4.0] - 2021-01-14 + +### Added +- Value methods for checking value kind (is string, number, array etc) +- C formatting via `clang-format` to aid future development +- Support of vendoring with `go mod vendor` +- Value methods to convert to primitive data types + +### Changed +- Use g++ (default for cgo) for linux builds of the static v8 lib + +## [v0.3.0] - 2020-12-18 + +### Added +- Support for Windows via [MSYS2](https://www.msys2.org/). Thanks to [@neptoess](https://github.com/neptoess) + +### Changed +- Upgraded V8 to 8.7.220.31 + +## [v0.2.0] - 2020-01-25 + +### Added +- Manually dispose of the isolate when required +- Monitor isolate heap statistics. Thanks to [@mehrdadrad](https://github.com/mehrdadrad) + +### Changed +- Upgrade V8 to 8.0.426.15 + +## [v0.1.0] - 2019-09-22 + +### Changed +- Upgrade V8 to 7.7.299.9 + +## [v0.0.1] - 2019-09-2020 + +### Added +- Create V8 Isolate +- Create Contexts +- Run JavaScript scripts +- Get Values back from JavaScript in Go +- Get detailed JavaScript errors in Go, including stack traces +- Terminate long running scripts from any Goroutine diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100755 index 0000000..fdb9396 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,21 @@ +# How to contribute + +**Working on your first Pull Request?** You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github) + +## Guidelines for Pull Requests + +How to get your contributions merged smoothly and quickly. + +* Create **small PRs** that are narrowly focused on **addressing a single concern**. We often times receive PRs that are trying to fix several things at a time, but only one fix is considered acceptable, nothing gets merged and both author's & review's time is wasted. Create more PRs to address different concerns and everyone will be happy. + +* For speculative changes, consider opening an issue and discussing it first. + +* Provide a good **PR description** as a record of **what** change is being made and **why** it was made. Link to a github issue if it exists. + +* Unless your PR is trivial, you should expect there will be reviewer comments that you'll need to address before merging. We expect you to be reasonably responsive to those comments, otherwise the PR will be closed after 2-3 weeks of inactivity. + +* Maintain **clean commit history** and use **meaningful commit messages**. PRs with messy commit history are difficult to review and won't be merged. Use `rebase -i upstream/main` to curate your commit history and/or to bring in latest changes from master (but avoid rebasing in the middle of a code review). + +* Keep your PR up to date with upstream/master (if there are merge conflicts, we can't really merge your change). + +* Exceptions to the rules can be made if there's a compelling reason for doing so. diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..79cf04b --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2019 Roger Chapman and the v8go contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..a109f2c --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +.DEFAULT_GOAL := build + +GO ?= go +GO_RUN_TOOLS ?= $(GO) run -modfile ./tools/go.mod +GO_TEST = $(GO_RUN_TOOLS) gotest.tools/gotestsum --format pkgname + + +.PHONY: generate +generate: + go generate ./... + +.PHONY: fmt +fmt: ## Run go fmt against code. + go run mvdan.cc/gofumpt -w . + +.PHONY: vet +vet: ## Run go vet against code. + go vet ./... + +.PHONY: test +test: fmt vet ## Run tests. + mkdir -p .test/reports + $(GO_TEST) --junitfile .test/reports/unit-test.xml -- -race ./... -count=1 -short -cover -coverprofile .test/reports/unit-test-coverage.out + +.PHONY: lint +lint: ## Run lint. + $(GO_RUN_TOOLS) github.com/golangci/golangci-lint/cmd/golangci-lint run --timeout 5m -c .golangci.yml + +.PHONY: clean +clean: ## Remove previous build. + find . -type f -name '*.gen.go' -exec rm {} + + git checkout go.mod \ No newline at end of file diff --git a/README.md b/README.md new file mode 100755 index 0000000..0c4671f --- /dev/null +++ b/README.md @@ -0,0 +1,296 @@ +# Execute JavaScript from Go + +V8 Gopher based on original artwork from the amazing Renee French + +## Usage + +```go +import v8 "github.com/nzhenev/v8go/v8go" +``` + +### Running a script + +```go +ctx := v8.NewContext() // creates a new V8 context with a new Isolate aka VM +ctx.RunScript("const add = (a, b) => a + b", "math.js") // executes a script on the global context +ctx.RunScript("const result = add(3, 4)", "main.js") // any functions previously added to the context can be called +val, _ := ctx.RunScript("result", "value.js") // return a value in JavaScript back to Go +fmt.Printf("addition result: %s", val) +``` + +### One VM, many contexts + +```go +iso := v8.NewIsolate() // creates a new JavaScript VM +ctx1 := v8.NewContext(iso) // new context within the VM +ctx1.RunScript("const multiply = (a, b) => a * b", "math.js") + +ctx2 := v8.NewContext(iso) // another context on the same VM +if _, err := ctx2.RunScript("multiply(3, 4)", "main.js"); err != nil { + // this will error as multiply is not defined in this context +} +``` + +### JavaScript function with Go callback + +```go +iso := v8.NewIsolate() // create a new VM +// a template that represents a JS function +printfn := v8.NewFunctionTemplate(iso, func(info *v8.FunctionCallbackInfo) *v8.Value { + fmt.Printf("%v", info.Args()) // when the JS function is called this Go callback will execute + return nil // you can return a value back to the JS caller if required +}) +global := v8.NewObjectTemplate(iso) // a template that represents a JS Object +global.Set("print", printfn) // sets the "print" property of the Object to our function +ctx := v8.NewContext(iso, global) // new Context with the global Object set to our object template +ctx.RunScript("print('foo')", "print.js") // will execute the Go callback with a single argunent 'foo' +``` + +### Update a JavaScript object from Go + +```go +ctx := v8.NewContext() // new context with a default VM +obj := ctx.Global() // get the global object from the context +obj.Set("version", "v1.0.0") // set the property "version" on the object +val, _ := ctx.RunScript("version", "version.js") // global object will have the property set within the JS VM +fmt.Printf("version: %s", val) + +if obj.Has("version") { // check if a property exists on the object + obj.Delete("version") // remove the property from the object +} +``` + +### JavaScript errors + +```go +val, err := ctx.RunScript(src, filename) +if err != nil { + e := err.(*v8.JSError) // JavaScript errors will be returned as the JSError struct + fmt.Println(e.Message) // the message of the exception thrown + fmt.Println(e.Location) // the filename, line number and the column where the error occured + fmt.Println(e.StackTrace) // the full stack trace of the error, if available + + fmt.Printf("javascript error: %v", e) // will format the standard error message + fmt.Printf("javascript stack trace: %+v", e) // will format the full error stack trace +} +``` + +### Pre-compile context-independent scripts to speed-up execution times + +For scripts that are large or are repeatedly run in different contexts, +it is beneficial to compile the script once and used the cached data from that +compilation to avoid recompiling every time you want to run it. + +```go +source := "const multiply = (a, b) => a * b" +iso1 := v8.NewIsolate() // creates a new JavaScript VM +ctx1 := v8.NewContext(iso1) // new context within the VM +script1, _ := iso1.CompileUnboundScript(source, "math.js", v8.CompileOptions{}) // compile script to get cached data +val, _ := script1.Run(ctx1) + +cachedData := script1.CreateCodeCache() + +iso2 := v8.NewIsolate() // create a new JavaScript VM +ctx2 := v8.NewContext(iso2) // new context within the VM + +script2, _ := iso2.CompileUnboundScript(source, "math.js", v8.CompileOptions{CachedData: cachedData}) // compile script in new isolate with cached data +val, _ = script2.Run(ctx2) +``` + +### Terminate long running scripts + +```go +vals := make(chan *v8.Value, 1) +errs := make(chan error, 1) + +go func() { + val, err := ctx.RunScript(script, "forever.js") // exec a long running script + if err != nil { + errs <- err + return + } + vals <- val +}() + +select { +case val := <- vals: + // success +case err := <- errs: + // javascript error +case <- time.After(200 * time.Milliseconds): + vm := ctx.Isolate() // get the Isolate from the context + vm.TerminateExecution() // terminate the execution + err := <- errs // will get a termination error back from the running script +} +``` + +### CPU Profiler + +```go +func createProfile() { + iso := v8.NewIsolate() + ctx := v8.NewContext(iso) + cpuProfiler := v8.NewCPUProfiler(iso) + + cpuProfiler.StartProfiling("my-profile") + + ctx.RunScript(profileScript, "script.js") # this script is defined in cpuprofiler_test.go + val, _ := ctx.Global().Get("start") + fn, _ := val.AsFunction() + fn.Call(ctx.Global()) + + cpuProfile := cpuProfiler.StopProfiling("my-profile") + + printTree("", cpuProfile.GetTopDownRoot()) # helper function to print the profile +} + +func printTree(nest string, node *v8.CPUProfileNode) { + fmt.Printf("%s%s %s:%d:%d\n", nest, node.GetFunctionName(), node.GetScriptResourceName(), node.GetLineNumber(), node.GetColumnNumber()) + count := node.GetChildrenCount() + if count == 0 { + return + } + nest = fmt.Sprintf("%s ", nest) + for i := 0; i < count; i++ { + printTree(nest, node.GetChild(i)) + } +} + +// Output +// (root) :0:0 +// (program) :0:0 +// start script.js:23:15 +// foo script.js:15:13 +// delay script.js:12:15 +// loop script.js:1:14 +// bar script.js:13:13 +// delay script.js:12:15 +// loop script.js:1:14 +// baz script.js:14:13 +// delay script.js:12:15 +// loop script.js:1:14 +// (garbage collector) :0:0 +``` + +## Documentation + +Go Reference & more examples: https://pkg.go.dev/ionos-cloud/v8go + +### Support + +If you would like to ask questions about this library or want to keep up-to-date with the latest changes and releases, +please join the [**#v8go**](https://gophers.slack.com/channels/v8go) channel on Gophers Slack. [Click here to join the Gophers Slack community!](https://invite.slack.golangbridge.org/) + +### Windows + +There used to be Windows binary support. For further information see, [PR #234](https://github.com/nzhenev/v8go/v8go/pull/234). + +The v8go library would welcome contributions from anyone able to get an external windows +build of the V8 library linking with v8go, using the version of V8 checked out in the +`deps/v8` git submodule, and documentation of the process involved. This process will likely +involve passing a linker flag when building v8go (e.g. using the `CGO_LDFLAGS` environment +variable. + +## V8 dependency + +V8 version: **9.0.257.18** (April 2021) + +In order to make `v8go` usable as a standard Go package, prebuilt static libraries of V8 +are included for Linux and macOS. you *should not* require to build V8 yourself. + +Due to security concerns of binary blobs hiding malicious code, the V8 binary is built via CI *ONLY*. + +## Project Goals + +To provide a high quality, idiomatic, Go binding to the [V8 C++ API](https://v8.github.io/api/head/index.html). + +The API should match the original API as closely as possible, but with an API that Gophers (Go enthusiasts) expect. For +example: using multiple return values to return both result and error from a function, rather than throwing an +exception. + +This project also aims to keep up-to-date with the latest (stable) release of V8. + +## License + +[![FOSSA Status](https://app.fossa.com/api/projects/custom%2B22862%2Fgit%40github.com%3Aionos-cloud%2Fv8go.git.svg?type=large)](https://app.fossa.com/projects/custom%2B22862%2Fgit%40github.com%3Aionos-cloud%2Fv8go.git?ref=badge_large) + +## Development + +### Recompile V8 with debug info and debug checks + +[Aside from data races, Go should be memory-safe](https://research.swtch.com/gorace) and v8go should preserve this property by adding the necessary checks to return an error or panic on these unsupported code paths. Release builds of v8go don't include debugging information for the V8 library since it significantly adds to the binary size, slows down compilation and shouldn't be needed by users of v8go. However, if a v8go bug causes a crash (e.g. during new feature development) then it can be helpful to build V8 with debugging information to get a C++ backtrace with line numbers. The following steps will not only do that, but also enable V8 debug checking, which can help with catching misuse of the V8 API. + +1) Make sure to clone the projects submodules (ie. the V8's `depot_tools` project): `git submodule update --init --recursive` +1) Build the V8 binary for your OS: `deps/build.py --debug`. V8 is a large project, and building the binary can take up to 30 minutes. +1) Build the executable to debug, using `go build` for commands or `go test -c` for tests. You may need to add the `-ldflags=-compressdwarf=false` option to disable debug information compression so this information can be read by the debugger (e.g. lldb that comes with Xcode v12.5.1, the latest Xcode released at the time of writing) +1) Run the executable with a debugger (e.g. `lldb -- ./v8go.test -test.run TestThatIsCrashing`, `run` to start execution then use `bt` to print a bracktrace after it breaks on a crash), since backtraces printed by Go or V8 don't currently include line number information. + +### Upgrading the V8 binaries + +We have the [upgradev8](https://github.com/nzhenev/v8go/v8go/.github/workflow/v8upgrade.yml) workflow. +The workflow is triggered every day or manually. + +If the current [v8_version](https://github.com/nzhenev/v8go/v8go/deps/v8_version) is different from the latest stable version, the workflow takes care of fetching the latest stable v8 files and copying them into `deps/include`. The last step of the workflow opens a new PR with the branch name `v8_upgrade/` with all the changes. + +The next steps are: + +1) The build is not yet triggered automatically. To trigger it manually, go to the [V8 +Build](https://github.com/nzhenev/v8go/v8go/actions?query=workflow%3A%22V8+Build%22) Github Action, Select "Run workflow", +and select your pushed branch eg. `v8_upgrade/`. +1) Once built, this should open 3 PRs against your branch to add the `libv8.a` for Linux (for x86_64) and macOS for x86_64 and arm64; merge +these PRs into your branch. You are now ready to raise the PR against `master` with the latest version of V8. + +### Flushing after C/C++ standard library printing for debugging + +When using the C/C++ standard library functions for printing (e.g. `printf`), then the output will be buffered by default. +This can cause some confusion, especially because the test binary (created through `go test`) does not flush the buffer +at exit (at the time of writing). When standard output is the terminal, then it will use line buffering and flush when +a new line is printed, otherwise (e.g. if the output is redirected to a pipe or file) it will be fully buffered and not even +flush at the end of a line. When the test binary is executed through `go test .` (e.g. instead of +separately compiled with `go test -c` and run with `./v8go.test`) Go may redirect standard output internally, resulting in +standard output being fully buffered. + +A simple way to avoid this problem is to flush the standard output stream after printing with the `fflush(stdout);` statement. +Not relying on the flushing at exit can also help ensure the output is printed before a crash. + +### Local leak checking + +Leak checking is automatically done in CI, but it can be useful to do locally to debug leaks. + +Leak checking is done using the [Leak Sanitizer](https://clang.llvm.org/docs/LeakSanitizer.html) which +is a part of LLVM. As such, compiling with clang as the C/C++ compiler seems to produce more complete +backtraces (unfortunately still only of the system stack at the time of writing). + +For instance, on a Debian-based Linux system, you can use `sudo apt-get install clang-12` to install a +recent version of clang. Then CC and CXX environment variables are needed to use that compiler. With +that compiler, the tests can be run as follows + +``` +CC=clang-12 CXX=clang++-12 go test -c --tags leakcheck && ./v8go.test +``` + +The separate compile and link commands are currently needed to get line numbers in the backtrace. + +On macOS, leak checking isn't available with the version of clang that comes with Xcode, so a separate +compiler installation is needed. For example, with homebrew, `brew install llvm` will install a version +of clang with support for this. The ASAN_OPTIONS environment variable will also be needed to run the code +with leak checking enabled, since it isn't enabled by default on macOS. E.g. with the homebrew +installation of llvm, the tests can be run with + +``` +CXX=/usr/local/opt/llvm/bin/clang++ CC=/usr/local/opt/llvm/bin/clang go test -c --tags leakcheck -ldflags=-compressdwarf=false +ASAN_OPTIONS=detect_leaks=1 ./v8go.test +``` + +The `-ldflags=-compressdwarf=false` is currently (with clang 13) needed to get line numbers in the backtrace. + +### Formatting + +Go has `go fmt`, C has `clang-format`. Any changes to the `v8go.h|cc` should be formated with `clang-format` with the +"Chromium" Coding style. This can be done easily by running the `go generate` command. + +`brew install clang-format` to install on macOS. + +--- + +V8 Gopher image based on original artwork from the amazing [Renee French](http://reneefrench.blogspot.com). diff --git a/cgo.go b/cgo.go new file mode 100755 index 0000000..0396c6e --- /dev/null +++ b/cgo.go @@ -0,0 +1,27 @@ +// Copyright 2019 Roger Chapman and the v8go contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package v8go + +//go:generate clang-format -i --verbose -style=Chromium v8go.h v8go.cc + +// #cgo CXXFLAGS: -fno-rtti -fPIC -std=c++17 -DV8_COMPRESS_POINTERS -DV8_31BIT_SMIS_ON_64BIT_ARCH -I${SRCDIR}/deps/include -Wall -DV8_ENABLE_SANDBOX +// #cgo LDFLAGS: -pthread -lv8 +// #cgo darwin,amd64 LDFLAGS: -L${SRCDIR}/deps/darwin_x86_64 +// #cgo darwin,arm64 LDFLAGS: -L${SRCDIR}/deps/darwin_arm64 +// #cgo linux,amd64 LDFLAGS: -L${SRCDIR}/deps/linux_x86_64 -ldl +// #cgo linux,arm64 LDFLAGS: -L${SRCDIR}/deps/linux_arm64 -ldl +import "C" + +// These imports forces `go mod vendor` to pull in all the folders that +// contain V8 libraries and headers which otherwise would be ignored. +// DO NOT REMOVE +// nolint:revive +import ( + _ "github.com/nzhenev/v8go/v8go/deps/darwin_arm64" + _ "github.com/nzhenev/v8go/v8go/deps/darwin_x86_64" + _ "github.com/nzhenev/v8go/v8go/deps/include" + _ "github.com/nzhenev/v8go/v8go/deps/linux_arm64" + _ "github.com/nzhenev/v8go/v8go/deps/linux_x86_64" +) diff --git a/context.go b/context.go new file mode 100755 index 0000000..33ca614 --- /dev/null +++ b/context.go @@ -0,0 +1,185 @@ +// Copyright 2019 Roger Chapman and the v8go contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package v8go + +// #include +// #include "v8go.h" +import "C" + +import ( + "runtime" + "sync" + "unsafe" +) + +// Due to the limitations of passing pointers to C from Go we need to create +// a registry so that we can lookup the Context from any given callback from V8. +// This is similar to what is described here: https://github.com/golang/go/wiki/cgo#function-variables +type ctxRef struct { + ctx *Context + refCount int +} + +var ( + ctxMutex sync.RWMutex + ctxRegistry = make(map[int]*ctxRef) + ctxSeq = 0 +) + +// Context is a global root execution environment that allows separate, +// unrelated, JavaScript applications to run in a single instance of V8. +type Context struct { + ref int + ptr C.ContextPtr + iso *Isolate +} + +type contextOptions struct { + iso *Isolate + gTmpl *ObjectTemplate +} + +// ContextOption sets options such as Isolate and Global Template to the NewContext +type ContextOption interface { + apply(*contextOptions) +} + +// NewContext creates a new JavaScript context; if no Isolate is passed as a +// ContextOption than a new Isolate will be created. +func NewContext(opt ...ContextOption) *Context { + opts := contextOptions{} + for _, o := range opt { + if o != nil { + o.apply(&opts) + } + } + + if opts.iso == nil { + opts.iso = NewIsolate() + } + + if opts.gTmpl == nil { + opts.gTmpl = &ObjectTemplate{&template{}} + } + + ctxMutex.Lock() + ctxSeq++ + ref := ctxSeq + ctxMutex.Unlock() + + ctx := &Context{ + ref: ref, + ptr: C.NewContext(opts.iso.ptr, opts.gTmpl.ptr, C.int(ref)), + iso: opts.iso, + } + ctx.register() + runtime.KeepAlive(opts.gTmpl) + return ctx +} + +// Isolate gets the current context's parent isolate. +func (c *Context) Isolate() *Isolate { + return c.iso +} + +func (c *Context) RetainedValueCount() int { + ctxMutex.Lock() + defer ctxMutex.Unlock() + return int(C.ContextRetainedValueCount(c.ptr)) +} + +// RunScript executes the source JavaScript; origin (a.k.a. filename) provides a +// reference for the script and used in the stack trace if there is an error. +// error will be of type `JSError` if not nil. +func (c *Context) RunScript(source string, origin string) (*Value, error) { + cSource := C.CString(source) + cOrigin := C.CString(origin) + defer C.free(unsafe.Pointer(cSource)) + defer C.free(unsafe.Pointer(cOrigin)) + + rtn := C.RunScript(c.ptr, cSource, cOrigin) + return valueResult(c, rtn) +} + +// Global returns the global proxy object. +// Global proxy object is a thin wrapper whose prototype points to actual +// context's global object with the properties like Object, etc. This is +// done that way for security reasons. +// Please note that changes to global proxy object prototype most probably +// would break the VM — V8 expects only global object as a prototype of +// global proxy object. +func (c *Context) Global() *Object { + valPtr := C.ContextGlobal(c.ptr) + v := &Value{valPtr, c} + return &Object{v} +} + +// PerformMicrotaskCheckpoint runs the default MicrotaskQueue until empty. +// This is used to make progress on Promises. +func (c *Context) PerformMicrotaskCheckpoint() { + C.IsolatePerformMicrotaskCheckpoint(c.iso.ptr) +} + +// Close will dispose the context and free the memory. +// Access to any values associated with the context after calling Close may panic. +func (c *Context) Close() { + c.deregister() + C.ContextFree(c.ptr) + c.ptr = nil +} + +func (c *Context) register() { + ctxMutex.Lock() + r := ctxRegistry[c.ref] + if r == nil { + r = &ctxRef{ctx: c} + ctxRegistry[c.ref] = r + } + r.refCount++ + ctxMutex.Unlock() +} + +func (c *Context) deregister() { + ctxMutex.Lock() + defer ctxMutex.Unlock() + r := ctxRegistry[c.ref] + if r == nil { + return + } + r.refCount-- + if r.refCount <= 0 { + delete(ctxRegistry, c.ref) + } +} + +func getContext(ref int) *Context { + ctxMutex.RLock() + defer ctxMutex.RUnlock() + r := ctxRegistry[ref] + if r == nil { + return nil + } + return r.ctx +} + +//export goContext +func goContext(ref int) C.ContextPtr { + ctx := getContext(ref) + return ctx.ptr +} + +func valueResult(ctx *Context, rtn C.RtnValue) (*Value, error) { + if rtn.value == nil { + return nil, newJSError(rtn.error) + } + return &Value{rtn.value, ctx}, nil +} + +func objectResult(ctx *Context, rtn C.RtnValue) (*Object, error) { + if rtn.value == nil { + return nil, newJSError(rtn.error) + } + return &Object{&Value{rtn.value, ctx}}, nil +} diff --git a/context_test.go b/context_test.go new file mode 100755 index 0000000..4659d34 --- /dev/null +++ b/context_test.go @@ -0,0 +1,210 @@ +// Copyright 2019 Roger Chapman and the v8go contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package v8go_test + +import ( + "encoding/json" + "fmt" + "testing" + + v8 "github.com/nzhenev/v8go/v8go" +) + +func TestContextExec(t *testing.T) { + t.Parallel() + ctx := v8.NewContext(nil) + defer ctx.Isolate().Dispose() + defer ctx.Close() + + ctx.RunScript(`const add = (a, b) => a + b`, "add.js") + val, _ := ctx.RunScript(`add(3, 4)`, "main.js") + rtn := val.String() + if rtn != "7" { + t.Errorf("script returned an unexpected value: expected %q, got %q", "7", rtn) + } + + _, err := ctx.RunScript(`add`, "func.js") + if err != nil { + t.Errorf("error not expected: %v", err) + } + + iso := ctx.Isolate() + ctx2 := v8.NewContext(iso) + defer ctx2.Close() + _, err = ctx2.RunScript(`add`, "ctx2.js") + if err == nil { + t.Error("error expected but was ") + } +} + +func TestJSExceptions(t *testing.T) { + t.Parallel() + + tests := [...]struct { + name string + source string + origin string + err string + }{ + {"SyntaxError", "bad js syntax", "syntax.js", "SyntaxError: Unexpected identifier 'js'"}, + {"ReferenceError", "add()", "add.js", "ReferenceError: add is not defined"}, + } + + ctx := v8.NewContext(nil) + defer ctx.Isolate().Dispose() + defer ctx.Close() + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + _, err := ctx.RunScript(tt.source, tt.origin) + if err == nil { + t.Error("error expected but got ") + return + } + if err.Error() != tt.err { + t.Errorf("expected %q, got %q", tt.err, err.Error()) + } + }) + } +} + +func TestContextRegistry(t *testing.T) { + t.Parallel() + + ctx := v8.NewContext() + defer ctx.Isolate().Dispose() + defer ctx.Close() + + ctxref := ctx.Ref() + + c1 := v8.GetContext(ctxref) + if c1 == nil { + t.Error("expected context, but got ") + } + if c1 != ctx { + t.Errorf("contexts should match %p != %p", c1, ctx) + } + + ctx.Close() + + c2 := v8.GetContext(ctxref) + if c2 != nil { + t.Error("expected context to be after close") + } +} + +func TestMemoryLeak(t *testing.T) { + t.Parallel() + + iso := v8.NewIsolate() + defer iso.Dispose() + + for i := 0; i < 6000; i++ { + ctx := v8.NewContext(iso) + _ = ctx.Global() + // _ = obj.String() + _, _ = ctx.RunScript("2", "") + ctx.Close() + } + if n := iso.GetHeapStatistics().NumberOfNativeContexts; n >= 6000 { + t.Errorf("Context not being GC'd, got %d native contexts", n) + } +} + +// https://github.com/rogchap/v8go/issues/186 +func TestRegistryFromJSON(t *testing.T) { + t.Parallel() + + iso := v8.NewIsolate() + defer iso.Dispose() + + global := v8.NewObjectTemplate(iso) + err := global.Set("location", v8.NewFunctionTemplate(iso, func(info *v8.FunctionCallbackInfo) *v8.Value { + v, err := v8.NewValue(iso, "world") + fatalIf(t, err) + return v + })) + fatalIf(t, err) + + ctx := v8.NewContext(iso, global) + defer ctx.Close() + + v, err := ctx.RunScript(` + new Proxy({ + "hello": "unknown" + }, { + get: function () { + return location() + }, + }) + `, "main.js") + fatalIf(t, err) + + s, err := v8.JSONStringify(ctx, v) + fatalIf(t, err) + + expected := `{"hello":"world"}` + if s != expected { + t.Fatalf("expected %q, got %q", expected, s) + } +} + +func BenchmarkContext(b *testing.B) { + b.ReportAllocs() + iso := v8.NewIsolate() + defer iso.Dispose() + for n := 0; n < b.N; n++ { + ctx := v8.NewContext(iso) + ctx.RunScript(script, "main.js") + str, _ := json.Marshal(makeObject()) + cmd := fmt.Sprintf("process(%s)", str) + ctx.RunScript(cmd, "cmd.js") + ctx.Close() + } +} + +func ExampleContext() { + ctx := v8.NewContext() + defer ctx.Isolate().Dispose() + defer ctx.Close() + ctx.RunScript("const add = (a, b) => a + b", "math.js") + ctx.RunScript("const result = add(3, 4)", "main.js") + val, _ := ctx.RunScript("result", "value.js") + fmt.Println(val) + // Output: + // 7 +} + +func ExampleContext_isolate() { + iso := v8.NewIsolate() + defer iso.Dispose() + ctx1 := v8.NewContext(iso) + defer ctx1.Close() + ctx1.RunScript("const foo = 'bar'", "context_one.js") + val, _ := ctx1.RunScript("foo", "foo.js") + fmt.Println(val) + + ctx2 := v8.NewContext(iso) + defer ctx2.Close() + _, err := ctx2.RunScript("foo", "context_two.js") + fmt.Println(err) + // Output: + // bar + // ReferenceError: foo is not defined +} + +func ExampleContext_globalTemplate() { + iso := v8.NewIsolate() + defer iso.Dispose() + obj := v8.NewObjectTemplate(iso) + obj.Set("version", "v1.0.0") + ctx := v8.NewContext(iso, obj) + defer ctx.Close() + val, _ := ctx.RunScript("version", "main.js") + fmt.Println(val) + // Output: + // v1.0.0 +} diff --git a/cpuprofile.go b/cpuprofile.go new file mode 100755 index 0000000..4a1593c --- /dev/null +++ b/cpuprofile.go @@ -0,0 +1,55 @@ +// Copyright 2021 the v8go contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package v8go + +/* +#include "v8go.h" +*/ +import "C" +import "time" + +type CPUProfile struct { + p *C.CPUProfile + + // The CPU profile title. + title string + + // root is the root node of the top down call tree. + root *CPUProfileNode + + // startTimeOffset is the time when the profile recording was started + // since some unspecified starting point. + startTimeOffset time.Duration + + // endTimeOffset is the time when the profile recording was stopped + // since some unspecified starting point. + // The point is equal to the starting point used by startTimeOffset. + endTimeOffset time.Duration +} + +// Returns CPU profile title. +func (c *CPUProfile) GetTitle() string { + return c.title +} + +// Returns the root node of the top down call tree. +func (c *CPUProfile) GetTopDownRoot() *CPUProfileNode { + return c.root +} + +// Returns the duration of the profile. +func (c *CPUProfile) GetDuration() time.Duration { + return c.endTimeOffset - c.startTimeOffset +} + +// Deletes the profile and removes it from CpuProfiler's list. +// All pointers to nodes previously returned become invalid. +func (c *CPUProfile) Delete() { + if c.p == nil { + return + } + C.CPUProfileDelete(c.p) + c.p = nil +} diff --git a/cpuprofile_test.go b/cpuprofile_test.go new file mode 100755 index 0000000..0646932 --- /dev/null +++ b/cpuprofile_test.go @@ -0,0 +1,70 @@ +// Copyright 2021 the v8go contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package v8go_test + +import ( + "testing" + + v8 "github.com/nzhenev/v8go/v8go" +) + +func TestCPUProfile(t *testing.T) { + t.Parallel() + + ctx := v8.NewContext(nil) + iso := ctx.Isolate() + defer iso.Dispose() + defer ctx.Close() + + cpuProfiler := v8.NewCPUProfiler(iso) + defer cpuProfiler.Dispose() + + title := "cpuprofiletest" + cpuProfiler.StartProfiling(title) + + _, err := ctx.RunScript(profileScript, "script.js") + fatalIf(t, err) + val, err := ctx.Global().Get("start") + fatalIf(t, err) + fn, err := val.AsFunction() + fatalIf(t, err) + _, err = fn.Call(ctx.Global()) + fatalIf(t, err) + + cpuProfile := cpuProfiler.StopProfiling(title) + defer cpuProfile.Delete() + + if cpuProfile.GetTitle() != title { + t.Fatalf("expected title %s, but got %s", title, cpuProfile.GetTitle()) + } + + root := cpuProfile.GetTopDownRoot() + if root == nil { + t.Fatal("expected root not to be nil") + } + if root.GetFunctionName() != "(root)" { + t.Errorf("expected (root), but got %v", root.GetFunctionName()) + } + + if cpuProfile.GetDuration() <= 0 { + t.Fatalf("expected positive profile duration (%s)", cpuProfile.GetDuration()) + } +} + +func TestCPUProfile_Delete(t *testing.T) { + t.Parallel() + + iso := v8.NewIsolate() + defer iso.Dispose() + + cpuProfiler := v8.NewCPUProfiler(iso) + defer cpuProfiler.Dispose() + + cpuProfiler.StartProfiling("cpuprofiletest") + cpuProfile := cpuProfiler.StopProfiling("cpuprofiletest") + cpuProfile.Delete() + // noop when called multiple times + cpuProfile.Delete() +} diff --git a/cpuprofilenode.go b/cpuprofilenode.go new file mode 100755 index 0000000..6ac5b2c --- /dev/null +++ b/cpuprofilenode.go @@ -0,0 +1,91 @@ +// Copyright 2021 the v8go contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package v8go + +type CPUProfileNode struct { + // The id of the current node, unique within the tree. + nodeID int + + // The id of the script where the function originates. + scriptID int + + // The resource name for script from where the function originates. + scriptResourceName string + + // The function name (empty string for anonymous functions.) + functionName string + + // The number of the line where the function originates. + lineNumber int + + // The number of the column where the function originates. + columnNumber int + + // The count of samples where the function was currently executing. + hitCount int + + // The bailout reason for the function if the optimization was disabled for it. + bailoutReason string + + // The children node of this node. + children []*CPUProfileNode + + // The parent node of this node. + parent *CPUProfileNode +} + +// Returns node id. +func (c *CPUProfileNode) GetNodeID() int { + return c.nodeID +} + +// Returns id for script from where the function originates. +func (c *CPUProfileNode) GetScriptID() int { + return c.scriptID +} + +// Returns function name (empty string for anonymous functions.) +func (c *CPUProfileNode) GetFunctionName() string { + return c.functionName +} + +// Returns resource name for script from where the function originates. +func (c *CPUProfileNode) GetScriptResourceName() string { + return c.scriptResourceName +} + +// Returns number of the line where the function originates. +func (c *CPUProfileNode) GetLineNumber() int { + return c.lineNumber +} + +// Returns number of the column where the function originates. +func (c *CPUProfileNode) GetColumnNumber() int { + return c.columnNumber +} + +// Returns count of samples where the function was currently executing. +func (c *CPUProfileNode) GetHitCount() int { + return c.hitCount +} + +// Returns the bailout reason for the function if the optimization was disabled for it. +func (c *CPUProfileNode) GetBailoutReason() string { + return c.bailoutReason +} + +// Retrieves the ancestor node, or nil if the root. +func (c *CPUProfileNode) GetParent() *CPUProfileNode { + return c.parent +} + +func (c *CPUProfileNode) GetChildrenCount() int { + return len(c.children) +} + +// Retrieves a child node by index. +func (c *CPUProfileNode) GetChild(index int) *CPUProfileNode { + return c.children[index] +} diff --git a/cpuprofilenode_test.go b/cpuprofilenode_test.go new file mode 100755 index 0000000..b9b4e89 --- /dev/null +++ b/cpuprofilenode_test.go @@ -0,0 +1,112 @@ +// Copyright 2021 the v8go contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package v8go_test + +import ( + "testing" + + v8 "github.com/nzhenev/v8go/v8go" +) + +func TestCPUProfileNode(t *testing.T) { + t.Parallel() + + ctx := v8.NewContext(nil) + iso := ctx.Isolate() + defer iso.Dispose() + defer ctx.Close() + + cpuProfiler := v8.NewCPUProfiler(iso) + defer cpuProfiler.Dispose() + + title := "cpuprofilenodetest" + cpuProfiler.StartProfiling(title) + + _, err := ctx.RunScript(profileScript, "script.js") + fatalIf(t, err) + val, err := ctx.Global().Get("start") + fatalIf(t, err) + fn, err := val.AsFunction() + fatalIf(t, err) + timeout, err := v8.NewValue(iso, int32(1000)) + fatalIf(t, err) + _, err = fn.Call(ctx.Global(), timeout) + fatalIf(t, err) + + cpuProfile := cpuProfiler.StopProfiling(title) + if cpuProfile == nil { + t.Fatal("expected profile not to be nil") + } + defer cpuProfile.Delete() + + rootNode := cpuProfile.GetTopDownRoot() + if rootNode == nil { + t.Fatal("expected top down root not to be nil") + } + count := rootNode.GetChildrenCount() + var startNode *v8.CPUProfileNode + for i := 0; i < count; i++ { + if rootNode.GetChild(i).GetFunctionName() == "start" { + startNode = rootNode.GetChild(i) + } + } + if startNode == nil { + t.Fatal("expected node not to be nil") + } + checkNode(t, startNode, "script.js", "start", 23, 15) + + parentName := startNode.GetParent().GetFunctionName() + if parentName != "(root)" { + t.Fatalf("expected (root), but got %v", parentName) + } + + fooNode := findChild(t, startNode, "foo") + checkNode(t, fooNode, "script.js", "foo", 15, 13) + + delayNode := findChild(t, fooNode, "delay") + checkNode(t, delayNode, "script.js", "delay", 12, 15) + + barNode := findChild(t, fooNode, "bar") + checkNode(t, barNode, "script.js", "bar", 13, 13) + + loopNode := findChild(t, delayNode, "loop") + checkNode(t, loopNode, "script.js", "loop", 1, 14) + + bazNode := findChild(t, fooNode, "baz") + checkNode(t, bazNode, "script.js", "baz", 14, 13) +} + +func findChild(t *testing.T, node *v8.CPUProfileNode, functionName string) *v8.CPUProfileNode { + t.Helper() + + var child *v8.CPUProfileNode + count := node.GetChildrenCount() + for i := 0; i < count; i++ { + if node.GetChild(i).GetFunctionName() == functionName { + child = node.GetChild(i) + } + } + if child == nil { + t.Fatal("failed to find child node") + } + return child +} + +func checkNode(t *testing.T, node *v8.CPUProfileNode, scriptResourceName string, functionName string, line, column int) { + t.Helper() + + if node.GetFunctionName() != functionName { + t.Fatalf("expected node to have function name %s, but got %s", functionName, node.GetFunctionName()) + } + if node.GetScriptResourceName() != scriptResourceName { + t.Fatalf("expected node to have script resource name %s, but got %s", scriptResourceName, node.GetScriptResourceName()) + } + if node.GetLineNumber() != line { + t.Fatalf("expected node at line %d, but got %d", line, node.GetLineNumber()) + } + if node.GetColumnNumber() != column { + t.Fatalf("expected node at column %d, but got %d", column, node.GetColumnNumber()) + } +} diff --git a/cpuprofiler.go b/cpuprofiler.go new file mode 100755 index 0000000..bbe7a17 --- /dev/null +++ b/cpuprofiler.go @@ -0,0 +1,98 @@ +// Copyright 2021 the v8go contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package v8go + +/* +#include +#include "v8go.h" +*/ +import "C" + +import ( + "time" + "unsafe" +) + +type CPUProfiler struct { + p *C.CPUProfiler + iso *Isolate +} + +// CPUProfiler is used to control CPU profiling. +func NewCPUProfiler(iso *Isolate) *CPUProfiler { + profiler := C.NewCPUProfiler(iso.ptr) + return &CPUProfiler{ + p: profiler, + iso: iso, + } +} + +// Dispose will dispose the profiler. +func (c *CPUProfiler) Dispose() { + if c.p == nil { + return + } + + C.CPUProfilerDispose(c.p) + c.p = nil +} + +// StartProfiling starts collecting a CPU profile. Title may be an empty string. Several +// profiles may be collected at once. Attempts to start collecting several +// profiles with the same title are silently ignored. +func (c *CPUProfiler) StartProfiling(title string) { + if c.p == nil || c.iso.ptr == nil { + panic("profiler or isolate are nil") + } + + tstr := C.CString(title) + defer C.free(unsafe.Pointer(tstr)) + + C.CPUProfilerStartProfiling(c.p, tstr) +} + +// Stops collecting CPU profile with a given title and returns it. +// If the title given is empty, finishes the last profile started. +func (c *CPUProfiler) StopProfiling(title string) *CPUProfile { + if c.p == nil || c.iso.ptr == nil { + panic("profiler or isolate are nil") + } + + tstr := C.CString(title) + defer C.free(unsafe.Pointer(tstr)) + + profile := C.CPUProfilerStopProfiling(c.p, tstr) + + return &CPUProfile{ + p: profile, + title: C.GoString(profile.title), + root: newCPUProfileNode(profile.root, nil), + startTimeOffset: time.Duration(profile.startTime) * time.Millisecond, + endTimeOffset: time.Duration(profile.endTime) * time.Millisecond, + } +} + +func newCPUProfileNode(node *C.CPUProfileNode, parent *CPUProfileNode) *CPUProfileNode { + n := &CPUProfileNode{ + nodeID: int(node.nodeId), + scriptID: int(node.scriptId), + scriptResourceName: C.GoString(node.scriptResourceName), + functionName: C.GoString(node.functionName), + lineNumber: int(node.lineNumber), + columnNumber: int(node.columnNumber), + hitCount: int(node.hitCount), + bailoutReason: C.GoString(node.bailoutReason), + parent: parent, + } + + if node.childrenCount > 0 { + n.children = make([]*CPUProfileNode, node.childrenCount) + for i, child := range (*[1 << 28]*C.CPUProfileNode)(unsafe.Pointer(node.children))[:node.childrenCount:node.childrenCount] { + n.children[i] = newCPUProfileNode(child, n) + } + } + + return n +} diff --git a/cpuprofiler_test.go b/cpuprofiler_test.go new file mode 100755 index 0000000..6b5eebb --- /dev/null +++ b/cpuprofiler_test.go @@ -0,0 +1,109 @@ +// Copyright 2021 the v8go contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package v8go_test + +import ( + "testing" + + v8 "github.com/nzhenev/v8go/v8go" +) + +func TestCPUProfiler_Dispose(t *testing.T) { + t.Parallel() + + iso := v8.NewIsolate() + defer iso.Dispose() + cpuProfiler := v8.NewCPUProfiler(iso) + + cpuProfiler.Dispose() + // noop when called multiple times + cpuProfiler.Dispose() + + // verify panics when profiler disposed + if recoverPanic(func() { cpuProfiler.StartProfiling("") }) == nil { + t.Error("expected panic") + } + + if recoverPanic(func() { cpuProfiler.StopProfiling("") }) == nil { + t.Error("expected panic") + } + + cpuProfiler = v8.NewCPUProfiler(iso) + defer cpuProfiler.Dispose() + iso.Dispose() + + // verify panics when isolate disposed + if recoverPanic(func() { cpuProfiler.StartProfiling("") }) == nil { + t.Error("expected panic") + } + + if recoverPanic(func() { cpuProfiler.StopProfiling("") }) == nil { + t.Error("expected panic") + } +} + +func TestCPUProfiler(t *testing.T) { + t.Parallel() + + ctx := v8.NewContext(nil) + iso := ctx.Isolate() + defer iso.Dispose() + defer ctx.Close() + + cpuProfiler := v8.NewCPUProfiler(iso) + defer cpuProfiler.Dispose() + + title := "cpuprofilertest" + cpuProfiler.StartProfiling(title) + + _, err := ctx.RunScript(profileScript, "script.js") + fatalIf(t, err) + val, err := ctx.Global().Get("start") + fatalIf(t, err) + fn, err := val.AsFunction() + fatalIf(t, err) + timeout, err := v8.NewValue(iso, int32(0)) + fatalIf(t, err) + _, err = fn.Call(ctx.Global(), timeout) + fatalIf(t, err) + + cpuProfile := cpuProfiler.StopProfiling(title) + defer cpuProfile.Delete() + + if cpuProfile.GetTitle() != title { + t.Errorf("expected %s, but got %s", title, cpuProfile.GetTitle()) + } +} + +const profileScript = `function loop(timeout) { + this.mmm = 0; + var start = Date.now(); + while (Date.now() - start < timeout) { + var n = 10; + while(n > 1) { + n--; + this.mmm += n * n * n; + } + } +} +function delay() { try { loop(10); } catch(e) { } } +function bar() { delay(); } +function baz() { delay(); } +function foo() { + try { + delay(); + bar(); + delay(); + baz(); + } catch (e) { } +} +function start(timeout) { + var start = Date.now(); + do { + foo(); + var duration = Date.now() - start; + } while (duration < timeout); + return duration; +};` diff --git a/deps/.gclient b/deps/.gclient new file mode 100755 index 0000000..a8725fe --- /dev/null +++ b/deps/.gclient @@ -0,0 +1,9 @@ +solutions = [ + { + "name": "v8", + "url": "https://chromium.googlesource.com/v8/v8.git", + "deps_file": "DEPS", + "managed": False, + "custom_deps": {}, + }, +] diff --git a/deps/build.py b/deps/build.py new file mode 100755 index 0000000..805585b --- /dev/null +++ b/deps/build.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python +import platform +import os +import subprocess +import shutil +import argparse + +valid_archs = ['arm64', 'x86_64'] +# "x86_64" is called "amd64" on Windows +current_arch = platform.uname()[4].lower().replace("amd64", "x86_64") +default_arch = current_arch if current_arch in valid_archs else None + +parser = argparse.ArgumentParser() +parser.add_argument('--debug', dest='debug', action='store_true') +parser.add_argument('--no-clang', dest='clang', action='store_false') +parser.add_argument('--arch', + dest='arch', + action='store', + choices=valid_archs, + default=default_arch, + required=default_arch is None) +parser.set_defaults(debug=False, clang=True) +args = parser.parse_args() + +deps_path = os.path.dirname(os.path.realpath(__file__)) +v8_path = os.path.join(deps_path, "v8") +tools_path = os.path.join(deps_path, "depot_tools") +is_windows = platform.system().lower() == "windows" + +gclient_sln = [ + { "name" : "v8", + "url" : "https://chromium.googlesource.com/v8/v8.git", + "deps_file" : "DEPS", + "managed" : False, + "custom_deps" : { + # These deps are unnecessary for building. + "v8/testing/gmock" : None, + "v8/test/wasm-js" : None, + "v8/third_party/android_tools" : None, + "v8/third_party/catapult" : None, + "v8/third_party/colorama/src" : None, + "v8/tools/gyp" : None, + "v8/tools/luci-go" : None, + }, + "custom_vars": { + "build_for_node" : True, + }, + }, +] + +gn_args = """ +is_debug=%s +is_clang=%s +target_cpu="%s" +v8_target_cpu="%s" +clang_use_chrome_plugins=false +use_custom_libcxx=false +use_sysroot=false +symbol_level=%s +strip_debug_info=%s +is_component_build=false +v8_monolithic=true +v8_use_external_startup_data=false +treat_warnings_as_errors=false +v8_embedder_string="-v8go" +v8_enable_gdbjit=false +v8_enable_i18n_support=true +icu_use_data_file=false +v8_enable_test_features=false +exclude_unwind_tables=true +""" + +def v8deps(): + spec = "solutions = %s" % gclient_sln + env = os.environ.copy() + env["PATH"] = tools_path + os.pathsep + env["PATH"] + subprocess.check_call(cmd(["gclient", "sync", "--spec", spec]), + cwd=deps_path, + env=env) + +def cmd(args): + return ["cmd", "/c"] + args if is_windows else args + +def os_arch(): + u = platform.uname() + return u[0].lower() + "_" + args.arch + +def v8_arch(): + if args.arch == "x86_64": + return "x64" + return args.arch + +def apply_mingw_patches(): + v8_build_path = os.path.join(v8_path, "build") + apply_patch("0000-add-mingw-main-code-changes", v8_path) + apply_patch("0001-add-mingw-toolchain", v8_build_path) + update_last_change() + zlib_path = os.path.join(v8_path, "third_party", "zlib") + zlib_src_gn = os.path.join(deps_path, os_arch(), "zlib.gn") + zlib_dst_gn = os.path.join(zlib_path, "BUILD.gn") + shutil.copy(zlib_src_gn, zlib_dst_gn) + +def apply_patch(patch_name, working_dir): + patch_path = os.path.join(deps_path, os_arch(), patch_name + ".patch") + subprocess.check_call(["git", "apply", "-v", patch_path], cwd=working_dir) + +def update_last_change(): + out_path = os.path.join(v8_path, "build", "util", "LASTCHANGE") + subprocess.check_call(["python", "build/util/lastchange.py", "-o", out_path], cwd=v8_path) + +def main(): + v8deps() + if is_windows: + apply_mingw_patches() + + gn_path = os.path.join(tools_path, "gn") + assert(os.path.exists(gn_path)) + ninja_path = os.path.join(tools_path, "ninja" + (".exe" if is_windows else "")) + assert(os.path.exists(ninja_path)) + + build_path = os.path.join(deps_path, ".build", os_arch()) + env = os.environ.copy() + + is_debug = 'true' if args.debug else 'false' + is_clang = 'true' if args.clang else 'false' + # symbol_level = 1 includes line number information + # symbol_level = 2 can be used for additional debug information, but it can increase the + # compiled library by an order of magnitude and further slow down compilation + symbol_level = 1 if args.debug else 0 + strip_debug_info = 'false' if args.debug else 'true' + + arch = v8_arch() + gnargs = gn_args % (is_debug, is_clang, arch, arch, symbol_level, strip_debug_info) + gen_args = gnargs.replace('\n', ' ') + + subprocess.check_call(cmd([gn_path, "gen", build_path, "--args=" + gen_args]), + cwd=v8_path, + env=env) + subprocess.check_call([ninja_path, "-v", "-C", build_path, "v8_monolith"], + cwd=v8_path, + env=env) + + lib_fn = os.path.join(build_path, "obj/libv8_monolith.a") + dest_path = os.path.join(deps_path, os_arch()) + if not os.path.exists(dest_path): + os.makedirs(dest_path) + dest_fn = os.path.join(dest_path, 'libv8.a') + shutil.copy(lib_fn, dest_fn) + + +if __name__ == "__main__": + main() diff --git a/deps/darwin_arm64/libv8.a b/deps/darwin_arm64/libv8.a new file mode 100755 index 0000000..2b76165 Binary files /dev/null and b/deps/darwin_arm64/libv8.a differ diff --git a/deps/darwin_arm64/vendor.go b/deps/darwin_arm64/vendor.go new file mode 100755 index 0000000..0d899f5 --- /dev/null +++ b/deps/darwin_arm64/vendor.go @@ -0,0 +1,3 @@ +// Package darwin_arm64 is required to provide support for vendoring modules +// DO NOT REMOVE +package darwin_arm64 diff --git a/deps/darwin_x86_64/libv8.a b/deps/darwin_x86_64/libv8.a new file mode 100755 index 0000000..3a75d22 Binary files /dev/null and b/deps/darwin_x86_64/libv8.a differ diff --git a/deps/darwin_x86_64/vendor.go b/deps/darwin_x86_64/vendor.go new file mode 100755 index 0000000..203e5f1 --- /dev/null +++ b/deps/darwin_x86_64/vendor.go @@ -0,0 +1,3 @@ +// Package darwin_x86_64 is required to provide support for vendoring modules +// DO NOT REMOVE +package darwin_x86_64 diff --git a/deps/include/APIDesign.md b/deps/include/APIDesign.md new file mode 100755 index 0000000..fe42c8e --- /dev/null +++ b/deps/include/APIDesign.md @@ -0,0 +1,72 @@ +# The V8 public C++ API + +# Overview + +The V8 public C++ API aims to support four use cases: + +1. Enable applications that embed V8 (called the embedder) to configure and run + one or more instances of V8. +2. Expose ECMAScript-like capabilities to the embedder. +3. Enable the embedder to interact with ECMAScript by exposing API objects. +4. Provide access to the V8 debugger (inspector). + +# Configuring and running an instance of V8 + +V8 requires access to certain OS-level primitives such as the ability to +schedule work on threads, or allocate memory. + +The embedder can define how to access those primitives via the v8::Platform +interface. While V8 bundles a basic implementation, embedders are highly +encouraged to implement v8::Platform themselves. + +Currently, the v8::ArrayBuffer::Allocator is passed to the v8::Isolate factory +method, however, conceptually it should also be part of the v8::Platform since +all instances of V8 should share one allocator. + +Once the v8::Platform is configured, an v8::Isolate can be created. All +further interactions with V8 should explicitly reference the v8::Isolate they +refer to. All API methods should eventually take an v8::Isolate parameter. + +When a given instance of V8 is no longer needed, it can be destroyed by +disposing the respective v8::Isolate. If the embedder wishes to free all memory +associated with the v8::Isolate, it has to first clear all global handles +associated with that v8::Isolate. + +# ECMAScript-like capabilities + +In general, the C++ API shouldn't enable capabilities that aren't available to +scripts running in V8. Experience has shown that it's not possible to maintain +such API methods in the long term. However, capabilities also available to +scripts, i.e., ones that are defined in the ECMAScript standard are there to +stay, and we can safely expose them to embedders. + +The C++ API should also be pleasant to use, and not require learning new +paradigms. Similarly to how the API exposed to scripts aims to provide good +ergonomics, we should aim to provide a reasonable developer experience for this +API surface. + +ECMAScript makes heavy use of exceptions, however, V8's C++ code doesn't use +C++ exceptions. Therefore, all API methods that can throw exceptions should +indicate so by returning a v8::Maybe<> or v8::MaybeLocal<> result, +and by taking a v8::Local<v8::Context> parameter that indicates in which +context a possible exception should be thrown. + +# API objects + +V8 allows embedders to define special objects that expose additional +capabilities and APIs to scripts. The most prominent example is exposing the +HTML DOM in Blink. Other examples are e.g. node.js. It is less clear what kind +of capabilities we want to expose via this API surface. As a rule of thumb, we +want to expose operations as defined in the WebIDL and HTML spec: we +assume that those requirements are somewhat stable, and that they are a +superset of the requirements of other embedders including node.js. + +Ideally, the API surfaces defined in those specs hook into the ECMAScript spec +which in turn guarantees long-term stability of the API. + +# The V8 inspector + +All debugging capabilities of V8 should be exposed via the inspector protocol. +The exception to this are profiling features exposed via v8-profiler.h. +Changes to the inspector protocol need to ensure backwards compatibility and +commitment to maintain. diff --git a/deps/include/DEPS b/deps/include/DEPS new file mode 100755 index 0000000..21ce3d9 --- /dev/null +++ b/deps/include/DEPS @@ -0,0 +1,10 @@ +include_rules = [ + # v8-inspector-protocol.h depends on generated files under include/inspector. + "+inspector", + "+cppgc/common.h", + # Used by v8-cppgc.h to bridge to cppgc. + "+cppgc/custom-space.h", + "+cppgc/heap-statistics.h", + "+cppgc/internal/write-barrier.h", + "+cppgc/visitor.h", +] diff --git a/deps/include/DIR_METADATA b/deps/include/DIR_METADATA new file mode 100755 index 0000000..a27ea1b --- /dev/null +++ b/deps/include/DIR_METADATA @@ -0,0 +1,11 @@ +# Metadata information for this directory. +# +# For more information on DIR_METADATA files, see: +# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md +# +# For the schema of this file, see Metadata message: +# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto + +monorail { + component: "Blink>JavaScript>API" +} \ No newline at end of file diff --git a/deps/include/OWNERS b/deps/include/OWNERS new file mode 100755 index 0000000..535040c --- /dev/null +++ b/deps/include/OWNERS @@ -0,0 +1,23 @@ +adamk@chromium.org +cbruni@chromium.org +leszeks@chromium.org +mlippautz@chromium.org +verwaest@chromium.org +yangguo@chromium.org + +per-file *DEPS=file:../COMMON_OWNERS +per-file v8-internal.h=file:../COMMON_OWNERS + +per-file v8-debug.h=file:../src/debug/OWNERS + +per-file js_protocol.pdl=file:../src/inspector/OWNERS +per-file v8-inspector*=file:../src/inspector/OWNERS +per-file v8-inspector*=file:../src/inspector/OWNERS + +# Needed by the auto_tag builder +per-file v8-version.h=v8-ci-autoroll-builder@chops-service-accounts.iam.gserviceaccount.com + +# For branch updates: +per-file v8-version.h=file:../INFRA_OWNERS +per-file v8-version.h=hablich@chromium.org +per-file v8-version.h=vahl@chromium.org diff --git a/deps/include/cppgc/DEPS b/deps/include/cppgc/DEPS new file mode 100755 index 0000000..861d118 --- /dev/null +++ b/deps/include/cppgc/DEPS @@ -0,0 +1,8 @@ +include_rules = [ + "-include", + "+v8config.h", + "+v8-platform.h", + "+cppgc", + "-src", + "+libplatform/libplatform.h", +] diff --git a/deps/include/cppgc/OWNERS b/deps/include/cppgc/OWNERS new file mode 100755 index 0000000..6ccabf6 --- /dev/null +++ b/deps/include/cppgc/OWNERS @@ -0,0 +1,2 @@ +bikineev@chromium.org +omerkatz@chromium.org \ No newline at end of file diff --git a/deps/include/cppgc/README.md b/deps/include/cppgc/README.md new file mode 100755 index 0000000..d825ea5 --- /dev/null +++ b/deps/include/cppgc/README.md @@ -0,0 +1,135 @@ +# Oilpan: C++ Garbage Collection + +Oilpan is an open-source garbage collection library for C++ that can be used stand-alone or in collaboration with V8's JavaScript garbage collector. +Oilpan implements mark-and-sweep garbage collection (GC) with limited compaction (for a subset of objects). + +**Key properties** + +- Trace-based garbage collection; +- Incremental and concurrent marking; +- Incremental and concurrent sweeping; +- Precise on-heap memory layout; +- Conservative on-stack memory layout; +- Allows for collection with and without considering stack; +- Non-incremental and non-concurrent compaction for selected spaces; + +See the [Hello World](https://chromium.googlesource.com/v8/v8/+/main/samples/cppgc/hello-world.cc) example on how to get started using Oilpan to manage C++ code. + +Oilpan follows V8's project organization, see e.g. on how we accept [contributions](https://v8.dev/docs/contribute) and [provide a stable API](https://v8.dev/docs/api). + +## Threading model + +Oilpan features thread-local garbage collection and assumes heaps are not shared among threads. +In other words, objects are accessed and ultimately reclaimed by the garbage collector on the same thread that allocates them. +This allows Oilpan to run garbage collection in parallel with mutators running in other threads. + +References to objects belonging to another thread's heap are modeled using cross-thread roots. +This is even true for on-heap to on-heap references. + +Oilpan heaps may generally not be accessed from different threads unless otherwise noted. + +## Heap partitioning + +Oilpan's heaps are partitioned into spaces. +The space for an object is chosen depending on a number of criteria, e.g.: + +- Objects over 64KiB are allocated in a large object space +- Objects can be assigned to a dedicated custom space. + Custom spaces can also be marked as compactable. +- Other objects are allocated in one of the normal page spaces bucketed depending on their size. + +## Precise and conservative garbage collection + +Oilpan supports two kinds of GCs: + +1. **Conservative GC.** +A GC is called conservative when it is executed while the regular native stack is not empty. +In this case, the native stack might contain references to objects in Oilpan's heap, which should be kept alive. +The GC scans the native stack and treats the pointers discovered via the native stack as part of the root set. +This kind of GC is considered imprecise because values on stack other than references may accidentally appear as references to on-heap object, which means these objects will be kept alive despite being in practice unreachable from the application as an actual reference. + +2. **Precise GC.** +A precise GC is triggered at the end of an event loop, which is controlled by an embedder via a platform. +At this point, it is guaranteed that there are no on-stack references pointing to Oilpan's heap. +This means there is no risk of confusing other value types with references. +Oilpan has precise knowledge of on-heap object layouts, and so it knows exactly where pointers lie in memory. +Oilpan can just start marking from the regular root set and collect all garbage precisely. + +## Atomic, incremental and concurrent garbage collection + +Oilpan has three modes of operation: + +1. **Atomic GC.** +The entire GC cycle, including all its phases (e.g. see [Marking](#Marking-phase) and [Sweeping](#Sweeping-phase)), are executed back to back in a single pause. +This mode of operation is also known as Stop-The-World (STW) garbage collection. +It results in the most jank (due to a single long pause), but is overall the most efficient (e.g. no need for write barriers). + +2. **Incremental GC.** +Garbage collection work is split up into multiple steps which are interleaved with the mutator, i.e. user code chunked into tasks. +Each step is a small chunk of work that is executed either as dedicated tasks between mutator tasks or, as needed, during mutator tasks. +Using incremental GC introduces the need for write barriers that record changes to the object graph so that a consistent state is observed and no objects are accidentally considered dead and reclaimed. +The incremental steps are followed by a smaller atomic pause to finalize garbage collection. +The smaller pause times, due to smaller chunks of work, helps with reducing jank. + +3. **Concurrent GC.** +This is the most common type of GC. +It builds on top of incremental GC and offloads much of the garbage collection work away from the mutator thread and on to background threads. +Using concurrent GC allows the mutator thread to spend less time on GC and more on the actual mutator. + +## Marking phase + +The marking phase consists of the following steps: + +1. Mark all objects in the root set. + +2. Mark all objects transitively reachable from the root set by calling `Trace()` methods defined on each object. + +3. Clear out all weak handles to unreachable objects and run weak callbacks. + +The marking phase can be executed atomically in a stop-the-world manner, in which all 3 steps are executed one after the other. + +Alternatively, it can also be executed incrementally/concurrently. +With incremental/concurrent marking, step 1 is executed in a short pause after which the mutator regains control. +Step 2 is repeatedly executed in an interleaved manner with the mutator. +When the GC is ready to finalize, i.e. step 2 is (almost) finished, another short pause is triggered in which step 2 is finished and step 3 is performed. + +To prevent a user-after-free (UAF) issues it is required for Oilpan to know about all edges in the object graph. +This means that all pointers except on-stack pointers must be wrapped with Oilpan's handles (i.e., Persistent<>, Member<>, WeakMember<>). +Raw pointers to on-heap objects create an edge that Oilpan cannot observe and cause UAF issues +Thus, raw pointers shall not be used to reference on-heap objects (except for raw pointers on native stacks). + +## Sweeping phase + +The sweeping phase consists of the following steps: + +1. Invoke pre-finalizers. +At this point, no destructors have been invoked and no memory has been reclaimed. +Pre-finalizers are allowed to access any other on-heap objects, even those that may get destructed. + +2. Sweeping invokes destructors of the dead (unreachable) objects and reclaims memory to be reused by future allocations. + +Assumptions should not be made about the order and the timing of their execution. +There is no guarantee on the order in which the destructors are invoked. +That's why destructors must not access any other on-heap objects (which might have already been destructed). +If some destructor unavoidably needs to access other on-heap objects, it will have to be converted to a pre-finalizer. +The pre-finalizer is allowed to access other on-heap objects. + +The mutator is resumed before all destructors have ran. +For example, imagine a case where X is a client of Y, and Y holds a list of clients. +If the code relies on X's destructor removing X from the list, there is a risk that Y iterates the list and calls some method of X which may touch other on-heap objects. +This causes a use-after-free. +Care must be taken to make sure that X is explicitly removed from the list before the mutator resumes its execution in a way that doesn't rely on X's destructor (e.g. a pre-finalizer). + +Similar to marking, sweeping can be executed in either an atomic stop-the-world manner or incrementally/concurrently. +With incremental/concurrent sweeping, step 2 is interleaved with mutator. +Incremental/concurrent sweeping can be atomically finalized in case it is needed to trigger another GC cycle. +Even with concurrent sweeping, destructors are guaranteed to run on the thread the object has been allocated on to preserve C++ semantics. + +Notes: + +* Weak processing runs only when the holder object of the WeakMember outlives the pointed object. +If the holder object and the pointed object die at the same time, weak processing doesn't run. +It is wrong to write code assuming that the weak processing always runs. + +* Pre-finalizers are heavy because the thread needs to scan all pre-finalizers at each sweeping phase to determine which pre-finalizers should be invoked (the thread needs to invoke pre-finalizers of dead objects). +Adding pre-finalizers to frequently created objects should be avoided. diff --git a/deps/include/cppgc/allocation.h b/deps/include/cppgc/allocation.h new file mode 100755 index 0000000..69883fb --- /dev/null +++ b/deps/include/cppgc/allocation.h @@ -0,0 +1,310 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_ALLOCATION_H_ +#define INCLUDE_CPPGC_ALLOCATION_H_ + +#include +#include +#include +#include +#include +#include + +#include "cppgc/custom-space.h" +#include "cppgc/internal/api-constants.h" +#include "cppgc/internal/gc-info.h" +#include "cppgc/type-traits.h" +#include "v8config.h" // NOLINT(build/include_directory) + +#if defined(__has_attribute) +#if __has_attribute(assume_aligned) +#define CPPGC_DEFAULT_ALIGNED \ + __attribute__((assume_aligned(api_constants::kDefaultAlignment))) +#define CPPGC_DOUBLE_WORD_ALIGNED \ + __attribute__((assume_aligned(2 * api_constants::kDefaultAlignment))) +#endif // __has_attribute(assume_aligned) +#endif // defined(__has_attribute) + +#if !defined(CPPGC_DEFAULT_ALIGNED) +#define CPPGC_DEFAULT_ALIGNED +#endif + +#if !defined(CPPGC_DOUBLE_WORD_ALIGNED) +#define CPPGC_DOUBLE_WORD_ALIGNED +#endif + +namespace cppgc { + +/** + * AllocationHandle is used to allocate garbage-collected objects. + */ +class AllocationHandle; + +namespace internal { + +// Similar to C++17 std::align_val_t; +enum class AlignVal : size_t {}; + +class V8_EXPORT MakeGarbageCollectedTraitInternal { + protected: + static inline void MarkObjectAsFullyConstructed(const void* payload) { + // See api_constants for an explanation of the constants. + std::atomic* atomic_mutable_bitfield = + reinterpret_cast*>( + const_cast(reinterpret_cast( + reinterpret_cast(payload) - + api_constants::kFullyConstructedBitFieldOffsetFromPayload))); + // It's safe to split use load+store here (instead of a read-modify-write + // operation), since it's guaranteed that this 16-bit bitfield is only + // modified by a single thread. This is cheaper in terms of code bloat (on + // ARM) and performance. + uint16_t value = atomic_mutable_bitfield->load(std::memory_order_relaxed); + value |= api_constants::kFullyConstructedBitMask; + atomic_mutable_bitfield->store(value, std::memory_order_release); + } + + // Dispatch based on compile-time information. + // + // Default implementation is for a custom space with >`kDefaultAlignment` byte + // alignment. + template + struct AllocationDispatcher final { + static void* Invoke(AllocationHandle& handle, size_t size) { + static_assert(std::is_base_of::value, + "Custom space must inherit from CustomSpaceBase."); + static_assert( + !CustomSpace::kSupportsCompaction, + "Custom spaces that support compaction do not support allocating " + "objects with non-default (i.e. word-sized) alignment."); + return MakeGarbageCollectedTraitInternal::Allocate( + handle, size, static_cast(alignment), + internal::GCInfoTrait::Index(), CustomSpace::kSpaceIndex); + } + }; + + // Fast path for regular allocations for the default space with + // `kDefaultAlignment` byte alignment. + template + struct AllocationDispatcher + final { + static void* Invoke(AllocationHandle& handle, size_t size) { + return MakeGarbageCollectedTraitInternal::Allocate( + handle, size, internal::GCInfoTrait::Index()); + } + }; + + // Default space with >`kDefaultAlignment` byte alignment. + template + struct AllocationDispatcher final { + static void* Invoke(AllocationHandle& handle, size_t size) { + return MakeGarbageCollectedTraitInternal::Allocate( + handle, size, static_cast(alignment), + internal::GCInfoTrait::Index()); + } + }; + + // Custom space with `kDefaultAlignment` byte alignment. + template + struct AllocationDispatcher + final { + static void* Invoke(AllocationHandle& handle, size_t size) { + static_assert(std::is_base_of::value, + "Custom space must inherit from CustomSpaceBase."); + return MakeGarbageCollectedTraitInternal::Allocate( + handle, size, internal::GCInfoTrait::Index(), + CustomSpace::kSpaceIndex); + } + }; + + private: + static void* CPPGC_DEFAULT_ALIGNED Allocate(cppgc::AllocationHandle&, size_t, + GCInfoIndex); + static void* CPPGC_DOUBLE_WORD_ALIGNED Allocate(cppgc::AllocationHandle&, + size_t, AlignVal, + GCInfoIndex); + static void* CPPGC_DEFAULT_ALIGNED Allocate(cppgc::AllocationHandle&, size_t, + GCInfoIndex, CustomSpaceIndex); + static void* CPPGC_DOUBLE_WORD_ALIGNED Allocate(cppgc::AllocationHandle&, + size_t, AlignVal, GCInfoIndex, + CustomSpaceIndex); + + friend class HeapObjectHeader; +}; + +} // namespace internal + +/** + * Base trait that provides utilities for advancers users that have custom + * allocation needs (e.g., overriding size). It's expected that users override + * MakeGarbageCollectedTrait (see below) and inherit from + * MakeGarbageCollectedTraitBase and make use of the low-level primitives + * offered to allocate and construct an object. + */ +template +class MakeGarbageCollectedTraitBase + : private internal::MakeGarbageCollectedTraitInternal { + private: + static_assert(internal::IsGarbageCollectedType::value, + "T needs to be a garbage collected object"); + static_assert(!IsGarbageCollectedWithMixinTypeV || + sizeof(T) <= + internal::api_constants::kLargeObjectSizeThreshold, + "GarbageCollectedMixin may not be a large object"); + + protected: + /** + * Allocates memory for an object of type T. + * + * \param handle AllocationHandle identifying the heap to allocate the object + * on. + * \param size The size that should be reserved for the object. + * \returns the memory to construct an object of type T on. + */ + V8_INLINE static void* Allocate(AllocationHandle& handle, size_t size) { + static_assert( + std::is_base_of::value, + "U of GarbageCollected must be a base of T. Check " + "GarbageCollected base class inheritance."); + static constexpr size_t kWantedAlignment = + alignof(T) < internal::api_constants::kDefaultAlignment + ? internal::api_constants::kDefaultAlignment + : alignof(T); + static_assert( + kWantedAlignment <= internal::api_constants::kMaxSupportedAlignment, + "Requested alignment larger than alignof(std::max_align_t) bytes. " + "Please file a bug to possibly get this restriction lifted."); + return AllocationDispatcher< + typename internal::GCInfoFolding< + T, typename T::ParentMostGarbageCollectedType>::ResultType, + typename SpaceTrait::Space, kWantedAlignment>::Invoke(handle, size); + } + + /** + * Marks an object as fully constructed, resulting in precise handling by the + * garbage collector. + * + * \param payload The base pointer the object is allocated at. + */ + V8_INLINE static void MarkObjectAsFullyConstructed(const void* payload) { + internal::MakeGarbageCollectedTraitInternal::MarkObjectAsFullyConstructed( + payload); + } +}; + +/** + * Passed to MakeGarbageCollected to specify how many bytes should be appended + * to the allocated object. + * + * Example: + * \code + * class InlinedArray final : public GarbageCollected { + * public: + * explicit InlinedArray(size_t bytes) : size(bytes), byte_array(this + 1) {} + * void Trace(Visitor*) const {} + + * size_t size; + * char* byte_array; + * }; + * + * auto* inlined_array = MakeGarbageCollectedbyte_array[i]); + * } + * \endcode + */ +struct AdditionalBytes { + constexpr explicit AdditionalBytes(size_t bytes) : value(bytes) {} + const size_t value; +}; + +/** + * Default trait class that specifies how to construct an object of type T. + * Advanced users may override how an object is constructed using the utilities + * that are provided through MakeGarbageCollectedTraitBase. + * + * Any trait overriding construction must + * - allocate through `MakeGarbageCollectedTraitBase::Allocate`; + * - mark the object as fully constructed using + * `MakeGarbageCollectedTraitBase::MarkObjectAsFullyConstructed`; + */ +template +class MakeGarbageCollectedTrait : public MakeGarbageCollectedTraitBase { + public: + template + static T* Call(AllocationHandle& handle, Args&&... args) { + void* memory = + MakeGarbageCollectedTraitBase::Allocate(handle, sizeof(T)); + T* object = ::new (memory) T(std::forward(args)...); + MakeGarbageCollectedTraitBase::MarkObjectAsFullyConstructed(object); + return object; + } + + template + static T* Call(AllocationHandle& handle, AdditionalBytes additional_bytes, + Args&&... args) { + void* memory = MakeGarbageCollectedTraitBase::Allocate( + handle, sizeof(T) + additional_bytes.value); + T* object = ::new (memory) T(std::forward(args)...); + MakeGarbageCollectedTraitBase::MarkObjectAsFullyConstructed(object); + return object; + } +}; + +/** + * Allows users to specify a post-construction callback for specific types. The + * callback is invoked on the instance of type T right after it has been + * constructed. This can be useful when the callback requires a + * fully-constructed object to be able to dispatch to virtual methods. + */ +template +struct PostConstructionCallbackTrait { + static void Call(T*) {} +}; + +/** + * Constructs a managed object of type T where T transitively inherits from + * GarbageCollected. + * + * \param args List of arguments with which an instance of T will be + * constructed. + * \returns an instance of type T. + */ +template +V8_INLINE T* MakeGarbageCollected(AllocationHandle& handle, Args&&... args) { + T* object = + MakeGarbageCollectedTrait::Call(handle, std::forward(args)...); + PostConstructionCallbackTrait::Call(object); + return object; +} + +/** + * Constructs a managed object of type T where T transitively inherits from + * GarbageCollected. Created objects will have additional bytes appended to + * it. Allocated memory would suffice for `sizeof(T) + additional_bytes`. + * + * \param additional_bytes Denotes how many bytes to append to T. + * \param args List of arguments with which an instance of T will be + * constructed. + * \returns an instance of type T. + */ +template +V8_INLINE T* MakeGarbageCollected(AllocationHandle& handle, + AdditionalBytes additional_bytes, + Args&&... args) { + T* object = MakeGarbageCollectedTrait::Call(handle, additional_bytes, + std::forward(args)...); + PostConstructionCallbackTrait::Call(object); + return object; +} + +} // namespace cppgc + +#undef CPPGC_DEFAULT_ALIGNED +#undef CPPGC_DOUBLE_WORD_ALIGNED + +#endif // INCLUDE_CPPGC_ALLOCATION_H_ diff --git a/deps/include/cppgc/common.h b/deps/include/cppgc/common.h new file mode 100755 index 0000000..9610383 --- /dev/null +++ b/deps/include/cppgc/common.h @@ -0,0 +1,28 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_COMMON_H_ +#define INCLUDE_CPPGC_COMMON_H_ + +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +/** + * Indicator for the stack state of the embedder. + */ +enum class EmbedderStackState { + /** + * Stack may contain interesting heap pointers. + */ + kMayContainHeapPointers, + /** + * Stack does not contain any interesting heap pointers. + */ + kNoHeapPointers, +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_COMMON_H_ diff --git a/deps/include/cppgc/cross-thread-persistent.h b/deps/include/cppgc/cross-thread-persistent.h new file mode 100755 index 0000000..1fa28af --- /dev/null +++ b/deps/include/cppgc/cross-thread-persistent.h @@ -0,0 +1,464 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_ +#define INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_ + +#include + +#include "cppgc/internal/persistent-node.h" +#include "cppgc/internal/pointer-policies.h" +#include "cppgc/persistent.h" +#include "cppgc/visitor.h" + +namespace cppgc { +namespace internal { + +// Wrapper around PersistentBase that allows accessing poisoned memory when +// using ASAN. This is needed as the GC of the heap that owns the value +// of a CTP, may clear it (heap termination, weakness) while the object +// holding the CTP may be poisoned as itself may be deemed dead. +class CrossThreadPersistentBase : public PersistentBase { + public: + CrossThreadPersistentBase() = default; + explicit CrossThreadPersistentBase(const void* raw) : PersistentBase(raw) {} + + V8_CLANG_NO_SANITIZE("address") const void* GetValueFromGC() const { + return raw_; + } + + V8_CLANG_NO_SANITIZE("address") + PersistentNode* GetNodeFromGC() const { return node_; } + + V8_CLANG_NO_SANITIZE("address") + void ClearFromGC() const { + raw_ = nullptr; + SetNodeSafe(nullptr); + } + + // GetNodeSafe() can be used for a thread-safe IsValid() check in a + // double-checked locking pattern. See ~BasicCrossThreadPersistent. + PersistentNode* GetNodeSafe() const { + return reinterpret_cast*>(&node_)->load( + std::memory_order_acquire); + } + + // The GC writes using SetNodeSafe() while holding the lock. + V8_CLANG_NO_SANITIZE("address") + void SetNodeSafe(PersistentNode* value) const { +#if defined(__has_feature) +#if __has_feature(address_sanitizer) +#define V8_IS_ASAN 1 +#endif +#endif + +#ifdef V8_IS_ASAN + __atomic_store(&node_, &value, __ATOMIC_RELEASE); +#else // !V8_IS_ASAN + // Non-ASAN builds can use atomics. This also covers MSVC which does not + // have the __atomic_store intrinsic. + reinterpret_cast*>(&node_)->store( + value, std::memory_order_release); +#endif // !V8_IS_ASAN + +#undef V8_IS_ASAN + } +}; + +template +class BasicCrossThreadPersistent final : public CrossThreadPersistentBase, + public LocationPolicy, + private WeaknessPolicy, + private CheckingPolicy { + public: + using typename WeaknessPolicy::IsStrongPersistent; + using PointeeType = T; + + ~BasicCrossThreadPersistent() { + // This implements fast path for destroying empty/sentinel. + // + // Simplified version of `AssignUnsafe()` to allow calling without a + // complete type `T`. Uses double-checked locking with a simple thread-safe + // check for a valid handle based on a node. + if (GetNodeSafe()) { + PersistentRegionLock guard; + const void* old_value = GetValue(); + // The fast path check (GetNodeSafe()) does not acquire the lock. Recheck + // validity while holding the lock to ensure the reference has not been + // cleared. + if (IsValid(old_value)) { + CrossThreadPersistentRegion& region = + this->GetPersistentRegion(old_value); + region.FreeNode(GetNode()); + SetNode(nullptr); + } else { + CPPGC_DCHECK(!GetNode()); + } + } + // No need to call SetValue() as the handle is not used anymore. This can + // leave behind stale sentinel values but will always destroy the underlying + // node. + } + + BasicCrossThreadPersistent( + const SourceLocation& loc = SourceLocation::Current()) + : LocationPolicy(loc) {} + + BasicCrossThreadPersistent( + std::nullptr_t, const SourceLocation& loc = SourceLocation::Current()) + : LocationPolicy(loc) {} + + BasicCrossThreadPersistent( + SentinelPointer s, const SourceLocation& loc = SourceLocation::Current()) + : CrossThreadPersistentBase(s), LocationPolicy(loc) {} + + BasicCrossThreadPersistent( + T* raw, const SourceLocation& loc = SourceLocation::Current()) + : CrossThreadPersistentBase(raw), LocationPolicy(loc) { + if (!IsValid(raw)) return; + PersistentRegionLock guard; + CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw); + SetNode(region.AllocateNode(this, &TraceAsRoot)); + this->CheckPointer(raw); + } + + class UnsafeCtorTag { + private: + UnsafeCtorTag() = default; + template + friend class BasicCrossThreadPersistent; + }; + + BasicCrossThreadPersistent( + UnsafeCtorTag, T* raw, + const SourceLocation& loc = SourceLocation::Current()) + : CrossThreadPersistentBase(raw), LocationPolicy(loc) { + if (!IsValid(raw)) return; + CrossThreadPersistentRegion& region = this->GetPersistentRegion(raw); + SetNode(region.AllocateNode(this, &TraceAsRoot)); + this->CheckPointer(raw); + } + + BasicCrossThreadPersistent( + T& raw, const SourceLocation& loc = SourceLocation::Current()) + : BasicCrossThreadPersistent(&raw, loc) {} + + template ::value>> + BasicCrossThreadPersistent( + internal::BasicMember + member, + const SourceLocation& loc = SourceLocation::Current()) + : BasicCrossThreadPersistent(member.Get(), loc) {} + + BasicCrossThreadPersistent( + const BasicCrossThreadPersistent& other, + const SourceLocation& loc = SourceLocation::Current()) + : BasicCrossThreadPersistent(loc) { + // Invoke operator=. + *this = other; + } + + // Heterogeneous ctor. + template ::value>> + BasicCrossThreadPersistent( + const BasicCrossThreadPersistent& other, + const SourceLocation& loc = SourceLocation::Current()) + : BasicCrossThreadPersistent(loc) { + *this = other; + } + + BasicCrossThreadPersistent( + BasicCrossThreadPersistent&& other, + const SourceLocation& loc = SourceLocation::Current()) noexcept { + // Invoke operator=. + *this = std::move(other); + } + + BasicCrossThreadPersistent& operator=( + const BasicCrossThreadPersistent& other) { + PersistentRegionLock guard; + AssignSafe(guard, other.Get()); + return *this; + } + + template ::value>> + BasicCrossThreadPersistent& operator=( + const BasicCrossThreadPersistent& other) { + PersistentRegionLock guard; + AssignSafe(guard, other.Get()); + return *this; + } + + BasicCrossThreadPersistent& operator=(BasicCrossThreadPersistent&& other) { + if (this == &other) return *this; + Clear(); + PersistentRegionLock guard; + PersistentBase::operator=(std::move(other)); + LocationPolicy::operator=(std::move(other)); + if (!IsValid(GetValue())) return *this; + GetNode()->UpdateOwner(this); + other.SetValue(nullptr); + other.SetNode(nullptr); + this->CheckPointer(Get()); + return *this; + } + + /** + * Assigns a raw pointer. + * + * Note: **Not thread-safe.** + */ + BasicCrossThreadPersistent& operator=(T* other) { + AssignUnsafe(other); + return *this; + } + + // Assignment from member. + template ::value>> + BasicCrossThreadPersistent& operator=( + internal::BasicMember + member) { + return operator=(member.Get()); + } + + /** + * Assigns a nullptr. + * + * \returns the handle. + */ + BasicCrossThreadPersistent& operator=(std::nullptr_t) { + Clear(); + return *this; + } + + /** + * Assigns the sentinel pointer. + * + * \returns the handle. + */ + BasicCrossThreadPersistent& operator=(SentinelPointer s) { + PersistentRegionLock guard; + AssignSafe(guard, s); + return *this; + } + + /** + * Returns a pointer to the stored object. + * + * Note: **Not thread-safe.** + * + * \returns a pointer to the stored object. + */ + // CFI cast exemption to allow passing SentinelPointer through T* and support + // heterogeneous assignments between different Member and Persistent handles + // based on their actual types. + V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const { + return static_cast(const_cast(GetValue())); + } + + /** + * Clears the stored object. + */ + void Clear() { + PersistentRegionLock guard; + AssignSafe(guard, nullptr); + } + + /** + * Returns a pointer to the stored object and releases it. + * + * Note: **Not thread-safe.** + * + * \returns a pointer to the stored object. + */ + T* Release() { + T* result = Get(); + Clear(); + return result; + } + + /** + * Conversio to boolean. + * + * Note: **Not thread-safe.** + * + * \returns true if an actual object has been stored and false otherwise. + */ + explicit operator bool() const { return Get(); } + + /** + * Conversion to object of type T. + * + * Note: **Not thread-safe.** + * + * \returns the object. + */ + operator T*() const { return Get(); } + + /** + * Dereferences the stored object. + * + * Note: **Not thread-safe.** + */ + T* operator->() const { return Get(); } + T& operator*() const { return *Get(); } + + template + BasicCrossThreadPersistent + To() const { + using OtherBasicCrossThreadPersistent = + BasicCrossThreadPersistent; + PersistentRegionLock guard; + return OtherBasicCrossThreadPersistent( + typename OtherBasicCrossThreadPersistent::UnsafeCtorTag(), + static_cast(Get())); + } + + template ::IsStrongPersistent::value>::type> + BasicCrossThreadPersistent + Lock() const { + return BasicCrossThreadPersistent< + U, internal::StrongCrossThreadPersistentPolicy>(*this); + } + + private: + static bool IsValid(const void* ptr) { + return ptr && ptr != kSentinelPointer; + } + + static void TraceAsRoot(RootVisitor& root_visitor, const void* ptr) { + root_visitor.Trace(*static_cast(ptr)); + } + + void AssignUnsafe(T* ptr) { + const void* old_value = GetValue(); + if (IsValid(old_value)) { + PersistentRegionLock guard; + old_value = GetValue(); + // The fast path check (IsValid()) does not acquire the lock. Reload + // the value to ensure the reference has not been cleared. + if (IsValid(old_value)) { + CrossThreadPersistentRegion& region = + this->GetPersistentRegion(old_value); + if (IsValid(ptr) && (®ion == &this->GetPersistentRegion(ptr))) { + SetValue(ptr); + this->CheckPointer(ptr); + return; + } + region.FreeNode(GetNode()); + SetNode(nullptr); + } else { + CPPGC_DCHECK(!GetNode()); + } + } + SetValue(ptr); + if (!IsValid(ptr)) return; + PersistentRegionLock guard; + SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot)); + this->CheckPointer(ptr); + } + + void AssignSafe(PersistentRegionLock&, T* ptr) { + PersistentRegionLock::AssertLocked(); + const void* old_value = GetValue(); + if (IsValid(old_value)) { + CrossThreadPersistentRegion& region = + this->GetPersistentRegion(old_value); + if (IsValid(ptr) && (®ion == &this->GetPersistentRegion(ptr))) { + SetValue(ptr); + this->CheckPointer(ptr); + return; + } + region.FreeNode(GetNode()); + SetNode(nullptr); + } + SetValue(ptr); + if (!IsValid(ptr)) return; + SetNode(this->GetPersistentRegion(ptr).AllocateNode(this, &TraceAsRoot)); + this->CheckPointer(ptr); + } + + void ClearFromGC() const { + if (IsValid(GetValueFromGC())) { + WeaknessPolicy::GetPersistentRegion(GetValueFromGC()) + .FreeNode(GetNodeFromGC()); + CrossThreadPersistentBase::ClearFromGC(); + } + } + + // See Get() for details. + V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") + T* GetFromGC() const { + return static_cast(const_cast(GetValueFromGC())); + } + + friend class internal::RootVisitor; +}; + +template +struct IsWeak< + BasicCrossThreadPersistent> + : std::true_type {}; + +} // namespace internal + +namespace subtle { + +/** + * **DO NOT USE: Has known caveats, see below.** + * + * CrossThreadPersistent allows retaining objects from threads other than the + * thread the owning heap is operating on. + * + * Known caveats: + * - Does not protect the heap owning an object from terminating. + * - Reaching transitively through the graph is unsupported as objects may be + * moved concurrently on the thread owning the object. + */ +template +using CrossThreadPersistent = internal::BasicCrossThreadPersistent< + T, internal::StrongCrossThreadPersistentPolicy>; + +/** + * **DO NOT USE: Has known caveats, see below.** + * + * CrossThreadPersistent allows weakly retaining objects from threads other than + * the thread the owning heap is operating on. + * + * Known caveats: + * - Does not protect the heap owning an object from terminating. + * - Reaching transitively through the graph is unsupported as objects may be + * moved concurrently on the thread owning the object. + */ +template +using WeakCrossThreadPersistent = internal::BasicCrossThreadPersistent< + T, internal::WeakCrossThreadPersistentPolicy>; + +} // namespace subtle +} // namespace cppgc + +#endif // INCLUDE_CPPGC_CROSS_THREAD_PERSISTENT_H_ diff --git a/deps/include/cppgc/custom-space.h b/deps/include/cppgc/custom-space.h new file mode 100755 index 0000000..757c4fd --- /dev/null +++ b/deps/include/cppgc/custom-space.h @@ -0,0 +1,97 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_CUSTOM_SPACE_H_ +#define INCLUDE_CPPGC_CUSTOM_SPACE_H_ + +#include + +namespace cppgc { + +/** + * Index identifying a custom space. + */ +struct CustomSpaceIndex { + constexpr CustomSpaceIndex(size_t value) : value(value) {} // NOLINT + size_t value; +}; + +/** + * Top-level base class for custom spaces. Users must inherit from CustomSpace + * below. + */ +class CustomSpaceBase { + public: + virtual ~CustomSpaceBase() = default; + virtual CustomSpaceIndex GetCustomSpaceIndex() const = 0; + virtual bool IsCompactable() const = 0; +}; + +/** + * Base class custom spaces should directly inherit from. The class inheriting + * from `CustomSpace` must define `kSpaceIndex` as unique space index. These + * indices need for form a sequence starting at 0. + * + * Example: + * \code + * class CustomSpace1 : public CustomSpace { + * public: + * static constexpr CustomSpaceIndex kSpaceIndex = 0; + * }; + * class CustomSpace2 : public CustomSpace { + * public: + * static constexpr CustomSpaceIndex kSpaceIndex = 1; + * }; + * \endcode + */ +template +class CustomSpace : public CustomSpaceBase { + public: + /** + * Compaction is only supported on spaces that manually manage slots + * recording. + */ + static constexpr bool kSupportsCompaction = false; + + CustomSpaceIndex GetCustomSpaceIndex() const final { + return ConcreteCustomSpace::kSpaceIndex; + } + bool IsCompactable() const final { + return ConcreteCustomSpace::kSupportsCompaction; + } +}; + +/** + * User-overridable trait that allows pinning types to custom spaces. + */ +template +struct SpaceTrait { + using Space = void; +}; + +namespace internal { + +template +struct IsAllocatedOnCompactableSpaceImpl { + static constexpr bool value = CustomSpace::kSupportsCompaction; +}; + +template <> +struct IsAllocatedOnCompactableSpaceImpl { + // Non-custom spaces are by default not compactable. + static constexpr bool value = false; +}; + +template +struct IsAllocatedOnCompactableSpace { + public: + static constexpr bool value = + IsAllocatedOnCompactableSpaceImpl::Space>::value; +}; + +} // namespace internal + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_CUSTOM_SPACE_H_ diff --git a/deps/include/cppgc/default-platform.h b/deps/include/cppgc/default-platform.h new file mode 100755 index 0000000..a27871c --- /dev/null +++ b/deps/include/cppgc/default-platform.h @@ -0,0 +1,67 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_DEFAULT_PLATFORM_H_ +#define INCLUDE_CPPGC_DEFAULT_PLATFORM_H_ + +#include + +#include "cppgc/platform.h" +#include "libplatform/libplatform.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +/** + * Platform provided by cppgc. Uses V8's DefaultPlatform provided by + * libplatform internally. Exception: `GetForegroundTaskRunner()`, see below. + */ +class V8_EXPORT DefaultPlatform : public Platform { + public: + using IdleTaskSupport = v8::platform::IdleTaskSupport; + explicit DefaultPlatform( + int thread_pool_size = 0, + IdleTaskSupport idle_task_support = IdleTaskSupport::kDisabled, + std::unique_ptr tracing_controller = {}) + : v8_platform_(v8::platform::NewDefaultPlatform( + thread_pool_size, idle_task_support, + v8::platform::InProcessStackDumping::kDisabled, + std::move(tracing_controller))) {} + + cppgc::PageAllocator* GetPageAllocator() override { + return v8_platform_->GetPageAllocator(); + } + + double MonotonicallyIncreasingTime() override { + return v8_platform_->MonotonicallyIncreasingTime(); + } + + std::shared_ptr GetForegroundTaskRunner() override { + // V8's default platform creates a new task runner when passed the + // `v8::Isolate` pointer the first time. For non-default platforms this will + // require getting the appropriate task runner. + return v8_platform_->GetForegroundTaskRunner(kNoIsolate); + } + + std::unique_ptr PostJob( + cppgc::TaskPriority priority, + std::unique_ptr job_task) override { + return v8_platform_->PostJob(priority, std::move(job_task)); + } + + TracingController* GetTracingController() override { + return v8_platform_->GetTracingController(); + } + + v8::Platform* GetV8Platform() const { return v8_platform_.get(); } + + protected: + static constexpr v8::Isolate* kNoIsolate = nullptr; + + std::unique_ptr v8_platform_; +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_DEFAULT_PLATFORM_H_ diff --git a/deps/include/cppgc/ephemeron-pair.h b/deps/include/cppgc/ephemeron-pair.h new file mode 100755 index 0000000..e16cf1f --- /dev/null +++ b/deps/include/cppgc/ephemeron-pair.h @@ -0,0 +1,30 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_EPHEMERON_PAIR_H_ +#define INCLUDE_CPPGC_EPHEMERON_PAIR_H_ + +#include "cppgc/liveness-broker.h" +#include "cppgc/member.h" + +namespace cppgc { + +/** + * An ephemeron pair is used to conditionally retain an object. + * The `value` will be kept alive only if the `key` is alive. + */ +template +struct EphemeronPair { + EphemeronPair(K* k, V* v) : key(k), value(v) {} + WeakMember key; + Member value; + + void ClearValueIfKeyIsDead(const LivenessBroker& broker) { + if (!broker.IsHeapObjectAlive(key)) value = nullptr; + } +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_EPHEMERON_PAIR_H_ diff --git a/deps/include/cppgc/explicit-management.h b/deps/include/cppgc/explicit-management.h new file mode 100755 index 0000000..0290328 --- /dev/null +++ b/deps/include/cppgc/explicit-management.h @@ -0,0 +1,100 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_EXPLICIT_MANAGEMENT_H_ +#define INCLUDE_CPPGC_EXPLICIT_MANAGEMENT_H_ + +#include + +#include "cppgc/allocation.h" +#include "cppgc/internal/logging.h" +#include "cppgc/type-traits.h" + +namespace cppgc { + +class HeapHandle; + +namespace subtle { + +template +void FreeUnreferencedObject(HeapHandle& heap_handle, T& object); +template +bool Resize(T& object, AdditionalBytes additional_bytes); + +} // namespace subtle + +namespace internal { + +class ExplicitManagementImpl final { + private: + V8_EXPORT static void FreeUnreferencedObject(HeapHandle&, void*); + V8_EXPORT static bool Resize(void*, size_t); + + template + friend void subtle::FreeUnreferencedObject(HeapHandle&, T&); + template + friend bool subtle::Resize(T&, AdditionalBytes); +}; +} // namespace internal + +namespace subtle { + +/** + * Informs the garbage collector that `object` can be immediately reclaimed. The + * destructor may not be invoked immediately but only on next garbage + * collection. + * + * It is up to the embedder to guarantee that no other object holds a reference + * to `object` after calling `FreeUnreferencedObject()`. In case such a + * reference exists, it's use results in a use-after-free. + * + * To aid in using the API, `FreeUnreferencedObject()` may be called from + * destructors on objects that would be reclaimed in the same garbage collection + * cycle. + * + * \param heap_handle The corresponding heap. + * \param object Reference to an object that is of type `GarbageCollected` and + * should be immediately reclaimed. + */ +template +void FreeUnreferencedObject(HeapHandle& heap_handle, T& object) { + static_assert(IsGarbageCollectedTypeV, + "Object must be of type GarbageCollected."); + internal::ExplicitManagementImpl::FreeUnreferencedObject(heap_handle, + &object); +} + +/** + * Tries to resize `object` of type `T` with additional bytes on top of + * sizeof(T). Resizing is only useful with trailing inlined storage, see e.g. + * `MakeGarbageCollected(AllocationHandle&, AdditionalBytes)`. + * + * `Resize()` performs growing or shrinking as needed and may skip the operation + * for internal reasons, see return value. + * + * It is up to the embedder to guarantee that in case of shrinking a larger + * object down, the reclaimed area is not used anymore. Any subsequent use + * results in a use-after-free. + * + * The `object` must be live when calling `Resize()`. + * + * \param object Reference to an object that is of type `GarbageCollected` and + * should be resized. + * \param additional_bytes Bytes in addition to sizeof(T) that the object should + * provide. + * \returns true when the operation was successful and the result can be relied + * on, and false otherwise. + */ +template +bool Resize(T& object, AdditionalBytes additional_bytes) { + static_assert(IsGarbageCollectedTypeV, + "Object must be of type GarbageCollected."); + return internal::ExplicitManagementImpl::Resize( + &object, sizeof(T) + additional_bytes.value); +} + +} // namespace subtle +} // namespace cppgc + +#endif // INCLUDE_CPPGC_EXPLICIT_MANAGEMENT_H_ diff --git a/deps/include/cppgc/garbage-collected.h b/deps/include/cppgc/garbage-collected.h new file mode 100755 index 0000000..6737c8b --- /dev/null +++ b/deps/include/cppgc/garbage-collected.h @@ -0,0 +1,106 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_GARBAGE_COLLECTED_H_ +#define INCLUDE_CPPGC_GARBAGE_COLLECTED_H_ + +#include "cppgc/internal/api-constants.h" +#include "cppgc/platform.h" +#include "cppgc/trace-trait.h" +#include "cppgc/type-traits.h" + +namespace cppgc { + +class Visitor; + +/** + * Base class for managed objects. Only descendent types of `GarbageCollected` + * can be constructed using `MakeGarbageCollected()`. Must be inherited from as + * left-most base class. + * + * Types inheriting from GarbageCollected must provide a method of + * signature `void Trace(cppgc::Visitor*) const` that dispatchs all managed + * pointers to the visitor and delegates to garbage-collected base classes. + * The method must be virtual if the type is not directly a child of + * GarbageCollected and marked as final. + * + * \code + * // Example using final class. + * class FinalType final : public GarbageCollected { + * public: + * void Trace(cppgc::Visitor* visitor) const { + * // Dispatch using visitor->Trace(...); + * } + * }; + * + * // Example using non-final base class. + * class NonFinalBase : public GarbageCollected { + * public: + * virtual void Trace(cppgc::Visitor*) const {} + * }; + * + * class FinalChild final : public NonFinalBase { + * public: + * void Trace(cppgc::Visitor* visitor) const final { + * // Dispatch using visitor->Trace(...); + * NonFinalBase::Trace(visitor); + * } + * }; + * \endcode + */ +template +class GarbageCollected { + public: + using IsGarbageCollectedTypeMarker = void; + using ParentMostGarbageCollectedType = T; + + // Must use MakeGarbageCollected. + void* operator new(size_t) = delete; + void* operator new[](size_t) = delete; + // The garbage collector is taking care of reclaiming the object. Also, + // virtual destructor requires an unambiguous, accessible 'operator delete'. + void operator delete(void*) { +#ifdef V8_ENABLE_CHECKS + internal::Fatal( + "Manually deleting a garbage collected object is not allowed"); +#endif // V8_ENABLE_CHECKS + } + void operator delete[](void*) = delete; + + protected: + GarbageCollected() = default; +}; + +/** + * Base class for managed mixin objects. Such objects cannot be constructed + * directly but must be mixed into the inheritance hierarchy of a + * GarbageCollected object. + * + * Types inheriting from GarbageCollectedMixin must override a virtual method + * of signature `void Trace(cppgc::Visitor*) const` that dispatchs all managed + * pointers to the visitor and delegates to base classes. + * + * \code + * class Mixin : public GarbageCollectedMixin { + * public: + * void Trace(cppgc::Visitor* visitor) const override { + * // Dispatch using visitor->Trace(...); + * } + * }; + * \endcode + */ +class GarbageCollectedMixin { + public: + using IsGarbageCollectedMixinTypeMarker = void; + + /** + * This Trace method must be overriden by objects inheriting from + * GarbageCollectedMixin. + */ + virtual void Trace(cppgc::Visitor*) const {} +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_GARBAGE_COLLECTED_H_ diff --git a/deps/include/cppgc/heap-consistency.h b/deps/include/cppgc/heap-consistency.h new file mode 100755 index 0000000..35c59ed --- /dev/null +++ b/deps/include/cppgc/heap-consistency.h @@ -0,0 +1,309 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_HEAP_CONSISTENCY_H_ +#define INCLUDE_CPPGC_HEAP_CONSISTENCY_H_ + +#include + +#include "cppgc/internal/write-barrier.h" +#include "cppgc/macros.h" +#include "cppgc/member.h" +#include "cppgc/trace-trait.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +class HeapHandle; + +namespace subtle { + +/** + * **DO NOT USE: Use the appropriate managed types.** + * + * Consistency helpers that aid in maintaining a consistent internal state of + * the garbage collector. + */ +class HeapConsistency final { + public: + using WriteBarrierParams = internal::WriteBarrier::Params; + using WriteBarrierType = internal::WriteBarrier::Type; + + /** + * Gets the required write barrier type for a specific write. + * + * \param slot Slot containing the pointer to the object. The slot itself + * must reside in an object that has been allocated using + * `MakeGarbageCollected()`. + * \param value The pointer to the object. May be an interior pointer to an + * interface of the actual object. + * \param params Parameters that may be used for actual write barrier calls. + * Only filled if return value indicates that a write barrier is needed. The + * contents of the `params` are an implementation detail. + * \returns whether a write barrier is needed and which barrier to invoke. + */ + static V8_INLINE WriteBarrierType GetWriteBarrierType( + const void* slot, const void* value, WriteBarrierParams& params) { + return internal::WriteBarrier::GetWriteBarrierType(slot, value, params); + } + + /** + * Gets the required write barrier type for a specific write. This override is + * only used for all the BasicMember types. + * + * \param slot Slot containing the pointer to the object. The slot itself + * must reside in an object that has been allocated using + * `MakeGarbageCollected()`. + * \param value The pointer to the object held via `BasicMember`. + * \param params Parameters that may be used for actual write barrier calls. + * Only filled if return value indicates that a write barrier is needed. The + * contents of the `params` are an implementation detail. + * \returns whether a write barrier is needed and which barrier to invoke. + */ + template + static V8_INLINE WriteBarrierType GetWriteBarrierType( + const internal::BasicMember& value, + WriteBarrierParams& params) { + return internal::WriteBarrier::GetWriteBarrierType( + value.GetRawSlot(), value.GetRawStorage(), params); + } + + /** + * Gets the required write barrier type for a specific write. + * + * \param slot Slot to some part of an object. The object must not necessarily + have been allocated using `MakeGarbageCollected()` but can also live + off-heap or on stack. + * \param params Parameters that may be used for actual write barrier calls. + * Only filled if return value indicates that a write barrier is needed. The + * contents of the `params` are an implementation detail. + * \param callback Callback returning the corresponding heap handle. The + * callback is only invoked if the heap cannot otherwise be figured out. The + * callback must not allocate. + * \returns whether a write barrier is needed and which barrier to invoke. + */ + template + static V8_INLINE WriteBarrierType + GetWriteBarrierType(const void* slot, WriteBarrierParams& params, + HeapHandleCallback callback) { + return internal::WriteBarrier::GetWriteBarrierType(slot, params, callback); + } + + /** + * Gets the required write barrier type for a specific write. + * This version is meant to be used in conjunction with with a marking write + * barrier barrier which doesn't consider the slot. + * + * \param value The pointer to the object. May be an interior pointer to an + * interface of the actual object. + * \param params Parameters that may be used for actual write barrier calls. + * Only filled if return value indicates that a write barrier is needed. The + * contents of the `params` are an implementation detail. + * \returns whether a write barrier is needed and which barrier to invoke. + */ + static V8_INLINE WriteBarrierType + GetWriteBarrierType(const void* value, WriteBarrierParams& params) { + return internal::WriteBarrier::GetWriteBarrierType(value, params); + } + + /** + * Conservative Dijkstra-style write barrier that processes an object if it + * has not yet been processed. + * + * \param params The parameters retrieved from `GetWriteBarrierType()`. + * \param object The pointer to the object. May be an interior pointer to a + * an interface of the actual object. + */ + static V8_INLINE void DijkstraWriteBarrier(const WriteBarrierParams& params, + const void* object) { + internal::WriteBarrier::DijkstraMarkingBarrier(params, object); + } + + /** + * Conservative Dijkstra-style write barrier that processes a range of + * elements if they have not yet been processed. + * + * \param params The parameters retrieved from `GetWriteBarrierType()`. + * \param first_element Pointer to the first element that should be processed. + * The slot itself must reside in an object that has been allocated using + * `MakeGarbageCollected()`. + * \param element_size Size of the element in bytes. + * \param number_of_elements Number of elements that should be processed, + * starting with `first_element`. + * \param trace_callback The trace callback that should be invoked for each + * element if necessary. + */ + static V8_INLINE void DijkstraWriteBarrierRange( + const WriteBarrierParams& params, const void* first_element, + size_t element_size, size_t number_of_elements, + TraceCallback trace_callback) { + internal::WriteBarrier::DijkstraMarkingBarrierRange( + params, first_element, element_size, number_of_elements, + trace_callback); + } + + /** + * Steele-style write barrier that re-processes an object if it has already + * been processed. + * + * \param params The parameters retrieved from `GetWriteBarrierType()`. + * \param object The pointer to the object which must point to an object that + * has been allocated using `MakeGarbageCollected()`. Interior pointers are + * not supported. + */ + static V8_INLINE void SteeleWriteBarrier(const WriteBarrierParams& params, + const void* object) { + internal::WriteBarrier::SteeleMarkingBarrier(params, object); + } + + /** + * Generational barrier for maintaining consistency when running with multiple + * generations. + * + * \param params The parameters retrieved from `GetWriteBarrierType()`. + * \param slot Slot containing the pointer to the object. The slot itself + * must reside in an object that has been allocated using + * `MakeGarbageCollected()`. + */ + static V8_INLINE void GenerationalBarrier(const WriteBarrierParams& params, + const void* slot) { + internal::WriteBarrier::GenerationalBarrier< + internal::WriteBarrier::GenerationalBarrierType::kPreciseSlot>(params, + slot); + } + + /** + * Generational barrier for maintaining consistency when running with multiple + * generations. This version is used when slot contains uncompressed pointer. + * + * \param params The parameters retrieved from `GetWriteBarrierType()`. + * \param slot Uncompressed slot containing the direct pointer to the object. + * The slot itself must reside in an object that has been allocated using + * `MakeGarbageCollected()`. + */ + static V8_INLINE void GenerationalBarrierForUncompressedSlot( + const WriteBarrierParams& params, const void* uncompressed_slot) { + internal::WriteBarrier::GenerationalBarrier< + internal::WriteBarrier::GenerationalBarrierType:: + kPreciseUncompressedSlot>(params, uncompressed_slot); + } + + /** + * Generational barrier for source object that may contain outgoing pointers + * to objects in young generation. + * + * \param params The parameters retrieved from `GetWriteBarrierType()`. + * \param inner_pointer Pointer to the source object. + */ + static V8_INLINE void GenerationalBarrierForSourceObject( + const WriteBarrierParams& params, const void* inner_pointer) { + internal::WriteBarrier::GenerationalBarrier< + internal::WriteBarrier::GenerationalBarrierType::kImpreciseSlot>( + params, inner_pointer); + } + + private: + HeapConsistency() = delete; +}; + +/** + * Disallows garbage collection finalizations. Any garbage collection triggers + * result in a crash when in this scope. + * + * Note that the garbage collector already covers paths that can lead to garbage + * collections, so user code does not require checking + * `IsGarbageCollectionAllowed()` before allocations. + */ +class V8_EXPORT V8_NODISCARD DisallowGarbageCollectionScope final { + CPPGC_STACK_ALLOCATED(); + + public: + /** + * \returns whether garbage collections are currently allowed. + */ + static bool IsGarbageCollectionAllowed(HeapHandle& heap_handle); + + /** + * Enters a disallow garbage collection scope. Must be paired with `Leave()`. + * Prefer a scope instance of `DisallowGarbageCollectionScope`. + * + * \param heap_handle The corresponding heap. + */ + static void Enter(HeapHandle& heap_handle); + + /** + * Leaves a disallow garbage collection scope. Must be paired with `Enter()`. + * Prefer a scope instance of `DisallowGarbageCollectionScope`. + * + * \param heap_handle The corresponding heap. + */ + static void Leave(HeapHandle& heap_handle); + + /** + * Constructs a scoped object that automatically enters and leaves a disallow + * garbage collection scope based on its lifetime. + * + * \param heap_handle The corresponding heap. + */ + explicit DisallowGarbageCollectionScope(HeapHandle& heap_handle); + ~DisallowGarbageCollectionScope(); + + DisallowGarbageCollectionScope(const DisallowGarbageCollectionScope&) = + delete; + DisallowGarbageCollectionScope& operator=( + const DisallowGarbageCollectionScope&) = delete; + + private: + HeapHandle& heap_handle_; +}; + +/** + * Avoids invoking garbage collection finalizations. Already running garbage + * collection phase are unaffected by this scope. + * + * Should only be used temporarily as the scope has an impact on memory usage + * and follow up garbage collections. + */ +class V8_EXPORT V8_NODISCARD NoGarbageCollectionScope final { + CPPGC_STACK_ALLOCATED(); + + public: + /** + * Enters a no garbage collection scope. Must be paired with `Leave()`. Prefer + * a scope instance of `NoGarbageCollectionScope`. + * + * \param heap_handle The corresponding heap. + */ + static void Enter(HeapHandle& heap_handle); + + /** + * Leaves a no garbage collection scope. Must be paired with `Enter()`. Prefer + * a scope instance of `NoGarbageCollectionScope`. + * + * \param heap_handle The corresponding heap. + */ + static void Leave(HeapHandle& heap_handle); + + /** + * Constructs a scoped object that automatically enters and leaves a no + * garbage collection scope based on its lifetime. + * + * \param heap_handle The corresponding heap. + */ + explicit NoGarbageCollectionScope(HeapHandle& heap_handle); + ~NoGarbageCollectionScope(); + + NoGarbageCollectionScope(const NoGarbageCollectionScope&) = delete; + NoGarbageCollectionScope& operator=(const NoGarbageCollectionScope&) = delete; + + private: + HeapHandle& heap_handle_; +}; + +} // namespace subtle +} // namespace cppgc + +#endif // INCLUDE_CPPGC_HEAP_CONSISTENCY_H_ diff --git a/deps/include/cppgc/heap-handle.h b/deps/include/cppgc/heap-handle.h new file mode 100755 index 0000000..0d1d21e --- /dev/null +++ b/deps/include/cppgc/heap-handle.h @@ -0,0 +1,48 @@ +// Copyright 2022 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_HEAP_HANDLE_H_ +#define INCLUDE_CPPGC_HEAP_HANDLE_H_ + +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +namespace internal { +class HeapBase; +class WriteBarrierTypeForCagedHeapPolicy; +class WriteBarrierTypeForNonCagedHeapPolicy; +} // namespace internal + +/** + * Opaque handle used for additional heap APIs. + */ +class HeapHandle { + public: + // Deleted copy ctor to avoid treating the type by value. + HeapHandle(const HeapHandle&) = delete; + HeapHandle& operator=(const HeapHandle&) = delete; + + private: + HeapHandle() = default; + + V8_INLINE bool is_incremental_marking_in_progress() const { + return is_incremental_marking_in_progress_; + } + + V8_INLINE bool is_young_generation_enabled() const { + return is_young_generation_enabled_; + } + + bool is_incremental_marking_in_progress_ = false; + bool is_young_generation_enabled_ = false; + + friend class internal::HeapBase; + friend class internal::WriteBarrierTypeForCagedHeapPolicy; + friend class internal::WriteBarrierTypeForNonCagedHeapPolicy; +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_HEAP_HANDLE_H_ diff --git a/deps/include/cppgc/heap-state.h b/deps/include/cppgc/heap-state.h new file mode 100755 index 0000000..2821258 --- /dev/null +++ b/deps/include/cppgc/heap-state.h @@ -0,0 +1,82 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_HEAP_STATE_H_ +#define INCLUDE_CPPGC_HEAP_STATE_H_ + +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +class HeapHandle; + +namespace subtle { + +/** + * Helpers to peek into heap-internal state. + */ +class V8_EXPORT HeapState final { + public: + /** + * Returns whether the garbage collector is marking. This API is experimental + * and is expected to be removed in future. + * + * \param heap_handle The corresponding heap. + * \returns true if the garbage collector is currently marking, and false + * otherwise. + */ + static bool IsMarking(const HeapHandle& heap_handle); + + /* + * Returns whether the garbage collector is sweeping. This API is experimental + * and is expected to be removed in future. + * + * \param heap_handle The corresponding heap. + * \returns true if the garbage collector is currently sweeping, and false + * otherwise. + */ + static bool IsSweeping(const HeapHandle& heap_handle); + + /* + * Returns whether the garbage collector is currently sweeping on the thread + * owning this heap. This API allows the caller to determine whether it has + * been called from a destructor of a managed object. This API is experimental + * and may be removed in future. + * + * \param heap_handle The corresponding heap. + * \returns true if the garbage collector is currently sweeping on this + * thread, and false otherwise. + */ + static bool IsSweepingOnOwningThread(const HeapHandle& heap_handle); + + /** + * Returns whether the garbage collector is in the atomic pause, i.e., the + * mutator is stopped from running. This API is experimental and is expected + * to be removed in future. + * + * \param heap_handle The corresponding heap. + * \returns true if the garbage collector is currently in the atomic pause, + * and false otherwise. + */ + static bool IsInAtomicPause(const HeapHandle& heap_handle); + + /** + * Returns whether the last garbage collection was finalized conservatively + * (i.e., with a non-empty stack). This API is experimental and is expected to + * be removed in future. + * + * \param heap_handle The corresponding heap. + * \returns true if the last garbage collection was finalized conservatively, + * and false otherwise. + */ + static bool PreviousGCWasConservative(const HeapHandle& heap_handle); + + private: + HeapState() = delete; +}; + +} // namespace subtle +} // namespace cppgc + +#endif // INCLUDE_CPPGC_HEAP_STATE_H_ diff --git a/deps/include/cppgc/heap-statistics.h b/deps/include/cppgc/heap-statistics.h new file mode 100755 index 0000000..5e38987 --- /dev/null +++ b/deps/include/cppgc/heap-statistics.h @@ -0,0 +1,120 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_HEAP_STATISTICS_H_ +#define INCLUDE_CPPGC_HEAP_STATISTICS_H_ + +#include +#include +#include +#include + +namespace cppgc { + +/** + * `HeapStatistics` contains memory consumption and utilization statistics for a + * cppgc heap. + */ +struct HeapStatistics final { + /** + * Specifies the detail level of the heap statistics. Brief statistics contain + * only the top-level allocated and used memory statistics for the entire + * heap. Detailed statistics also contain a break down per space and page, as + * well as freelist statistics and object type histograms. Note that used + * memory reported by brief statistics and detailed statistics might differ + * slightly. + */ + enum DetailLevel : uint8_t { + kBrief, + kDetailed, + }; + + /** + * Object statistics for a single type. + */ + struct ObjectStatsEntry { + /** + * Number of allocated bytes. + */ + size_t allocated_bytes; + /** + * Number of allocated objects. + */ + size_t object_count; + }; + + /** + * Page granularity statistics. For each page the statistics record the + * allocated memory size and overall used memory size for the page. + */ + struct PageStatistics { + /** Overall committed amount of memory for the page. */ + size_t committed_size_bytes = 0; + /** Resident amount of memory held by the page. */ + size_t resident_size_bytes = 0; + /** Amount of memory actually used on the page. */ + size_t used_size_bytes = 0; + /** Statistics for object allocated on the page. Filled only when + * NameProvider::SupportsCppClassNamesAsObjectNames() is true. */ + std::vector object_statistics; + }; + + /** + * Statistics of the freelist (used only in non-large object spaces). For + * each bucket in the freelist the statistics record the bucket size, the + * number of freelist entries in the bucket, and the overall allocated memory + * consumed by these freelist entries. + */ + struct FreeListStatistics { + /** bucket sizes in the freelist. */ + std::vector bucket_size; + /** number of freelist entries per bucket. */ + std::vector free_count; + /** memory size consumed by freelist entries per size. */ + std::vector free_size; + }; + + /** + * Space granularity statistics. For each space the statistics record the + * space name, the amount of allocated memory and overall used memory for the + * space. The statistics also contain statistics for each of the space's + * pages, its freelist and the objects allocated on the space. + */ + struct SpaceStatistics { + /** The space name */ + std::string name; + /** Overall committed amount of memory for the heap. */ + size_t committed_size_bytes = 0; + /** Resident amount of memory held by the heap. */ + size_t resident_size_bytes = 0; + /** Amount of memory actually used on the space. */ + size_t used_size_bytes = 0; + /** Statistics for each of the pages in the space. */ + std::vector page_stats; + /** Statistics for the freelist of the space. */ + FreeListStatistics free_list_stats; + }; + + /** Overall committed amount of memory for the heap. */ + size_t committed_size_bytes = 0; + /** Resident amount of memory held by the heap. */ + size_t resident_size_bytes = 0; + /** Amount of memory actually used on the heap. */ + size_t used_size_bytes = 0; + /** Detail level of this HeapStatistics. */ + DetailLevel detail_level; + + /** Statistics for each of the spaces in the heap. Filled only when + * `detail_level` is `DetailLevel::kDetailed`. */ + std::vector space_stats; + + /** + * Vector of `cppgc::GarbageCollected` type names. + */ + std::vector type_names; +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_HEAP_STATISTICS_H_ diff --git a/deps/include/cppgc/heap.h b/deps/include/cppgc/heap.h new file mode 100755 index 0000000..02ee12e --- /dev/null +++ b/deps/include/cppgc/heap.h @@ -0,0 +1,202 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_HEAP_H_ +#define INCLUDE_CPPGC_HEAP_H_ + +#include +#include +#include +#include + +#include "cppgc/common.h" +#include "cppgc/custom-space.h" +#include "cppgc/platform.h" +#include "v8config.h" // NOLINT(build/include_directory) + +/** + * cppgc - A C++ garbage collection library. + */ +namespace cppgc { + +class AllocationHandle; +class HeapHandle; + +/** + * Implementation details of cppgc. Those details are considered internal and + * may change at any point in time without notice. Users should never rely on + * the contents of this namespace. + */ +namespace internal { +class Heap; +} // namespace internal + +class V8_EXPORT Heap { + public: + /** + * Specifies the stack state the embedder is in. + */ + using StackState = EmbedderStackState; + + /** + * Specifies whether conservative stack scanning is supported. + */ + enum class StackSupport : uint8_t { + /** + * Conservative stack scan is supported. + */ + kSupportsConservativeStackScan, + /** + * Conservative stack scan is not supported. Embedders may use this option + * when using custom infrastructure that is unsupported by the library. + */ + kNoConservativeStackScan, + }; + + /** + * Specifies supported marking types. + */ + enum class MarkingType : uint8_t { + /** + * Atomic stop-the-world marking. This option does not require any write + * barriers but is the most intrusive in terms of jank. + */ + kAtomic, + /** + * Incremental marking interleaves marking with the rest of the application + * workload on the same thread. + */ + kIncremental, + /** + * Incremental and concurrent marking. + */ + kIncrementalAndConcurrent + }; + + /** + * Specifies supported sweeping types. + */ + enum class SweepingType : uint8_t { + /** + * Atomic stop-the-world sweeping. All of sweeping is performed at once. + */ + kAtomic, + /** + * Incremental sweeping interleaves sweeping with the rest of the + * application workload on the same thread. + */ + kIncremental, + /** + * Incremental and concurrent sweeping. Sweeping is split and interleaved + * with the rest of the application. + */ + kIncrementalAndConcurrent + }; + + /** + * Constraints for a Heap setup. + */ + struct ResourceConstraints { + /** + * Allows the heap to grow to some initial size in bytes before triggering + * garbage collections. This is useful when it is known that applications + * need a certain minimum heap to run to avoid repeatedly invoking the + * garbage collector when growing the heap. + */ + size_t initial_heap_size_bytes = 0; + }; + + /** + * Options specifying Heap properties (e.g. custom spaces) when initializing a + * heap through `Heap::Create()`. + */ + struct HeapOptions { + /** + * Creates reasonable defaults for instantiating a Heap. + * + * \returns the HeapOptions that can be passed to `Heap::Create()`. + */ + static HeapOptions Default() { return {}; } + + /** + * Custom spaces added to heap are required to have indices forming a + * numbered sequence starting at 0, i.e., their `kSpaceIndex` must + * correspond to the index they reside in the vector. + */ + std::vector> custom_spaces; + + /** + * Specifies whether conservative stack scan is supported. When conservative + * stack scan is not supported, the collector may try to invoke + * garbage collections using non-nestable task, which are guaranteed to have + * no interesting stack, through the provided Platform. If such tasks are + * not supported by the Platform, the embedder must take care of invoking + * the GC through `ForceGarbageCollectionSlow()`. + */ + StackSupport stack_support = StackSupport::kSupportsConservativeStackScan; + + /** + * Specifies which types of marking are supported by the heap. + */ + MarkingType marking_support = MarkingType::kIncrementalAndConcurrent; + + /** + * Specifies which types of sweeping are supported by the heap. + */ + SweepingType sweeping_support = SweepingType::kIncrementalAndConcurrent; + + /** + * Resource constraints specifying various properties that the internal + * GC scheduler follows. + */ + ResourceConstraints resource_constraints; + }; + + /** + * Creates a new heap that can be used for object allocation. + * + * \param platform implemented and provided by the embedder. + * \param options HeapOptions specifying various properties for the Heap. + * \returns a new Heap instance. + */ + static std::unique_ptr Create( + std::shared_ptr platform, + HeapOptions options = HeapOptions::Default()); + + virtual ~Heap() = default; + + /** + * Forces garbage collection. + * + * \param source String specifying the source (or caller) triggering a + * forced garbage collection. + * \param reason String specifying the reason for the forced garbage + * collection. + * \param stack_state The embedder stack state, see StackState. + */ + void ForceGarbageCollectionSlow( + const char* source, const char* reason, + StackState stack_state = StackState::kMayContainHeapPointers); + + /** + * \returns the opaque handle for allocating objects using + * `MakeGarbageCollected()`. + */ + AllocationHandle& GetAllocationHandle(); + + /** + * \returns the opaque heap handle which may be used to refer to this heap in + * other APIs. Valid as long as the underlying `Heap` is alive. + */ + HeapHandle& GetHeapHandle(); + + private: + Heap() = default; + + friend class internal::Heap; +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_HEAP_H_ diff --git a/deps/include/cppgc/internal/api-constants.h b/deps/include/cppgc/internal/api-constants.h new file mode 100755 index 0000000..023426e --- /dev/null +++ b/deps/include/cppgc/internal/api-constants.h @@ -0,0 +1,65 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_INTERNAL_API_CONSTANTS_H_ +#define INCLUDE_CPPGC_INTERNAL_API_CONSTANTS_H_ + +#include +#include + +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { +namespace internal { + +// Embedders should not rely on this code! + +// Internal constants to avoid exposing internal types on the API surface. +namespace api_constants { + +constexpr size_t kKB = 1024; +constexpr size_t kMB = kKB * 1024; +constexpr size_t kGB = kMB * 1024; + +// Offset of the uint16_t bitfield from the payload contaning the +// in-construction bit. This is subtracted from the payload pointer to get +// to the right bitfield. +static constexpr size_t kFullyConstructedBitFieldOffsetFromPayload = + 2 * sizeof(uint16_t); +// Mask for in-construction bit. +static constexpr uint16_t kFullyConstructedBitMask = uint16_t{1}; + +static constexpr size_t kPageSize = size_t{1} << 17; + +#if defined(V8_TARGET_ARCH_ARM64) && defined(V8_OS_MACOS) +constexpr size_t kGuardPageSize = 0; +#else +constexpr size_t kGuardPageSize = 4096; +#endif + +static constexpr size_t kLargeObjectSizeThreshold = kPageSize / 2; + +#if defined(CPPGC_CAGED_HEAP) +#if defined(CPPGC_2GB_CAGE) +constexpr size_t kCagedHeapReservationSize = static_cast(2) * kGB; +#else // !defined(CPPGC_2GB_CAGE) +constexpr size_t kCagedHeapReservationSize = static_cast(4) * kGB; +#endif // !defined(CPPGC_2GB_CAGE) +constexpr size_t kCagedHeapReservationAlignment = kCagedHeapReservationSize; +#endif // defined(CPPGC_CAGED_HEAP) + +static constexpr size_t kDefaultAlignment = sizeof(void*); + +// Maximum support alignment for a type as in `alignof(T)`. +static constexpr size_t kMaxSupportedAlignment = 2 * kDefaultAlignment; + +// Granularity of heap allocations. +constexpr size_t kAllocationGranularity = sizeof(void*); + +} // namespace api_constants + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_API_CONSTANTS_H_ diff --git a/deps/include/cppgc/internal/atomic-entry-flag.h b/deps/include/cppgc/internal/atomic-entry-flag.h new file mode 100755 index 0000000..5a7d3b8 --- /dev/null +++ b/deps/include/cppgc/internal/atomic-entry-flag.h @@ -0,0 +1,48 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_INTERNAL_ATOMIC_ENTRY_FLAG_H_ +#define INCLUDE_CPPGC_INTERNAL_ATOMIC_ENTRY_FLAG_H_ + +#include + +namespace cppgc { +namespace internal { + +// A flag which provides a fast check whether a scope may be entered on the +// current thread, without needing to access thread-local storage or mutex. Can +// have false positives (i.e., spuriously report that it might be entered), so +// it is expected that this will be used in tandem with a precise check that the +// scope is in fact entered on that thread. +// +// Example: +// g_frobnicating_flag.MightBeEntered() && +// ThreadLocalFrobnicator().IsFrobnicating() +// +// Relaxed atomic operations are sufficient, since: +// - all accesses remain atomic +// - each thread must observe its own operations in order +// - no thread ever exits the flag more times than it enters (if used correctly) +// And so if a thread observes zero, it must be because it has observed an equal +// number of exits as entries. +class AtomicEntryFlag final { + public: + void Enter() { entries_.fetch_add(1, std::memory_order_relaxed); } + void Exit() { entries_.fetch_sub(1, std::memory_order_relaxed); } + + // Returns false only if the current thread is not between a call to Enter + // and a call to Exit. Returns true if this thread or another thread may + // currently be in the scope guarded by this flag. + bool MightBeEntered() const { + return entries_.load(std::memory_order_relaxed) != 0; + } + + private: + std::atomic_int entries_{0}; +}; + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_ATOMIC_ENTRY_FLAG_H_ diff --git a/deps/include/cppgc/internal/base-page-handle.h b/deps/include/cppgc/internal/base-page-handle.h new file mode 100755 index 0000000..9c69075 --- /dev/null +++ b/deps/include/cppgc/internal/base-page-handle.h @@ -0,0 +1,45 @@ +// Copyright 2022 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_INTERNAL_BASE_PAGE_HANDLE_H_ +#define INCLUDE_CPPGC_INTERNAL_BASE_PAGE_HANDLE_H_ + +#include "cppgc/heap-handle.h" +#include "cppgc/internal/api-constants.h" +#include "cppgc/internal/logging.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { +namespace internal { + +// The class is needed in the header to allow for fast access to HeapHandle in +// the write barrier. +class BasePageHandle { + public: + static V8_INLINE BasePageHandle* FromPayload(void* payload) { + return reinterpret_cast( + (reinterpret_cast(payload) & + ~(api_constants::kPageSize - 1)) + + api_constants::kGuardPageSize); + } + static V8_INLINE const BasePageHandle* FromPayload(const void* payload) { + return FromPayload(const_cast(payload)); + } + + HeapHandle& heap_handle() { return heap_handle_; } + const HeapHandle& heap_handle() const { return heap_handle_; } + + protected: + explicit BasePageHandle(HeapHandle& heap_handle) : heap_handle_(heap_handle) { + CPPGC_DCHECK(reinterpret_cast(this) % api_constants::kPageSize == + api_constants::kGuardPageSize); + } + + HeapHandle& heap_handle_; +}; + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_BASE_PAGE_HANDLE_H_ diff --git a/deps/include/cppgc/internal/caged-heap-local-data.h b/deps/include/cppgc/internal/caged-heap-local-data.h new file mode 100755 index 0000000..7d689f8 --- /dev/null +++ b/deps/include/cppgc/internal/caged-heap-local-data.h @@ -0,0 +1,111 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_LOCAL_DATA_H_ +#define INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_LOCAL_DATA_H_ + +#include +#include +#include + +#include "cppgc/internal/api-constants.h" +#include "cppgc/internal/caged-heap.h" +#include "cppgc/internal/logging.h" +#include "cppgc/platform.h" +#include "v8config.h" // NOLINT(build/include_directory) + +#if __cpp_lib_bitopts +#include +#endif // __cpp_lib_bitopts + +#if defined(CPPGC_CAGED_HEAP) + +namespace cppgc { +namespace internal { + +class HeapBase; +class HeapBaseHandle; + +#if defined(CPPGC_YOUNG_GENERATION) + +// AgeTable is the bytemap needed for the fast generation check in the write +// barrier. AgeTable contains entries that correspond to 4096 bytes memory +// regions (cards). Each entry in the table represents generation of the objects +// that reside on the corresponding card (young, old or mixed). +class V8_EXPORT AgeTable final { + static constexpr size_t kRequiredSize = 1 * api_constants::kMB; + static constexpr size_t kAllocationGranularity = + api_constants::kAllocationGranularity; + + public: + // Represents age of the objects living on a single card. + enum class Age : uint8_t { kOld, kYoung, kMixed }; + // When setting age for a range, consider or ignore ages of the adjacent + // cards. + enum class AdjacentCardsPolicy : uint8_t { kConsider, kIgnore }; + + static constexpr size_t kCardSizeInBytes = + api_constants::kCagedHeapReservationSize / kRequiredSize; + + void SetAge(uintptr_t cage_offset, Age age) { + table_[card(cage_offset)] = age; + } + + V8_INLINE Age GetAge(uintptr_t cage_offset) const { + return table_[card(cage_offset)]; + } + + void SetAgeForRange(uintptr_t cage_offset_begin, uintptr_t cage_offset_end, + Age age, AdjacentCardsPolicy adjacent_cards_policy); + + Age GetAgeForRange(uintptr_t cage_offset_begin, + uintptr_t cage_offset_end) const; + + void ResetForTesting(); + + private: + V8_INLINE size_t card(uintptr_t offset) const { + constexpr size_t kGranularityBits = +#if __cpp_lib_bitopts + std::countr_zero(static_cast(kCardSizeInBytes)); +#elif V8_HAS_BUILTIN_CTZ + __builtin_ctz(static_cast(kCardSizeInBytes)); +#else //! V8_HAS_BUILTIN_CTZ + // Hardcode and check with assert. +#if defined(CPPGC_2GB_CAGE) + 11; +#else // !defined(CPPGC_2GB_CAGE) + 12; +#endif // !defined(CPPGC_2GB_CAGE) +#endif // !V8_HAS_BUILTIN_CTZ + static_assert((1 << kGranularityBits) == kCardSizeInBytes); + const size_t entry = offset >> kGranularityBits; + CPPGC_DCHECK(table_.size() > entry); + return entry; + } + + std::array table_; +}; + +static_assert(sizeof(AgeTable) == 1 * api_constants::kMB, + "Size of AgeTable is 1MB"); + +#endif // CPPGC_YOUNG_GENERATION + +struct CagedHeapLocalData final { + V8_INLINE static CagedHeapLocalData& Get() { + return *reinterpret_cast(CagedHeapBase::GetBase()); + } + +#if defined(CPPGC_YOUNG_GENERATION) + AgeTable age_table; +#endif +}; + +} // namespace internal +} // namespace cppgc + +#endif // defined(CPPGC_CAGED_HEAP) + +#endif // INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_LOCAL_DATA_H_ diff --git a/deps/include/cppgc/internal/caged-heap.h b/deps/include/cppgc/internal/caged-heap.h new file mode 100755 index 0000000..4db42ae --- /dev/null +++ b/deps/include/cppgc/internal/caged-heap.h @@ -0,0 +1,61 @@ +// Copyright 2022 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_H_ +#define INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_H_ + +#include +#include + +#include "cppgc/internal/api-constants.h" +#include "cppgc/internal/base-page-handle.h" +#include "v8config.h" // NOLINT(build/include_directory) + +#if defined(CPPGC_CAGED_HEAP) + +namespace cppgc { +namespace internal { + +class V8_EXPORT CagedHeapBase { + public: + V8_INLINE static uintptr_t OffsetFromAddress(const void* address) { + return reinterpret_cast(address) & + (api_constants::kCagedHeapReservationAlignment - 1); + } + + V8_INLINE static bool IsWithinCage(const void* address) { + CPPGC_DCHECK(g_heap_base_); + return (reinterpret_cast(address) & + ~(api_constants::kCagedHeapReservationAlignment - 1)) == + g_heap_base_; + } + + V8_INLINE static bool AreWithinCage(const void* addr1, const void* addr2) { +#if defined(CPPGC_2GB_CAGE) + static constexpr size_t kHalfWordShift = sizeof(uint32_t) * CHAR_BIT - 1; +#else //! defined(CPPGC_2GB_CAGE) + static constexpr size_t kHalfWordShift = sizeof(uint32_t) * CHAR_BIT; +#endif //! defined(CPPGC_2GB_CAGE) + static_assert((static_cast(1) << kHalfWordShift) == + api_constants::kCagedHeapReservationSize); + CPPGC_DCHECK(g_heap_base_); + return !(((reinterpret_cast(addr1) ^ g_heap_base_) | + (reinterpret_cast(addr2) ^ g_heap_base_)) >> + kHalfWordShift); + } + + V8_INLINE static uintptr_t GetBase() { return g_heap_base_; } + + private: + friend class CagedHeap; + + static uintptr_t g_heap_base_; +}; + +} // namespace internal +} // namespace cppgc + +#endif // defined(CPPGC_CAGED_HEAP) + +#endif // INCLUDE_CPPGC_INTERNAL_CAGED_HEAP_H_ diff --git a/deps/include/cppgc/internal/compiler-specific.h b/deps/include/cppgc/internal/compiler-specific.h new file mode 100755 index 0000000..595b639 --- /dev/null +++ b/deps/include/cppgc/internal/compiler-specific.h @@ -0,0 +1,38 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_INTERNAL_COMPILER_SPECIFIC_H_ +#define INCLUDE_CPPGC_INTERNAL_COMPILER_SPECIFIC_H_ + +namespace cppgc { + +#if defined(__has_attribute) +#define CPPGC_HAS_ATTRIBUTE(FEATURE) __has_attribute(FEATURE) +#else +#define CPPGC_HAS_ATTRIBUTE(FEATURE) 0 +#endif + +#if defined(__has_cpp_attribute) +#define CPPGC_HAS_CPP_ATTRIBUTE(FEATURE) __has_cpp_attribute(FEATURE) +#else +#define CPPGC_HAS_CPP_ATTRIBUTE(FEATURE) 0 +#endif + +// [[no_unique_address]] comes in C++20 but supported in clang with -std >= +// c++11. +#if CPPGC_HAS_CPP_ATTRIBUTE(no_unique_address) +#define CPPGC_NO_UNIQUE_ADDRESS [[no_unique_address]] +#else +#define CPPGC_NO_UNIQUE_ADDRESS +#endif + +#if CPPGC_HAS_ATTRIBUTE(unused) +#define CPPGC_UNUSED __attribute__((unused)) +#else +#define CPPGC_UNUSED +#endif + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_COMPILER_SPECIFIC_H_ diff --git a/deps/include/cppgc/internal/finalizer-trait.h b/deps/include/cppgc/internal/finalizer-trait.h new file mode 100755 index 0000000..ab49af8 --- /dev/null +++ b/deps/include/cppgc/internal/finalizer-trait.h @@ -0,0 +1,93 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_INTERNAL_FINALIZER_TRAIT_H_ +#define INCLUDE_CPPGC_INTERNAL_FINALIZER_TRAIT_H_ + +#include + +#include "cppgc/type-traits.h" + +namespace cppgc { +namespace internal { + +using FinalizationCallback = void (*)(void*); + +template +struct HasFinalizeGarbageCollectedObject : std::false_type {}; + +template +struct HasFinalizeGarbageCollectedObject< + T, + std::void_t().FinalizeGarbageCollectedObject())>> + : std::true_type {}; + +// The FinalizerTraitImpl specifies how to finalize objects. +template +struct FinalizerTraitImpl; + +template +struct FinalizerTraitImpl { + private: + // Dispatch to custom FinalizeGarbageCollectedObject(). + struct Custom { + static void Call(void* obj) { + static_cast(obj)->FinalizeGarbageCollectedObject(); + } + }; + + // Dispatch to regular destructor. + struct Destructor { + static void Call(void* obj) { static_cast(obj)->~T(); } + }; + + using FinalizeImpl = + std::conditional_t::value, Custom, + Destructor>; + + public: + static void Finalize(void* obj) { + static_assert(sizeof(T), "T must be fully defined"); + FinalizeImpl::Call(obj); + } +}; + +template +struct FinalizerTraitImpl { + static void Finalize(void* obj) { + static_assert(sizeof(T), "T must be fully defined"); + } +}; + +// The FinalizerTrait is used to determine if a type requires finalization and +// what finalization means. +template +struct FinalizerTrait { + private: + // Object has a finalizer if it has + // - a custom FinalizeGarbageCollectedObject method, or + // - a destructor. + static constexpr bool kNonTrivialFinalizer = + internal::HasFinalizeGarbageCollectedObject::value || + !std::is_trivially_destructible::type>::value; + + static void Finalize(void* obj) { + internal::FinalizerTraitImpl::Finalize(obj); + } + + public: + static constexpr bool HasFinalizer() { return kNonTrivialFinalizer; } + + // The callback used to finalize an object of type T. + static constexpr FinalizationCallback kCallback = + kNonTrivialFinalizer ? Finalize : nullptr; +}; + +template +constexpr FinalizationCallback FinalizerTrait::kCallback; + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_FINALIZER_TRAIT_H_ diff --git a/deps/include/cppgc/internal/gc-info.h b/deps/include/cppgc/internal/gc-info.h new file mode 100755 index 0000000..e8f90fe --- /dev/null +++ b/deps/include/cppgc/internal/gc-info.h @@ -0,0 +1,155 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_INTERNAL_GC_INFO_H_ +#define INCLUDE_CPPGC_INTERNAL_GC_INFO_H_ + +#include +#include +#include + +#include "cppgc/internal/finalizer-trait.h" +#include "cppgc/internal/name-trait.h" +#include "cppgc/trace-trait.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { +namespace internal { + +using GCInfoIndex = uint16_t; + +struct V8_EXPORT EnsureGCInfoIndexTrait final { + // Acquires a new GC info object and returns the index. In addition, also + // updates `registered_index` atomically. + template + V8_INLINE static GCInfoIndex EnsureIndex( + std::atomic& registered_index) { + return EnsureGCInfoIndexTraitDispatch{}(registered_index); + } + + private: + template ::value, + bool = FinalizerTrait::HasFinalizer(), + bool = NameTrait::HasNonHiddenName()> + struct EnsureGCInfoIndexTraitDispatch; + + static GCInfoIndex EnsureGCInfoIndexPolymorphic(std::atomic&, + TraceCallback, + FinalizationCallback, + NameCallback); + static GCInfoIndex EnsureGCInfoIndexPolymorphic(std::atomic&, + TraceCallback, + FinalizationCallback); + static GCInfoIndex EnsureGCInfoIndexPolymorphic(std::atomic&, + TraceCallback, NameCallback); + static GCInfoIndex EnsureGCInfoIndexPolymorphic(std::atomic&, + TraceCallback); + static GCInfoIndex EnsureGCInfoIndexNonPolymorphic(std::atomic&, + TraceCallback, + FinalizationCallback, + NameCallback); + static GCInfoIndex EnsureGCInfoIndexNonPolymorphic(std::atomic&, + TraceCallback, + FinalizationCallback); + static GCInfoIndex EnsureGCInfoIndexNonPolymorphic(std::atomic&, + TraceCallback, + NameCallback); + static GCInfoIndex EnsureGCInfoIndexNonPolymorphic(std::atomic&, + TraceCallback); +}; + +#define DISPATCH(is_polymorphic, has_finalizer, has_non_hidden_name, function) \ + template \ + struct EnsureGCInfoIndexTrait::EnsureGCInfoIndexTraitDispatch< \ + T, is_polymorphic, has_finalizer, has_non_hidden_name> { \ + V8_INLINE GCInfoIndex \ + operator()(std::atomic& registered_index) { \ + return function; \ + } \ + }; + +// --------------------------------------------------------------------- // +// DISPATCH(is_polymorphic, has_finalizer, has_non_hidden_name, function) +// --------------------------------------------------------------------- // +DISPATCH(true, true, true, // + EnsureGCInfoIndexPolymorphic(registered_index, // + TraceTrait::Trace, // + FinalizerTrait::kCallback, // + NameTrait::GetName)) // +DISPATCH(true, true, false, // + EnsureGCInfoIndexPolymorphic(registered_index, // + TraceTrait::Trace, // + FinalizerTrait::kCallback)) // +DISPATCH(true, false, true, // + EnsureGCInfoIndexPolymorphic(registered_index, // + TraceTrait::Trace, // + NameTrait::GetName)) // +DISPATCH(true, false, false, // + EnsureGCInfoIndexPolymorphic(registered_index, // + TraceTrait::Trace)) // +DISPATCH(false, true, true, // + EnsureGCInfoIndexNonPolymorphic(registered_index, // + TraceTrait::Trace, // + FinalizerTrait::kCallback, // + NameTrait::GetName)) // +DISPATCH(false, true, false, // + EnsureGCInfoIndexNonPolymorphic(registered_index, // + TraceTrait::Trace, // + FinalizerTrait::kCallback)) // +DISPATCH(false, false, true, // + EnsureGCInfoIndexNonPolymorphic(registered_index, // + TraceTrait::Trace, // + NameTrait::GetName)) // +DISPATCH(false, false, false, // + EnsureGCInfoIndexNonPolymorphic(registered_index, // + TraceTrait::Trace)) // + +#undef DISPATCH + +// Fold types based on finalizer behavior. Note that finalizer characteristics +// align with trace behavior, i.e., destructors are virtual when trace methods +// are and vice versa. +template +struct GCInfoFolding { + static constexpr bool kHasVirtualDestructorAtBase = + std::has_virtual_destructor::value; + static constexpr bool kBothTypesAreTriviallyDestructible = + std::is_trivially_destructible::value && + std::is_trivially_destructible::value; + static constexpr bool kHasCustomFinalizerDispatchAtBase = + internal::HasFinalizeGarbageCollectedObject< + ParentMostGarbageCollectedType>::value; +#ifdef CPPGC_SUPPORTS_OBJECT_NAMES + static constexpr bool kWantsDetailedObjectNames = true; +#else // !CPPGC_SUPPORTS_OBJECT_NAMES + static constexpr bool kWantsDetailedObjectNames = false; +#endif // !CPPGC_SUPPORTS_OBJECT_NAMES + + // Folding would regresses name resolution when deriving names from C++ + // class names as it would just folds a name to the base class name. + using ResultType = std::conditional_t<(kHasVirtualDestructorAtBase || + kBothTypesAreTriviallyDestructible || + kHasCustomFinalizerDispatchAtBase) && + !kWantsDetailedObjectNames, + ParentMostGarbageCollectedType, T>; +}; + +// Trait determines how the garbage collector treats objects wrt. to traversing, +// finalization, and naming. +template +struct GCInfoTrait final { + V8_INLINE static GCInfoIndex Index() { + static_assert(sizeof(T), "T must be fully defined"); + static std::atomic + registered_index; // Uses zero initialization. + const GCInfoIndex index = registered_index.load(std::memory_order_acquire); + return index ? index + : EnsureGCInfoIndexTrait::EnsureIndex(registered_index); + } +}; + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_GC_INFO_H_ diff --git a/deps/include/cppgc/internal/logging.h b/deps/include/cppgc/internal/logging.h new file mode 100755 index 0000000..3a279fe --- /dev/null +++ b/deps/include/cppgc/internal/logging.h @@ -0,0 +1,50 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_INTERNAL_LOGGING_H_ +#define INCLUDE_CPPGC_INTERNAL_LOGGING_H_ + +#include "cppgc/source-location.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { +namespace internal { + +void V8_EXPORT DCheckImpl(const char*, + const SourceLocation& = SourceLocation::Current()); +[[noreturn]] void V8_EXPORT +FatalImpl(const char*, const SourceLocation& = SourceLocation::Current()); + +// Used to ignore -Wunused-variable. +template +struct EatParams {}; + +#if defined(DEBUG) +#define CPPGC_DCHECK_MSG(condition, message) \ + do { \ + if (V8_UNLIKELY(!(condition))) { \ + ::cppgc::internal::DCheckImpl(message); \ + } \ + } while (false) +#else // !defined(DEBUG) +#define CPPGC_DCHECK_MSG(condition, message) \ + (static_cast(::cppgc::internal::EatParams(condition), message)>{})) +#endif // !defined(DEBUG) + +#define CPPGC_DCHECK(condition) CPPGC_DCHECK_MSG(condition, #condition) + +#define CPPGC_CHECK_MSG(condition, message) \ + do { \ + if (V8_UNLIKELY(!(condition))) { \ + ::cppgc::internal::FatalImpl(message); \ + } \ + } while (false) + +#define CPPGC_CHECK(condition) CPPGC_CHECK_MSG(condition, #condition) + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_LOGGING_H_ diff --git a/deps/include/cppgc/internal/member-storage.h b/deps/include/cppgc/internal/member-storage.h new file mode 100755 index 0000000..0eb6382 --- /dev/null +++ b/deps/include/cppgc/internal/member-storage.h @@ -0,0 +1,236 @@ +// Copyright 2022 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_INTERNAL_MEMBER_STORAGE_H_ +#define INCLUDE_CPPGC_INTERNAL_MEMBER_STORAGE_H_ + +#include +#include +#include + +#include "cppgc/internal/api-constants.h" +#include "cppgc/internal/logging.h" +#include "cppgc/sentinel-pointer.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { +namespace internal { + +#if defined(CPPGC_POINTER_COMPRESSION) + +#if defined(__clang__) +// Attribute const allows the compiler to assume that CageBaseGlobal::g_base_ +// doesn't change (e.g. across calls) and thereby avoid redundant loads. +#define CPPGC_CONST __attribute__((const)) +#define CPPGC_REQUIRE_CONSTANT_INIT \ + __attribute__((require_constant_initialization)) +#else // defined(__clang__) +#define CPPGC_CONST +#define CPPGC_REQUIRE_CONSTANT_INIT +#endif // defined(__clang__) + +class CageBaseGlobal final { + public: + V8_INLINE CPPGC_CONST static uintptr_t Get() { + CPPGC_DCHECK(IsBaseConsistent()); + return g_base_; + } + + V8_INLINE CPPGC_CONST static bool IsSet() { + CPPGC_DCHECK(IsBaseConsistent()); + return (g_base_ & ~kLowerHalfWordMask) != 0; + } + + private: + // We keep the lower halfword as ones to speed up decompression. + static constexpr uintptr_t kLowerHalfWordMask = + (api_constants::kCagedHeapReservationAlignment - 1); + + static V8_EXPORT uintptr_t g_base_ CPPGC_REQUIRE_CONSTANT_INIT; + + CageBaseGlobal() = delete; + + V8_INLINE static bool IsBaseConsistent() { + return kLowerHalfWordMask == (g_base_ & kLowerHalfWordMask); + } + + friend class CageBaseGlobalUpdater; +}; + +#undef CPPGC_REQUIRE_CONSTANT_INIT +#undef CPPGC_CONST + +class V8_TRIVIAL_ABI CompressedPointer final { + public: + using IntegralType = uint32_t; + + V8_INLINE CompressedPointer() : value_(0u) {} + V8_INLINE explicit CompressedPointer(const void* ptr) + : value_(Compress(ptr)) {} + V8_INLINE explicit CompressedPointer(std::nullptr_t) : value_(0u) {} + V8_INLINE explicit CompressedPointer(SentinelPointer) + : value_(kCompressedSentinel) {} + + V8_INLINE const void* Load() const { return Decompress(value_); } + V8_INLINE const void* LoadAtomic() const { + return Decompress( + reinterpret_cast&>(value_).load( + std::memory_order_relaxed)); + } + + V8_INLINE void Store(const void* ptr) { value_ = Compress(ptr); } + V8_INLINE void StoreAtomic(const void* value) { + reinterpret_cast&>(value_).store( + Compress(value), std::memory_order_relaxed); + } + + V8_INLINE void Clear() { value_ = 0u; } + V8_INLINE bool IsCleared() const { return !value_; } + + V8_INLINE bool IsSentinel() const { return value_ == kCompressedSentinel; } + + V8_INLINE uint32_t GetAsInteger() const { return value_; } + + V8_INLINE friend bool operator==(CompressedPointer a, CompressedPointer b) { + return a.value_ == b.value_; + } + V8_INLINE friend bool operator!=(CompressedPointer a, CompressedPointer b) { + return a.value_ != b.value_; + } + V8_INLINE friend bool operator<(CompressedPointer a, CompressedPointer b) { + return a.value_ < b.value_; + } + V8_INLINE friend bool operator<=(CompressedPointer a, CompressedPointer b) { + return a.value_ <= b.value_; + } + V8_INLINE friend bool operator>(CompressedPointer a, CompressedPointer b) { + return a.value_ > b.value_; + } + V8_INLINE friend bool operator>=(CompressedPointer a, CompressedPointer b) { + return a.value_ >= b.value_; + } + + static V8_INLINE IntegralType Compress(const void* ptr) { + static_assert( + SentinelPointer::kSentinelValue == 0b10, + "The compression scheme relies on the sentinel encoded as 0b10"); + static constexpr size_t kGigaCageMask = + ~(api_constants::kCagedHeapReservationAlignment - 1); + + CPPGC_DCHECK(CageBaseGlobal::IsSet()); + const uintptr_t base = CageBaseGlobal::Get(); + CPPGC_DCHECK(!ptr || ptr == kSentinelPointer || + (base & kGigaCageMask) == + (reinterpret_cast(ptr) & kGigaCageMask)); + +#if defined(CPPGC_2GB_CAGE) + // Truncate the pointer. + auto compressed = + static_cast(reinterpret_cast(ptr)); +#else // !defined(CPPGC_2GB_CAGE) + const auto uptr = reinterpret_cast(ptr); + // Shift the pointer by one and truncate. + auto compressed = static_cast(uptr >> 1); +#endif // !defined(CPPGC_2GB_CAGE) + // Normal compressed pointers must have the MSB set. + CPPGC_DCHECK((!compressed || compressed == kCompressedSentinel) || + (compressed & (1 << 31))); + return compressed; + } + + static V8_INLINE void* Decompress(IntegralType ptr) { + CPPGC_DCHECK(CageBaseGlobal::IsSet()); + const uintptr_t base = CageBaseGlobal::Get(); + // Treat compressed pointer as signed and cast it to uint64_t, which will + // sign-extend it. +#if defined(CPPGC_2GB_CAGE) + const uint64_t mask = static_cast(static_cast(ptr)); +#else // !defined(CPPGC_2GB_CAGE) + // Then, shift the result by one. It's important to shift the unsigned + // value, as otherwise it would result in undefined behavior. + const uint64_t mask = static_cast(static_cast(ptr)) << 1; +#endif // !defined(CPPGC_2GB_CAGE) + return reinterpret_cast(mask & base); + } + + private: +#if defined(CPPGC_2GB_CAGE) + static constexpr IntegralType kCompressedSentinel = + SentinelPointer::kSentinelValue; +#else // !defined(CPPGC_2GB_CAGE) + static constexpr IntegralType kCompressedSentinel = + SentinelPointer::kSentinelValue >> 1; +#endif // !defined(CPPGC_2GB_CAGE) + // All constructors initialize `value_`. Do not add a default value here as it + // results in a non-atomic write on some builds, even when the atomic version + // of the constructor is used. + IntegralType value_; +}; + +#endif // defined(CPPGC_POINTER_COMPRESSION) + +class V8_TRIVIAL_ABI RawPointer final { + public: + using IntegralType = uintptr_t; + + V8_INLINE RawPointer() : ptr_(nullptr) {} + V8_INLINE explicit RawPointer(const void* ptr) : ptr_(ptr) {} + + V8_INLINE const void* Load() const { return ptr_; } + V8_INLINE const void* LoadAtomic() const { + return reinterpret_cast&>(ptr_).load( + std::memory_order_relaxed); + } + + V8_INLINE void Store(const void* ptr) { ptr_ = ptr; } + V8_INLINE void StoreAtomic(const void* ptr) { + reinterpret_cast&>(ptr_).store( + ptr, std::memory_order_relaxed); + } + + V8_INLINE void Clear() { ptr_ = nullptr; } + V8_INLINE bool IsCleared() const { return !ptr_; } + + V8_INLINE bool IsSentinel() const { return ptr_ == kSentinelPointer; } + + V8_INLINE uintptr_t GetAsInteger() const { + return reinterpret_cast(ptr_); + } + + V8_INLINE friend bool operator==(RawPointer a, RawPointer b) { + return a.ptr_ == b.ptr_; + } + V8_INLINE friend bool operator!=(RawPointer a, RawPointer b) { + return a.ptr_ != b.ptr_; + } + V8_INLINE friend bool operator<(RawPointer a, RawPointer b) { + return a.ptr_ < b.ptr_; + } + V8_INLINE friend bool operator<=(RawPointer a, RawPointer b) { + return a.ptr_ <= b.ptr_; + } + V8_INLINE friend bool operator>(RawPointer a, RawPointer b) { + return a.ptr_ > b.ptr_; + } + V8_INLINE friend bool operator>=(RawPointer a, RawPointer b) { + return a.ptr_ >= b.ptr_; + } + + private: + // All constructors initialize `ptr_`. Do not add a default value here as it + // results in a non-atomic write on some builds, even when the atomic version + // of the constructor is used. + const void* ptr_; +}; + +#if defined(CPPGC_POINTER_COMPRESSION) +using MemberStorage = CompressedPointer; +#else // !defined(CPPGC_POINTER_COMPRESSION) +using MemberStorage = RawPointer; +#endif // !defined(CPPGC_POINTER_COMPRESSION) + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_MEMBER_STORAGE_H_ diff --git a/deps/include/cppgc/internal/name-trait.h b/deps/include/cppgc/internal/name-trait.h new file mode 100755 index 0000000..1d927a9 --- /dev/null +++ b/deps/include/cppgc/internal/name-trait.h @@ -0,0 +1,137 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_INTERNAL_NAME_TRAIT_H_ +#define INCLUDE_CPPGC_INTERNAL_NAME_TRAIT_H_ + +#include +#include +#include + +#include "cppgc/name-provider.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { +namespace internal { + +#if CPPGC_SUPPORTS_OBJECT_NAMES && defined(__clang__) +#define CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME 1 + +// Provides constexpr c-string storage for a name of fixed |Size| characters. +// Automatically appends terminating 0 byte. +template +struct NameBuffer { + char name[Size + 1]{}; + + static constexpr NameBuffer FromCString(const char* str) { + NameBuffer result; + for (size_t i = 0; i < Size; ++i) result.name[i] = str[i]; + result.name[Size] = 0; + return result; + } +}; + +template +const char* GetTypename() { + static constexpr char kSelfPrefix[] = + "const char *cppgc::internal::GetTypename() [T ="; + static_assert(__builtin_strncmp(__PRETTY_FUNCTION__, kSelfPrefix, + sizeof(kSelfPrefix) - 1) == 0, + "The prefix must match"); + static constexpr const char* kTypenameStart = + __PRETTY_FUNCTION__ + sizeof(kSelfPrefix); + static constexpr size_t kTypenameSize = + __builtin_strlen(__PRETTY_FUNCTION__) - sizeof(kSelfPrefix) - 1; + // NameBuffer is an indirection that is needed to make sure that only a + // substring of __PRETTY_FUNCTION__ gets materialized in the binary. + static constexpr auto buffer = + NameBuffer::FromCString(kTypenameStart); + return buffer.name; +} + +#else +#define CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME 0 +#endif + +struct HeapObjectName { + const char* value; + bool name_was_hidden; +}; + +enum class HeapObjectNameForUnnamedObject : uint8_t { + kUseClassNameIfSupported, + kUseHiddenName, +}; + +class V8_EXPORT NameTraitBase { + protected: + static HeapObjectName GetNameFromTypeSignature(const char*); +}; + +// Trait that specifies how the garbage collector retrieves the name for a +// given object. +template +class NameTrait final : public NameTraitBase { + public: + static constexpr bool HasNonHiddenName() { +#if CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME + return true; +#elif CPPGC_SUPPORTS_OBJECT_NAMES + return true; +#else // !CPPGC_SUPPORTS_OBJECT_NAMES + return std::is_base_of::value; +#endif // !CPPGC_SUPPORTS_OBJECT_NAMES + } + + static HeapObjectName GetName( + const void* obj, HeapObjectNameForUnnamedObject name_retrieval_mode) { + return GetNameFor(static_cast(obj), name_retrieval_mode); + } + + private: + static HeapObjectName GetNameFor(const NameProvider* name_provider, + HeapObjectNameForUnnamedObject) { + // Objects inheriting from `NameProvider` are not considered unnamed as + // users already provided a name for them. + return {name_provider->GetHumanReadableName(), false}; + } + + static HeapObjectName GetNameFor( + const void*, HeapObjectNameForUnnamedObject name_retrieval_mode) { + if (name_retrieval_mode == HeapObjectNameForUnnamedObject::kUseHiddenName) + return {NameProvider::kHiddenName, true}; + +#if CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME + return {GetTypename(), false}; +#elif CPPGC_SUPPORTS_OBJECT_NAMES + +#if defined(V8_CC_GNU) +#define PRETTY_FUNCTION_VALUE __PRETTY_FUNCTION__ +#elif defined(V8_CC_MSVC) +#define PRETTY_FUNCTION_VALUE __FUNCSIG__ +#else +#define PRETTY_FUNCTION_VALUE nullptr +#endif + + static const HeapObjectName leaky_name = + GetNameFromTypeSignature(PRETTY_FUNCTION_VALUE); + return leaky_name; + +#undef PRETTY_FUNCTION_VALUE + +#else // !CPPGC_SUPPORTS_OBJECT_NAMES + return {NameProvider::kHiddenName, true}; +#endif // !CPPGC_SUPPORTS_OBJECT_NAMES + } +}; + +using NameCallback = HeapObjectName (*)(const void*, + HeapObjectNameForUnnamedObject); + +} // namespace internal +} // namespace cppgc + +#undef CPPGC_SUPPORTS_COMPILE_TIME_TYPENAME + +#endif // INCLUDE_CPPGC_INTERNAL_NAME_TRAIT_H_ diff --git a/deps/include/cppgc/internal/persistent-node.h b/deps/include/cppgc/internal/persistent-node.h new file mode 100755 index 0000000..d22692a --- /dev/null +++ b/deps/include/cppgc/internal/persistent-node.h @@ -0,0 +1,214 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_INTERNAL_PERSISTENT_NODE_H_ +#define INCLUDE_CPPGC_INTERNAL_PERSISTENT_NODE_H_ + +#include +#include +#include + +#include "cppgc/internal/logging.h" +#include "cppgc/trace-trait.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { +namespace internal { + +class CrossThreadPersistentRegion; +class FatalOutOfMemoryHandler; +class RootVisitor; + +// PersistentNode represents a variant of two states: +// 1) traceable node with a back pointer to the Persistent object; +// 2) freelist entry. +class PersistentNode final { + public: + PersistentNode() = default; + + PersistentNode(const PersistentNode&) = delete; + PersistentNode& operator=(const PersistentNode&) = delete; + + void InitializeAsUsedNode(void* owner, TraceRootCallback trace) { + CPPGC_DCHECK(trace); + owner_ = owner; + trace_ = trace; + } + + void InitializeAsFreeNode(PersistentNode* next) { + next_ = next; + trace_ = nullptr; + } + + void UpdateOwner(void* owner) { + CPPGC_DCHECK(IsUsed()); + owner_ = owner; + } + + PersistentNode* FreeListNext() const { + CPPGC_DCHECK(!IsUsed()); + return next_; + } + + void Trace(RootVisitor& root_visitor) const { + CPPGC_DCHECK(IsUsed()); + trace_(root_visitor, owner_); + } + + bool IsUsed() const { return trace_; } + + void* owner() const { + CPPGC_DCHECK(IsUsed()); + return owner_; + } + + private: + // PersistentNode acts as a designated union: + // If trace_ != nullptr, owner_ points to the corresponding Persistent handle. + // Otherwise, next_ points to the next freed PersistentNode. + union { + void* owner_ = nullptr; + PersistentNode* next_; + }; + TraceRootCallback trace_ = nullptr; +}; + +class V8_EXPORT PersistentRegionBase { + using PersistentNodeSlots = std::array; + + public: + // Clears Persistent fields to avoid stale pointers after heap teardown. + ~PersistentRegionBase(); + + PersistentRegionBase(const PersistentRegionBase&) = delete; + PersistentRegionBase& operator=(const PersistentRegionBase&) = delete; + + void Iterate(RootVisitor&); + + size_t NodesInUse() const; + + void ClearAllUsedNodes(); + + protected: + explicit PersistentRegionBase(const FatalOutOfMemoryHandler& oom_handler); + + PersistentNode* TryAllocateNodeFromFreeList(void* owner, + TraceRootCallback trace) { + PersistentNode* node = nullptr; + if (V8_LIKELY(free_list_head_)) { + node = free_list_head_; + free_list_head_ = free_list_head_->FreeListNext(); + CPPGC_DCHECK(!node->IsUsed()); + node->InitializeAsUsedNode(owner, trace); + nodes_in_use_++; + } + return node; + } + + void FreeNode(PersistentNode* node) { + CPPGC_DCHECK(node); + CPPGC_DCHECK(node->IsUsed()); + node->InitializeAsFreeNode(free_list_head_); + free_list_head_ = node; + CPPGC_DCHECK(nodes_in_use_ > 0); + nodes_in_use_--; + } + + PersistentNode* RefillFreeListAndAllocateNode(void* owner, + TraceRootCallback trace); + + private: + template + void ClearAllUsedNodes(); + + void RefillFreeList(); + + std::vector> nodes_; + PersistentNode* free_list_head_ = nullptr; + size_t nodes_in_use_ = 0; + const FatalOutOfMemoryHandler& oom_handler_; + + friend class CrossThreadPersistentRegion; +}; + +// Variant of PersistentRegionBase that checks whether the allocation and +// freeing happens only on the thread that created the region. +class V8_EXPORT PersistentRegion final : public PersistentRegionBase { + public: + explicit PersistentRegion(const FatalOutOfMemoryHandler&); + // Clears Persistent fields to avoid stale pointers after heap teardown. + ~PersistentRegion() = default; + + PersistentRegion(const PersistentRegion&) = delete; + PersistentRegion& operator=(const PersistentRegion&) = delete; + + V8_INLINE PersistentNode* AllocateNode(void* owner, TraceRootCallback trace) { + CPPGC_DCHECK(IsCreationThread()); + auto* node = TryAllocateNodeFromFreeList(owner, trace); + if (V8_LIKELY(node)) return node; + + // Slow path allocation allows for checking thread correspondence. + CPPGC_CHECK(IsCreationThread()); + return RefillFreeListAndAllocateNode(owner, trace); + } + + V8_INLINE void FreeNode(PersistentNode* node) { + CPPGC_DCHECK(IsCreationThread()); + PersistentRegionBase::FreeNode(node); + } + + private: + bool IsCreationThread(); + + int creation_thread_id_; +}; + +// CrossThreadPersistent uses PersistentRegionBase but protects it using this +// lock when needed. +class V8_EXPORT PersistentRegionLock final { + public: + PersistentRegionLock(); + ~PersistentRegionLock(); + + static void AssertLocked(); +}; + +// Variant of PersistentRegionBase that checks whether the PersistentRegionLock +// is locked. +class V8_EXPORT CrossThreadPersistentRegion final + : protected PersistentRegionBase { + public: + explicit CrossThreadPersistentRegion(const FatalOutOfMemoryHandler&); + // Clears Persistent fields to avoid stale pointers after heap teardown. + ~CrossThreadPersistentRegion(); + + CrossThreadPersistentRegion(const CrossThreadPersistentRegion&) = delete; + CrossThreadPersistentRegion& operator=(const CrossThreadPersistentRegion&) = + delete; + + V8_INLINE PersistentNode* AllocateNode(void* owner, TraceRootCallback trace) { + PersistentRegionLock::AssertLocked(); + auto* node = TryAllocateNodeFromFreeList(owner, trace); + if (V8_LIKELY(node)) return node; + + return RefillFreeListAndAllocateNode(owner, trace); + } + + V8_INLINE void FreeNode(PersistentNode* node) { + PersistentRegionLock::AssertLocked(); + PersistentRegionBase::FreeNode(node); + } + + void Iterate(RootVisitor&); + + size_t NodesInUse() const; + + void ClearAllUsedNodes(); +}; + +} // namespace internal + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_PERSISTENT_NODE_H_ diff --git a/deps/include/cppgc/internal/pointer-policies.h b/deps/include/cppgc/internal/pointer-policies.h new file mode 100755 index 0000000..8455b3d --- /dev/null +++ b/deps/include/cppgc/internal/pointer-policies.h @@ -0,0 +1,207 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_INTERNAL_POINTER_POLICIES_H_ +#define INCLUDE_CPPGC_INTERNAL_POINTER_POLICIES_H_ + +#include +#include + +#include "cppgc/internal/member-storage.h" +#include "cppgc/internal/write-barrier.h" +#include "cppgc/sentinel-pointer.h" +#include "cppgc/source-location.h" +#include "cppgc/type-traits.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { +namespace internal { + +class HeapBase; +class PersistentRegion; +class CrossThreadPersistentRegion; + +// Tags to distinguish between strong and weak member types. +class StrongMemberTag; +class WeakMemberTag; +class UntracedMemberTag; + +struct DijkstraWriteBarrierPolicy { + V8_INLINE static void InitializingBarrier(const void*, const void*) { + // Since in initializing writes the source object is always white, having no + // barrier doesn't break the tri-color invariant. + } + + V8_INLINE static void AssigningBarrier(const void* slot, const void* value) { + WriteBarrier::Params params; + const WriteBarrier::Type type = + WriteBarrier::GetWriteBarrierType(slot, value, params); + WriteBarrier(type, params, slot, value); + } + + V8_INLINE static void AssigningBarrier(const void* slot, + MemberStorage storage) { + WriteBarrier::Params params; + const WriteBarrier::Type type = + WriteBarrier::GetWriteBarrierType(slot, storage, params); + WriteBarrier(type, params, slot, storage.Load()); + } + + private: + V8_INLINE static void WriteBarrier(WriteBarrier::Type type, + const WriteBarrier::Params& params, + const void* slot, const void* value) { + switch (type) { + case WriteBarrier::Type::kGenerational: + WriteBarrier::GenerationalBarrier< + WriteBarrier::GenerationalBarrierType::kPreciseSlot>(params, slot); + break; + case WriteBarrier::Type::kMarking: + WriteBarrier::DijkstraMarkingBarrier(params, value); + break; + case WriteBarrier::Type::kNone: + break; + } + } +}; + +struct NoWriteBarrierPolicy { + V8_INLINE static void InitializingBarrier(const void*, const void*) {} + V8_INLINE static void AssigningBarrier(const void*, const void*) {} + V8_INLINE static void AssigningBarrier(const void*, MemberStorage) {} +}; + +class V8_EXPORT SameThreadEnabledCheckingPolicyBase { + protected: + void CheckPointerImpl(const void* ptr, bool points_to_payload, + bool check_off_heap_assignments); + + const HeapBase* heap_ = nullptr; +}; + +template +class V8_EXPORT SameThreadEnabledCheckingPolicy + : private SameThreadEnabledCheckingPolicyBase { + protected: + template + void CheckPointer(const T* ptr) { + if (!ptr || (kSentinelPointer == ptr)) return; + + CheckPointersImplTrampoline::Call(this, ptr); + } + + private: + template > + struct CheckPointersImplTrampoline { + static void Call(SameThreadEnabledCheckingPolicy* policy, const T* ptr) { + policy->CheckPointerImpl(ptr, false, kCheckOffHeapAssignments); + } + }; + + template + struct CheckPointersImplTrampoline { + static void Call(SameThreadEnabledCheckingPolicy* policy, const T* ptr) { + policy->CheckPointerImpl(ptr, IsGarbageCollectedTypeV, + kCheckOffHeapAssignments); + } + }; +}; + +class DisabledCheckingPolicy { + protected: + V8_INLINE void CheckPointer(const void*) {} +}; + +#ifdef DEBUG +// Off heap members are not connected to object graph and thus cannot ressurect +// dead objects. +using DefaultMemberCheckingPolicy = + SameThreadEnabledCheckingPolicy; +using DefaultPersistentCheckingPolicy = + SameThreadEnabledCheckingPolicy; +#else // !DEBUG +using DefaultMemberCheckingPolicy = DisabledCheckingPolicy; +using DefaultPersistentCheckingPolicy = DisabledCheckingPolicy; +#endif // !DEBUG +// For CT(W)P neither marking information (for value), nor objectstart bitmap +// (for slot) are guaranteed to be present because there's no synchronization +// between heaps after marking. +using DefaultCrossThreadPersistentCheckingPolicy = DisabledCheckingPolicy; + +class KeepLocationPolicy { + public: + constexpr const SourceLocation& Location() const { return location_; } + + protected: + constexpr KeepLocationPolicy() = default; + constexpr explicit KeepLocationPolicy(const SourceLocation& location) + : location_(location) {} + + // KeepLocationPolicy must not copy underlying source locations. + KeepLocationPolicy(const KeepLocationPolicy&) = delete; + KeepLocationPolicy& operator=(const KeepLocationPolicy&) = delete; + + // Location of the original moved from object should be preserved. + KeepLocationPolicy(KeepLocationPolicy&&) = default; + KeepLocationPolicy& operator=(KeepLocationPolicy&&) = default; + + private: + SourceLocation location_; +}; + +class IgnoreLocationPolicy { + public: + constexpr SourceLocation Location() const { return {}; } + + protected: + constexpr IgnoreLocationPolicy() = default; + constexpr explicit IgnoreLocationPolicy(const SourceLocation&) {} +}; + +#if CPPGC_SUPPORTS_OBJECT_NAMES +using DefaultLocationPolicy = KeepLocationPolicy; +#else +using DefaultLocationPolicy = IgnoreLocationPolicy; +#endif + +struct StrongPersistentPolicy { + using IsStrongPersistent = std::true_type; + static V8_EXPORT PersistentRegion& GetPersistentRegion(const void* object); +}; + +struct WeakPersistentPolicy { + using IsStrongPersistent = std::false_type; + static V8_EXPORT PersistentRegion& GetPersistentRegion(const void* object); +}; + +struct StrongCrossThreadPersistentPolicy { + using IsStrongPersistent = std::true_type; + static V8_EXPORT CrossThreadPersistentRegion& GetPersistentRegion( + const void* object); +}; + +struct WeakCrossThreadPersistentPolicy { + using IsStrongPersistent = std::false_type; + static V8_EXPORT CrossThreadPersistentRegion& GetPersistentRegion( + const void* object); +}; + +// Forward declarations setting up the default policies. +template +class BasicCrossThreadPersistent; +template +class BasicPersistent; +template +class BasicMember; + +} // namespace internal + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_POINTER_POLICIES_H_ diff --git a/deps/include/cppgc/internal/write-barrier.h b/deps/include/cppgc/internal/write-barrier.h new file mode 100755 index 0000000..37bc5c9 --- /dev/null +++ b/deps/include/cppgc/internal/write-barrier.h @@ -0,0 +1,477 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_INTERNAL_WRITE_BARRIER_H_ +#define INCLUDE_CPPGC_INTERNAL_WRITE_BARRIER_H_ + +#include +#include + +#include "cppgc/heap-handle.h" +#include "cppgc/heap-state.h" +#include "cppgc/internal/api-constants.h" +#include "cppgc/internal/atomic-entry-flag.h" +#include "cppgc/internal/base-page-handle.h" +#include "cppgc/internal/member-storage.h" +#include "cppgc/platform.h" +#include "cppgc/sentinel-pointer.h" +#include "cppgc/trace-trait.h" +#include "v8config.h" // NOLINT(build/include_directory) + +#if defined(CPPGC_CAGED_HEAP) +#include "cppgc/internal/caged-heap-local-data.h" +#include "cppgc/internal/caged-heap.h" +#endif + +namespace cppgc { + +class HeapHandle; + +namespace internal { + +#if defined(CPPGC_CAGED_HEAP) +class WriteBarrierTypeForCagedHeapPolicy; +#else // !CPPGC_CAGED_HEAP +class WriteBarrierTypeForNonCagedHeapPolicy; +#endif // !CPPGC_CAGED_HEAP + +class V8_EXPORT WriteBarrier final { + public: + enum class Type : uint8_t { + kNone, + kMarking, + kGenerational, + }; + + enum class GenerationalBarrierType : uint8_t { + kPreciseSlot, + kPreciseUncompressedSlot, + kImpreciseSlot, + }; + + struct Params { + HeapHandle* heap = nullptr; +#if V8_ENABLE_CHECKS + Type type = Type::kNone; +#endif // !V8_ENABLE_CHECKS +#if defined(CPPGC_CAGED_HEAP) + uintptr_t slot_offset = 0; + uintptr_t value_offset = 0; +#endif // CPPGC_CAGED_HEAP + }; + + enum class ValueMode { + kValuePresent, + kNoValuePresent, + }; + + // Returns the required write barrier for a given `slot` and `value`. + static V8_INLINE Type GetWriteBarrierType(const void* slot, const void* value, + Params& params); + // Returns the required write barrier for a given `slot` and `value`. + static V8_INLINE Type GetWriteBarrierType(const void* slot, MemberStorage, + Params& params); + // Returns the required write barrier for a given `slot`. + template + static V8_INLINE Type GetWriteBarrierType(const void* slot, Params& params, + HeapHandleCallback callback); + // Returns the required write barrier for a given `value`. + static V8_INLINE Type GetWriteBarrierType(const void* value, Params& params); + + static V8_INLINE void DijkstraMarkingBarrier(const Params& params, + const void* object); + static V8_INLINE void DijkstraMarkingBarrierRange( + const Params& params, const void* first_element, size_t element_size, + size_t number_of_elements, TraceCallback trace_callback); + static V8_INLINE void SteeleMarkingBarrier(const Params& params, + const void* object); +#if defined(CPPGC_YOUNG_GENERATION) + template + static V8_INLINE void GenerationalBarrier(const Params& params, + const void* slot); +#else // !CPPGC_YOUNG_GENERATION + template + static V8_INLINE void GenerationalBarrier(const Params& params, + const void* slot){} +#endif // CPPGC_YOUNG_GENERATION + +#if V8_ENABLE_CHECKS + static void CheckParams(Type expected_type, const Params& params); +#else // !V8_ENABLE_CHECKS + static void CheckParams(Type expected_type, const Params& params) {} +#endif // !V8_ENABLE_CHECKS + + // The FlagUpdater class allows cppgc internal to update + // |write_barrier_enabled_|. + class FlagUpdater; + static bool IsEnabled() { return write_barrier_enabled_.MightBeEntered(); } + + private: + WriteBarrier() = delete; + +#if defined(CPPGC_CAGED_HEAP) + using WriteBarrierTypePolicy = WriteBarrierTypeForCagedHeapPolicy; +#else // !CPPGC_CAGED_HEAP + using WriteBarrierTypePolicy = WriteBarrierTypeForNonCagedHeapPolicy; +#endif // !CPPGC_CAGED_HEAP + + static void DijkstraMarkingBarrierSlow(const void* value); + static void DijkstraMarkingBarrierSlowWithSentinelCheck(const void* value); + static void DijkstraMarkingBarrierRangeSlow(HeapHandle& heap_handle, + const void* first_element, + size_t element_size, + size_t number_of_elements, + TraceCallback trace_callback); + static void SteeleMarkingBarrierSlow(const void* value); + static void SteeleMarkingBarrierSlowWithSentinelCheck(const void* value); + +#if defined(CPPGC_YOUNG_GENERATION) + static CagedHeapLocalData& GetLocalData(HeapHandle&); + static void GenerationalBarrierSlow(const CagedHeapLocalData& local_data, + const AgeTable& age_table, + const void* slot, uintptr_t value_offset, + HeapHandle* heap_handle); + static void GenerationalBarrierForUncompressedSlotSlow( + const CagedHeapLocalData& local_data, const AgeTable& age_table, + const void* slot, uintptr_t value_offset, HeapHandle* heap_handle); + static void GenerationalBarrierForSourceObjectSlow( + const CagedHeapLocalData& local_data, const void* object, + HeapHandle* heap_handle); +#endif // CPPGC_YOUNG_GENERATION + + static AtomicEntryFlag write_barrier_enabled_; +}; + +template +V8_INLINE WriteBarrier::Type SetAndReturnType(WriteBarrier::Params& params) { + if constexpr (type == WriteBarrier::Type::kNone) + return WriteBarrier::Type::kNone; +#if V8_ENABLE_CHECKS + params.type = type; +#endif // !V8_ENABLE_CHECKS + return type; +} + +#if defined(CPPGC_CAGED_HEAP) +class V8_EXPORT WriteBarrierTypeForCagedHeapPolicy final { + public: + template + static V8_INLINE WriteBarrier::Type Get(const void* slot, const void* value, + WriteBarrier::Params& params, + HeapHandleCallback callback) { + return ValueModeDispatch::Get(slot, value, params, callback); + } + + template + static V8_INLINE WriteBarrier::Type Get(const void* slot, MemberStorage value, + WriteBarrier::Params& params, + HeapHandleCallback callback) { + return ValueModeDispatch::Get(slot, value, params, callback); + } + + template + static V8_INLINE WriteBarrier::Type Get(const void* value, + WriteBarrier::Params& params, + HeapHandleCallback callback) { + return GetNoSlot(value, params, callback); + } + + private: + WriteBarrierTypeForCagedHeapPolicy() = delete; + + template + static V8_INLINE WriteBarrier::Type GetNoSlot(const void* value, + WriteBarrier::Params& params, + HeapHandleCallback) { + const bool within_cage = CagedHeapBase::IsWithinCage(value); + if (!within_cage) return WriteBarrier::Type::kNone; + + // We know that |value| points either within the normal page or to the + // beginning of large-page, so extract the page header by bitmasking. + BasePageHandle* page = + BasePageHandle::FromPayload(const_cast(value)); + + HeapHandle& heap_handle = page->heap_handle(); + if (V8_UNLIKELY(heap_handle.is_incremental_marking_in_progress())) { + return SetAndReturnType(params); + } + + return SetAndReturnType(params); + } + + template + struct ValueModeDispatch; +}; + +template <> +struct WriteBarrierTypeForCagedHeapPolicy::ValueModeDispatch< + WriteBarrier::ValueMode::kValuePresent> { + template + static V8_INLINE WriteBarrier::Type Get(const void* slot, + MemberStorage storage, + WriteBarrier::Params& params, + HeapHandleCallback) { + if (V8_LIKELY(!WriteBarrier::IsEnabled())) + return SetAndReturnType(params); + + return BarrierEnabledGet(slot, storage.Load(), params); + } + + template + static V8_INLINE WriteBarrier::Type Get(const void* slot, const void* value, + WriteBarrier::Params& params, + HeapHandleCallback) { + if (V8_LIKELY(!WriteBarrier::IsEnabled())) + return SetAndReturnType(params); + + return BarrierEnabledGet(slot, value, params); + } + + private: + static V8_INLINE WriteBarrier::Type BarrierEnabledGet( + const void* slot, const void* value, WriteBarrier::Params& params) { + const bool within_cage = CagedHeapBase::AreWithinCage(slot, value); + if (!within_cage) return WriteBarrier::Type::kNone; + + // We know that |value| points either within the normal page or to the + // beginning of large-page, so extract the page header by bitmasking. + BasePageHandle* page = + BasePageHandle::FromPayload(const_cast(value)); + + HeapHandle& heap_handle = page->heap_handle(); + if (V8_LIKELY(!heap_handle.is_incremental_marking_in_progress())) { +#if defined(CPPGC_YOUNG_GENERATION) + if (!heap_handle.is_young_generation_enabled()) + return WriteBarrier::Type::kNone; + params.heap = &heap_handle; + params.slot_offset = CagedHeapBase::OffsetFromAddress(slot); + params.value_offset = CagedHeapBase::OffsetFromAddress(value); + return SetAndReturnType(params); +#else // !CPPGC_YOUNG_GENERATION + return SetAndReturnType(params); +#endif // !CPPGC_YOUNG_GENERATION + } + + // Use marking barrier. + params.heap = &heap_handle; + return SetAndReturnType(params); + } +}; + +template <> +struct WriteBarrierTypeForCagedHeapPolicy::ValueModeDispatch< + WriteBarrier::ValueMode::kNoValuePresent> { + template + static V8_INLINE WriteBarrier::Type Get(const void* slot, const void*, + WriteBarrier::Params& params, + HeapHandleCallback callback) { + if (V8_LIKELY(!WriteBarrier::IsEnabled())) + return SetAndReturnType(params); + + HeapHandle& handle = callback(); +#if defined(CPPGC_YOUNG_GENERATION) + if (V8_LIKELY(!handle.is_incremental_marking_in_progress())) { + if (!handle.is_young_generation_enabled()) { + return WriteBarrier::Type::kNone; + } + params.heap = &handle; + // Check if slot is on stack. + if (V8_UNLIKELY(!CagedHeapBase::IsWithinCage(slot))) { + return SetAndReturnType(params); + } + params.slot_offset = CagedHeapBase::OffsetFromAddress(slot); + return SetAndReturnType(params); + } +#else // !defined(CPPGC_YOUNG_GENERATION) + if (V8_UNLIKELY(!handle.is_incremental_marking_in_progress())) { + return SetAndReturnType(params); + } +#endif // !defined(CPPGC_YOUNG_GENERATION) + params.heap = &handle; + return SetAndReturnType(params); + } +}; + +#endif // CPPGC_CAGED_HEAP + +class V8_EXPORT WriteBarrierTypeForNonCagedHeapPolicy final { + public: + template + static V8_INLINE WriteBarrier::Type Get(const void* slot, const void* value, + WriteBarrier::Params& params, + HeapHandleCallback callback) { + return ValueModeDispatch::Get(slot, value, params, callback); + } + + template + static V8_INLINE WriteBarrier::Type Get(const void* slot, MemberStorage value, + WriteBarrier::Params& params, + HeapHandleCallback callback) { + // `MemberStorage` will always be `RawPointer` for non-caged heap builds. + // Just convert to `void*` in this case. + return ValueModeDispatch::Get(slot, value.Load(), params, + callback); + } + + template + static V8_INLINE WriteBarrier::Type Get(const void* value, + WriteBarrier::Params& params, + HeapHandleCallback callback) { + // The slot will never be used in `Get()` below. + return Get(nullptr, value, params, + callback); + } + + private: + template + struct ValueModeDispatch; + + WriteBarrierTypeForNonCagedHeapPolicy() = delete; +}; + +template <> +struct WriteBarrierTypeForNonCagedHeapPolicy::ValueModeDispatch< + WriteBarrier::ValueMode::kValuePresent> { + template + static V8_INLINE WriteBarrier::Type Get(const void*, const void* object, + WriteBarrier::Params& params, + HeapHandleCallback callback) { + // The following check covers nullptr as well as sentinel pointer. + if (object <= static_cast(kSentinelPointer)) { + return SetAndReturnType(params); + } + if (V8_LIKELY(!WriteBarrier::IsEnabled())) { + return SetAndReturnType(params); + } + // We know that |object| is within the normal page or in the beginning of a + // large page, so extract the page header by bitmasking. + BasePageHandle* page = + BasePageHandle::FromPayload(const_cast(object)); + + HeapHandle& heap_handle = page->heap_handle(); + if (V8_LIKELY(heap_handle.is_incremental_marking_in_progress())) { + return SetAndReturnType(params); + } + return SetAndReturnType(params); + } +}; + +template <> +struct WriteBarrierTypeForNonCagedHeapPolicy::ValueModeDispatch< + WriteBarrier::ValueMode::kNoValuePresent> { + template + static V8_INLINE WriteBarrier::Type Get(const void*, const void*, + WriteBarrier::Params& params, + HeapHandleCallback callback) { + if (V8_UNLIKELY(WriteBarrier::IsEnabled())) { + HeapHandle& handle = callback(); + if (V8_LIKELY(handle.is_incremental_marking_in_progress())) { + params.heap = &handle; + return SetAndReturnType(params); + } + } + return WriteBarrier::Type::kNone; + } +}; + +// static +WriteBarrier::Type WriteBarrier::GetWriteBarrierType( + const void* slot, const void* value, WriteBarrier::Params& params) { + return WriteBarrierTypePolicy::Get(slot, value, + params, []() {}); +} + +// static +WriteBarrier::Type WriteBarrier::GetWriteBarrierType( + const void* slot, MemberStorage value, WriteBarrier::Params& params) { + return WriteBarrierTypePolicy::Get(slot, value, + params, []() {}); +} + +// static +template +WriteBarrier::Type WriteBarrier::GetWriteBarrierType( + const void* slot, WriteBarrier::Params& params, + HeapHandleCallback callback) { + return WriteBarrierTypePolicy::Get( + slot, nullptr, params, callback); +} + +// static +WriteBarrier::Type WriteBarrier::GetWriteBarrierType( + const void* value, WriteBarrier::Params& params) { + return WriteBarrierTypePolicy::Get(value, params, + []() {}); +} + +// static +void WriteBarrier::DijkstraMarkingBarrier(const Params& params, + const void* object) { + CheckParams(Type::kMarking, params); +#if defined(CPPGC_CAGED_HEAP) + // Caged heap already filters out sentinels. + DijkstraMarkingBarrierSlow(object); +#else // !CPPGC_CAGED_HEAP + DijkstraMarkingBarrierSlowWithSentinelCheck(object); +#endif // !CPPGC_CAGED_HEAP +} + +// static +void WriteBarrier::DijkstraMarkingBarrierRange(const Params& params, + const void* first_element, + size_t element_size, + size_t number_of_elements, + TraceCallback trace_callback) { + CheckParams(Type::kMarking, params); + DijkstraMarkingBarrierRangeSlow(*params.heap, first_element, element_size, + number_of_elements, trace_callback); +} + +// static +void WriteBarrier::SteeleMarkingBarrier(const Params& params, + const void* object) { + CheckParams(Type::kMarking, params); +#if defined(CPPGC_CAGED_HEAP) + // Caged heap already filters out sentinels. + SteeleMarkingBarrierSlow(object); +#else // !CPPGC_CAGED_HEAP + SteeleMarkingBarrierSlowWithSentinelCheck(object); +#endif // !CPPGC_CAGED_HEAP +} + +#if defined(CPPGC_YOUNG_GENERATION) + +// static +template +void WriteBarrier::GenerationalBarrier(const Params& params, const void* slot) { + CheckParams(Type::kGenerational, params); + + const CagedHeapLocalData& local_data = CagedHeapLocalData::Get(); + const AgeTable& age_table = local_data.age_table; + + // Bail out if the slot (precise or imprecise) is in young generation. + if (V8_LIKELY(age_table.GetAge(params.slot_offset) == AgeTable::Age::kYoung)) + return; + + // Dispatch between different types of barriers. + // TODO(chromium:1029379): Consider reload local_data in the slow path to + // reduce register pressure. + if constexpr (type == GenerationalBarrierType::kPreciseSlot) { + GenerationalBarrierSlow(local_data, age_table, slot, params.value_offset, + params.heap); + } else if constexpr (type == + GenerationalBarrierType::kPreciseUncompressedSlot) { + GenerationalBarrierForUncompressedSlotSlow( + local_data, age_table, slot, params.value_offset, params.heap); + } else { + GenerationalBarrierForSourceObjectSlow(local_data, slot, params.heap); + } +} + +#endif // !CPPGC_YOUNG_GENERATION + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_INTERNAL_WRITE_BARRIER_H_ diff --git a/deps/include/cppgc/liveness-broker.h b/deps/include/cppgc/liveness-broker.h new file mode 100755 index 0000000..2c94f1c --- /dev/null +++ b/deps/include/cppgc/liveness-broker.h @@ -0,0 +1,78 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_LIVENESS_BROKER_H_ +#define INCLUDE_CPPGC_LIVENESS_BROKER_H_ + +#include "cppgc/heap.h" +#include "cppgc/member.h" +#include "cppgc/sentinel-pointer.h" +#include "cppgc/trace-trait.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +namespace internal { +class LivenessBrokerFactory; +} // namespace internal + +/** + * The broker is passed to weak callbacks to allow (temporarily) querying + * the liveness state of an object. References to non-live objects must be + * cleared when `IsHeapObjectAlive()` returns false. + * + * \code + * class GCedWithCustomWeakCallback final + * : public GarbageCollected { + * public: + * UntracedMember bar; + * + * void CustomWeakCallbackMethod(const LivenessBroker& broker) { + * if (!broker.IsHeapObjectAlive(bar)) + * bar = nullptr; + * } + * + * void Trace(cppgc::Visitor* visitor) const { + * visitor->RegisterWeakCallbackMethod< + * GCedWithCustomWeakCallback, + * &GCedWithCustomWeakCallback::CustomWeakCallbackMethod>(this); + * } + * }; + * \endcode + */ +class V8_EXPORT LivenessBroker final { + public: + template + bool IsHeapObjectAlive(const T* object) const { + // - nullptr objects are considered alive to allow weakness to be used from + // stack while running into a conservative GC. Treating nullptr as dead + // would mean that e.g. custom collections could not be strongified on + // stack. + // - Sentinel pointers are also preserved in weakness and not cleared. + return !object || object == kSentinelPointer || + IsHeapObjectAliveImpl( + TraceTrait::GetTraceDescriptor(object).base_object_payload); + } + + template + bool IsHeapObjectAlive(const WeakMember& weak_member) const { + return IsHeapObjectAlive(weak_member.Get()); + } + + template + bool IsHeapObjectAlive(const UntracedMember& untraced_member) const { + return IsHeapObjectAlive(untraced_member.Get()); + } + + private: + LivenessBroker() = default; + + bool IsHeapObjectAliveImpl(const void*) const; + + friend class internal::LivenessBrokerFactory; +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_LIVENESS_BROKER_H_ diff --git a/deps/include/cppgc/macros.h b/deps/include/cppgc/macros.h new file mode 100755 index 0000000..030f397 --- /dev/null +++ b/deps/include/cppgc/macros.h @@ -0,0 +1,26 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_MACROS_H_ +#define INCLUDE_CPPGC_MACROS_H_ + +#include + +#include "cppgc/internal/compiler-specific.h" + +namespace cppgc { + +// Use if the object is only stack allocated. +#define CPPGC_STACK_ALLOCATED() \ + public: \ + using IsStackAllocatedTypeMarker CPPGC_UNUSED = int; \ + \ + private: \ + void* operator new(size_t) = delete; \ + void* operator new(size_t, void*) = delete; \ + static_assert(true, "Force semicolon.") + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_MACROS_H_ diff --git a/deps/include/cppgc/member.h b/deps/include/cppgc/member.h new file mode 100755 index 0000000..9bc3836 --- /dev/null +++ b/deps/include/cppgc/member.h @@ -0,0 +1,566 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_MEMBER_H_ +#define INCLUDE_CPPGC_MEMBER_H_ + +#include +#include +#include + +#include "cppgc/internal/api-constants.h" +#include "cppgc/internal/member-storage.h" +#include "cppgc/internal/pointer-policies.h" +#include "cppgc/sentinel-pointer.h" +#include "cppgc/type-traits.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +namespace subtle { +class HeapConsistency; +} // namespace subtle + +class Visitor; + +namespace internal { + +// MemberBase always refers to the object as const object and defers to +// BasicMember on casting to the right type as needed. +class V8_TRIVIAL_ABI MemberBase { + public: +#if defined(CPPGC_POINTER_COMPRESSION) + using RawStorage = CompressedPointer; +#else // !defined(CPPGC_POINTER_COMPRESSION) + using RawStorage = RawPointer; +#endif // !defined(CPPGC_POINTER_COMPRESSION) + protected: + struct AtomicInitializerTag {}; + + V8_INLINE MemberBase() = default; + V8_INLINE explicit MemberBase(const void* value) : raw_(value) {} + V8_INLINE MemberBase(const void* value, AtomicInitializerTag) { + SetRawAtomic(value); + } + + V8_INLINE explicit MemberBase(RawStorage raw) : raw_(raw) {} + V8_INLINE explicit MemberBase(std::nullptr_t) : raw_(nullptr) {} + V8_INLINE explicit MemberBase(SentinelPointer s) : raw_(s) {} + + V8_INLINE const void** GetRawSlot() const { + return reinterpret_cast(const_cast(this)); + } + V8_INLINE const void* GetRaw() const { return raw_.Load(); } + V8_INLINE void SetRaw(void* value) { raw_.Store(value); } + + V8_INLINE const void* GetRawAtomic() const { return raw_.LoadAtomic(); } + V8_INLINE void SetRawAtomic(const void* value) { raw_.StoreAtomic(value); } + + V8_INLINE RawStorage GetRawStorage() const { return raw_; } + V8_INLINE void SetRawStorageAtomic(RawStorage other) { + reinterpret_cast&>(raw_).store( + other, std::memory_order_relaxed); + } + + V8_INLINE bool IsCleared() const { return raw_.IsCleared(); } + + V8_INLINE void ClearFromGC() const { raw_.Clear(); } + + private: + friend class MemberDebugHelper; + + mutable RawStorage raw_; +}; + +// The basic class from which all Member classes are 'generated'. +template +class V8_TRIVIAL_ABI BasicMember final : private MemberBase, + private CheckingPolicy { + public: + using PointeeType = T; + + V8_INLINE constexpr BasicMember() = default; + V8_INLINE constexpr BasicMember(std::nullptr_t) {} // NOLINT + V8_INLINE BasicMember(SentinelPointer s) : MemberBase(s) {} // NOLINT + V8_INLINE BasicMember(T* raw) : MemberBase(raw) { // NOLINT + InitializingWriteBarrier(raw); + this->CheckPointer(Get()); + } + V8_INLINE BasicMember(T& raw) // NOLINT + : BasicMember(&raw) {} + + // Atomic ctor. Using the AtomicInitializerTag forces BasicMember to + // initialize using atomic assignments. This is required for preventing + // data races with concurrent marking. + using AtomicInitializerTag = MemberBase::AtomicInitializerTag; + V8_INLINE BasicMember(std::nullptr_t, AtomicInitializerTag atomic) + : MemberBase(nullptr, atomic) {} + V8_INLINE BasicMember(SentinelPointer s, AtomicInitializerTag atomic) + : MemberBase(s, atomic) {} + V8_INLINE BasicMember(T* raw, AtomicInitializerTag atomic) + : MemberBase(raw, atomic) { + InitializingWriteBarrier(raw); + this->CheckPointer(Get()); + } + V8_INLINE BasicMember(T& raw, AtomicInitializerTag atomic) + : BasicMember(&raw, atomic) {} + + // Copy ctor. + V8_INLINE BasicMember(const BasicMember& other) + : BasicMember(other.GetRawStorage()) {} + + // Heterogeneous copy constructors. When the source pointer have a different + // type, perform a compress-decompress round, because the source pointer may + // need to be adjusted. + template >* = nullptr> + V8_INLINE BasicMember( // NOLINT + const BasicMember& other) + : BasicMember(other.GetRawStorage()) {} + + template >* = nullptr> + V8_INLINE BasicMember( // NOLINT + const BasicMember& other) + : BasicMember(other.Get()) {} + + // Move ctor. + V8_INLINE BasicMember(BasicMember&& other) noexcept + : BasicMember(other.GetRawStorage()) { + other.Clear(); + } + + // Heterogeneous move constructors. When the source pointer have a different + // type, perform a compress-decompress round, because the source pointer may + // need to be adjusted. + template >* = nullptr> + V8_INLINE BasicMember(BasicMember&& other) noexcept + : BasicMember(other.GetRawStorage()) { + other.Clear(); + } + + template >* = nullptr> + V8_INLINE BasicMember(BasicMember&& other) noexcept + : BasicMember(other.Get()) { + other.Clear(); + } + + // Construction from Persistent. + template ::value>> + V8_INLINE BasicMember(const BasicPersistent& p) + : BasicMember(p.Get()) {} + + // Copy assignment. + V8_INLINE BasicMember& operator=(const BasicMember& other) { + return operator=(other.GetRawStorage()); + } + + // Heterogeneous copy assignment. When the source pointer have a different + // type, perform a compress-decompress round, because the source pointer may + // need to be adjusted. + template + V8_INLINE BasicMember& operator=( + const BasicMember& other) { + if constexpr (internal::IsDecayedSameV) { + return operator=(other.GetRawStorage()); + } else { + static_assert(internal::IsStrictlyBaseOfV); + return operator=(other.Get()); + } + } + + // Move assignment. + V8_INLINE BasicMember& operator=(BasicMember&& other) noexcept { + operator=(other.GetRawStorage()); + other.Clear(); + return *this; + } + + // Heterogeneous move assignment. When the source pointer have a different + // type, perform a compress-decompress round, because the source pointer may + // need to be adjusted. + template + V8_INLINE BasicMember& operator=( + BasicMember&& other) noexcept { + if constexpr (internal::IsDecayedSameV) { + operator=(other.GetRawStorage()); + } else { + static_assert(internal::IsStrictlyBaseOfV); + operator=(other.Get()); + } + other.Clear(); + return *this; + } + + // Assignment from Persistent. + template ::value>> + V8_INLINE BasicMember& operator=( + const BasicPersistent& + other) { + return operator=(other.Get()); + } + + V8_INLINE BasicMember& operator=(T* other) { + SetRawAtomic(other); + AssigningWriteBarrier(other); + this->CheckPointer(Get()); + return *this; + } + + V8_INLINE BasicMember& operator=(std::nullptr_t) { + Clear(); + return *this; + } + V8_INLINE BasicMember& operator=(SentinelPointer s) { + SetRawAtomic(s); + return *this; + } + + template + V8_INLINE void Swap(BasicMember& other) { + auto tmp = GetRawStorage(); + *this = other; + other = tmp; + } + + V8_INLINE explicit operator bool() const { return !IsCleared(); } + V8_INLINE operator T*() const { return Get(); } + V8_INLINE T* operator->() const { return Get(); } + V8_INLINE T& operator*() const { return *Get(); } + + // CFI cast exemption to allow passing SentinelPointer through T* and support + // heterogeneous assignments between different Member and Persistent handles + // based on their actual types. + V8_INLINE V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const { + // Executed by the mutator, hence non atomic load. + // + // The const_cast below removes the constness from MemberBase storage. The + // following static_cast re-adds any constness if specified through the + // user-visible template parameter T. + return static_cast(const_cast(MemberBase::GetRaw())); + } + + V8_INLINE void Clear() { SetRawStorageAtomic(RawStorage{}); } + + V8_INLINE T* Release() { + T* result = Get(); + Clear(); + return result; + } + + V8_INLINE const T** GetSlotForTesting() const { + return reinterpret_cast(GetRawSlot()); + } + + V8_INLINE RawStorage GetRawStorage() const { + return MemberBase::GetRawStorage(); + } + + private: + V8_INLINE explicit BasicMember(RawStorage raw) : MemberBase(raw) { + InitializingWriteBarrier(Get()); + this->CheckPointer(Get()); + } + + V8_INLINE BasicMember& operator=(RawStorage other) { + SetRawStorageAtomic(other); + AssigningWriteBarrier(); + this->CheckPointer(Get()); + return *this; + } + + V8_INLINE const T* GetRawAtomic() const { + return static_cast(MemberBase::GetRawAtomic()); + } + + V8_INLINE void InitializingWriteBarrier(T* value) const { + WriteBarrierPolicy::InitializingBarrier(GetRawSlot(), value); + } + V8_INLINE void AssigningWriteBarrier(T* value) const { + WriteBarrierPolicy::AssigningBarrier(GetRawSlot(), value); + } + V8_INLINE void AssigningWriteBarrier() const { + WriteBarrierPolicy::AssigningBarrier(GetRawSlot(), GetRawStorage()); + } + + V8_INLINE void ClearFromGC() const { MemberBase::ClearFromGC(); } + + V8_INLINE T* GetFromGC() const { return Get(); } + + friend class cppgc::subtle::HeapConsistency; + friend class cppgc::Visitor; + template + friend struct cppgc::TraceTrait; + template + friend class BasicMember; +}; + +// Member equality operators. +template +V8_INLINE bool operator==( + const BasicMember& + member1, + const BasicMember& + member2) { + if constexpr (internal::IsDecayedSameV) { + // Check compressed pointers if types are the same. + return member1.GetRawStorage() == member2.GetRawStorage(); + } else { + static_assert(internal::IsStrictlyBaseOfV || + internal::IsStrictlyBaseOfV); + // Otherwise, check decompressed pointers. + return member1.Get() == member2.Get(); + } +} + +template +V8_INLINE bool operator!=( + const BasicMember& + member1, + const BasicMember& + member2) { + return !(member1 == member2); +} + +// Equality with raw pointers. +template +V8_INLINE bool operator==(const BasicMember& member, + U* raw) { + // Never allow comparison with erased pointers. + static_assert(!internal::IsDecayedSameV); + + if constexpr (internal::IsDecayedSameV) { + // Check compressed pointers if types are the same. + return member.GetRawStorage() == MemberBase::RawStorage(raw); + } else if constexpr (internal::IsStrictlyBaseOfV) { + // Cast the raw pointer to T, which may adjust the pointer. + return member.GetRawStorage() == + MemberBase::RawStorage(static_cast(raw)); + } else { + // Otherwise, decompressed the member. + return member.Get() == raw; + } +} + +template +V8_INLINE bool operator!=(const BasicMember& member, + U* raw) { + return !(member == raw); +} + +template +V8_INLINE bool operator==(T* raw, + const BasicMember& member) { + return member == raw; +} + +template +V8_INLINE bool operator!=(T* raw, + const BasicMember& member) { + return !(raw == member); +} + +// Equality with sentinel. +template +V8_INLINE bool operator==(const BasicMember& member, + SentinelPointer) { + return member.GetRawStorage().IsSentinel(); +} + +template +V8_INLINE bool operator!=(const BasicMember& member, + SentinelPointer s) { + return !(member == s); +} + +template +V8_INLINE bool operator==(SentinelPointer s, + const BasicMember& member) { + return member == s; +} + +template +V8_INLINE bool operator!=(SentinelPointer s, + const BasicMember& member) { + return !(s == member); +} + +// Equality with nullptr. +template +V8_INLINE bool operator==(const BasicMember& member, + std::nullptr_t) { + return !static_cast(member); +} + +template +V8_INLINE bool operator!=(const BasicMember& member, + std::nullptr_t n) { + return !(member == n); +} + +template +V8_INLINE bool operator==(std::nullptr_t n, + const BasicMember& member) { + return member == n; +} + +template +V8_INLINE bool operator!=(std::nullptr_t n, + const BasicMember& member) { + return !(n == member); +} + +// Relational operators. +template +V8_INLINE bool operator<( + const BasicMember& + member1, + const BasicMember& + member2) { + static_assert( + internal::IsDecayedSameV, + "Comparison works only for same pointer type modulo cv-qualifiers"); + return member1.GetRawStorage() < member2.GetRawStorage(); +} + +template +V8_INLINE bool operator<=( + const BasicMember& + member1, + const BasicMember& + member2) { + static_assert( + internal::IsDecayedSameV, + "Comparison works only for same pointer type modulo cv-qualifiers"); + return member1.GetRawStorage() <= member2.GetRawStorage(); +} + +template +V8_INLINE bool operator>( + const BasicMember& + member1, + const BasicMember& + member2) { + static_assert( + internal::IsDecayedSameV, + "Comparison works only for same pointer type modulo cv-qualifiers"); + return member1.GetRawStorage() > member2.GetRawStorage(); +} + +template +V8_INLINE bool operator>=( + const BasicMember& + member1, + const BasicMember& + member2) { + static_assert( + internal::IsDecayedSameV, + "Comparison works only for same pointer type modulo cv-qualifiers"); + return member1.GetRawStorage() >= member2.GetRawStorage(); +} + +template +struct IsWeak< + internal::BasicMember> + : std::true_type {}; + +} // namespace internal + +/** + * Members are used in classes to contain strong pointers to other garbage + * collected objects. All Member fields of a class must be traced in the class' + * trace method. + */ +template +using Member = internal::BasicMember; + +/** + * WeakMember is similar to Member in that it is used to point to other garbage + * collected objects. However instead of creating a strong pointer to the + * object, the WeakMember creates a weak pointer, which does not keep the + * pointee alive. Hence if all pointers to to a heap allocated object are weak + * the object will be garbage collected. At the time of GC the weak pointers + * will automatically be set to null. + */ +template +using WeakMember = internal::BasicMember; + +/** + * UntracedMember is a pointer to an on-heap object that is not traced for some + * reason. Do not use this unless you know what you are doing. Keeping raw + * pointers to on-heap objects is prohibited unless used from stack. Pointee + * must be kept alive through other means. + */ +template +using UntracedMember = internal::BasicMember; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_MEMBER_H_ diff --git a/deps/include/cppgc/name-provider.h b/deps/include/cppgc/name-provider.h new file mode 100755 index 0000000..216f609 --- /dev/null +++ b/deps/include/cppgc/name-provider.h @@ -0,0 +1,65 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_NAME_PROVIDER_H_ +#define INCLUDE_CPPGC_NAME_PROVIDER_H_ + +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +/** + * NameProvider allows for providing a human-readable name for garbage-collected + * objects. + * + * There's two cases of names to distinguish: + * a. Explicitly specified names via using NameProvider. Such names are always + * preserved in the system. + * b. Internal names that Oilpan infers from a C++ type on the class hierarchy + * of the object. This is not necessarily the type of the actually + * instantiated object. + * + * Depending on the build configuration, Oilpan may hide names, i.e., represent + * them with kHiddenName, of case b. to avoid exposing internal details. + */ +class V8_EXPORT NameProvider { + public: + /** + * Name that is used when hiding internals. + */ + static constexpr const char kHiddenName[] = "InternalNode"; + + /** + * Name that is used in case compiler support is missing for composing a name + * from C++ types. + */ + static constexpr const char kNoNameDeducible[] = ""; + + /** + * Indicating whether the build supports extracting C++ names as object names. + * + * @returns true if C++ names should be hidden and represented by kHiddenName. + */ + static constexpr bool SupportsCppClassNamesAsObjectNames() { +#if CPPGC_SUPPORTS_OBJECT_NAMES + return true; +#else // !CPPGC_SUPPORTS_OBJECT_NAMES + return false; +#endif // !CPPGC_SUPPORTS_OBJECT_NAMES + } + + virtual ~NameProvider() = default; + + /** + * Specifies a name for the garbage-collected object. Such names will never + * be hidden, as they are explicitly specified by the user of this API. + * + * @returns a human readable name for the object. + */ + virtual const char* GetHumanReadableName() const = 0; +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_NAME_PROVIDER_H_ diff --git a/deps/include/cppgc/object-size-trait.h b/deps/include/cppgc/object-size-trait.h new file mode 100755 index 0000000..3579559 --- /dev/null +++ b/deps/include/cppgc/object-size-trait.h @@ -0,0 +1,58 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_OBJECT_SIZE_TRAIT_H_ +#define INCLUDE_CPPGC_OBJECT_SIZE_TRAIT_H_ + +#include + +#include "cppgc/type-traits.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +namespace internal { + +struct V8_EXPORT BaseObjectSizeTrait { + protected: + static size_t GetObjectSizeForGarbageCollected(const void*); + static size_t GetObjectSizeForGarbageCollectedMixin(const void*); +}; + +} // namespace internal + +namespace subtle { + +/** + * Trait specifying how to get the size of an object that was allocated using + * `MakeGarbageCollected()`. Also supports querying the size with an inner + * pointer to a mixin. + */ +template > +struct ObjectSizeTrait; + +template +struct ObjectSizeTrait : cppgc::internal::BaseObjectSizeTrait { + static_assert(sizeof(T), "T must be fully defined"); + static_assert(IsGarbageCollectedTypeV, + "T must be of type GarbageCollected or GarbageCollectedMixin"); + + static size_t GetSize(const T& object) { + return GetObjectSizeForGarbageCollected(&object); + } +}; + +template +struct ObjectSizeTrait : cppgc::internal::BaseObjectSizeTrait { + static_assert(sizeof(T), "T must be fully defined"); + + static size_t GetSize(const T& object) { + return GetObjectSizeForGarbageCollectedMixin(&object); + } +}; + +} // namespace subtle +} // namespace cppgc + +#endif // INCLUDE_CPPGC_OBJECT_SIZE_TRAIT_H_ diff --git a/deps/include/cppgc/persistent.h b/deps/include/cppgc/persistent.h new file mode 100755 index 0000000..3a66ccc --- /dev/null +++ b/deps/include/cppgc/persistent.h @@ -0,0 +1,366 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_PERSISTENT_H_ +#define INCLUDE_CPPGC_PERSISTENT_H_ + +#include + +#include "cppgc/internal/persistent-node.h" +#include "cppgc/internal/pointer-policies.h" +#include "cppgc/sentinel-pointer.h" +#include "cppgc/source-location.h" +#include "cppgc/type-traits.h" +#include "cppgc/visitor.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { +namespace internal { + +// PersistentBase always refers to the object as const object and defers to +// BasicPersistent on casting to the right type as needed. +class PersistentBase { + protected: + PersistentBase() = default; + explicit PersistentBase(const void* raw) : raw_(raw) {} + + const void* GetValue() const { return raw_; } + void SetValue(const void* value) { raw_ = value; } + + PersistentNode* GetNode() const { return node_; } + void SetNode(PersistentNode* node) { node_ = node; } + + // Performs a shallow clear which assumes that internal persistent nodes are + // destroyed elsewhere. + void ClearFromGC() const { + raw_ = nullptr; + node_ = nullptr; + } + + protected: + mutable const void* raw_ = nullptr; + mutable PersistentNode* node_ = nullptr; + + friend class PersistentRegionBase; +}; + +// The basic class from which all Persistent classes are generated. +template +class BasicPersistent final : public PersistentBase, + public LocationPolicy, + private WeaknessPolicy, + private CheckingPolicy { + public: + using typename WeaknessPolicy::IsStrongPersistent; + using PointeeType = T; + + // Null-state/sentinel constructors. + BasicPersistent( // NOLINT + const SourceLocation& loc = SourceLocation::Current()) + : LocationPolicy(loc) {} + + BasicPersistent(std::nullptr_t, // NOLINT + const SourceLocation& loc = SourceLocation::Current()) + : LocationPolicy(loc) {} + + BasicPersistent( // NOLINT + SentinelPointer s, const SourceLocation& loc = SourceLocation::Current()) + : PersistentBase(s), LocationPolicy(loc) {} + + // Raw value constructors. + BasicPersistent(T* raw, // NOLINT + const SourceLocation& loc = SourceLocation::Current()) + : PersistentBase(raw), LocationPolicy(loc) { + if (!IsValid()) return; + SetNode(WeaknessPolicy::GetPersistentRegion(GetValue()) + .AllocateNode(this, &TraceAsRoot)); + this->CheckPointer(Get()); + } + + BasicPersistent(T& raw, // NOLINT + const SourceLocation& loc = SourceLocation::Current()) + : BasicPersistent(&raw, loc) {} + + // Copy ctor. + BasicPersistent(const BasicPersistent& other, + const SourceLocation& loc = SourceLocation::Current()) + : BasicPersistent(other.Get(), loc) {} + + // Heterogeneous ctor. + template ::value>> + BasicPersistent( + const BasicPersistent& other, + const SourceLocation& loc = SourceLocation::Current()) + : BasicPersistent(other.Get(), loc) {} + + // Move ctor. The heterogeneous move ctor is not supported since e.g. + // persistent can't reuse persistent node from weak persistent. + BasicPersistent( + BasicPersistent&& other, + const SourceLocation& loc = SourceLocation::Current()) noexcept + : PersistentBase(std::move(other)), LocationPolicy(std::move(other)) { + if (!IsValid()) return; + GetNode()->UpdateOwner(this); + other.SetValue(nullptr); + other.SetNode(nullptr); + this->CheckPointer(Get()); + } + + // Constructor from member. + template ::value>> + BasicPersistent( + const internal::BasicMember& member, + const SourceLocation& loc = SourceLocation::Current()) + : BasicPersistent(member.Get(), loc) {} + + ~BasicPersistent() { Clear(); } + + // Copy assignment. + BasicPersistent& operator=(const BasicPersistent& other) { + return operator=(other.Get()); + } + + template ::value>> + BasicPersistent& operator=( + const BasicPersistent& other) { + return operator=(other.Get()); + } + + // Move assignment. + BasicPersistent& operator=(BasicPersistent&& other) noexcept { + if (this == &other) return *this; + Clear(); + PersistentBase::operator=(std::move(other)); + LocationPolicy::operator=(std::move(other)); + if (!IsValid()) return *this; + GetNode()->UpdateOwner(this); + other.SetValue(nullptr); + other.SetNode(nullptr); + this->CheckPointer(Get()); + return *this; + } + + // Assignment from member. + template ::value>> + BasicPersistent& operator=( + const internal::BasicMember& member) { + return operator=(member.Get()); + } + + BasicPersistent& operator=(T* other) { + Assign(other); + return *this; + } + + BasicPersistent& operator=(std::nullptr_t) { + Clear(); + return *this; + } + + BasicPersistent& operator=(SentinelPointer s) { + Assign(s); + return *this; + } + + explicit operator bool() const { return Get(); } + operator T*() const { return Get(); } + T* operator->() const { return Get(); } + T& operator*() const { return *Get(); } + + // CFI cast exemption to allow passing SentinelPointer through T* and support + // heterogeneous assignments between different Member and Persistent handles + // based on their actual types. + V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") T* Get() const { + // The const_cast below removes the constness from PersistentBase storage. + // The following static_cast re-adds any constness if specified through the + // user-visible template parameter T. + return static_cast(const_cast(GetValue())); + } + + void Clear() { + // Simplified version of `Assign()` to allow calling without a complete type + // `T`. + if (IsValid()) { + WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode()); + SetNode(nullptr); + } + SetValue(nullptr); + } + + T* Release() { + T* result = Get(); + Clear(); + return result; + } + + template + BasicPersistent + To() const { + return BasicPersistent(static_cast(Get())); + } + + private: + static void TraceAsRoot(RootVisitor& root_visitor, const void* ptr) { + root_visitor.Trace(*static_cast(ptr)); + } + + bool IsValid() const { + // Ideally, handling kSentinelPointer would be done by the embedder. On the + // other hand, having Persistent aware of it is beneficial since no node + // gets wasted. + return GetValue() != nullptr && GetValue() != kSentinelPointer; + } + + void Assign(T* ptr) { + if (IsValid()) { + if (ptr && ptr != kSentinelPointer) { + // Simply assign the pointer reusing the existing node. + SetValue(ptr); + this->CheckPointer(ptr); + return; + } + WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode()); + SetNode(nullptr); + } + SetValue(ptr); + if (!IsValid()) return; + SetNode(WeaknessPolicy::GetPersistentRegion(GetValue()) + .AllocateNode(this, &TraceAsRoot)); + this->CheckPointer(Get()); + } + + void ClearFromGC() const { + if (IsValid()) { + WeaknessPolicy::GetPersistentRegion(GetValue()).FreeNode(GetNode()); + PersistentBase::ClearFromGC(); + } + } + + // Set Get() for details. + V8_CLANG_NO_SANITIZE("cfi-unrelated-cast") + T* GetFromGC() const { + return static_cast(const_cast(GetValue())); + } + + friend class internal::RootVisitor; +}; + +template +bool operator==(const BasicPersistent& p1, + const BasicPersistent& p2) { + return p1.Get() == p2.Get(); +} + +template +bool operator!=(const BasicPersistent& p1, + const BasicPersistent& p2) { + return !(p1 == p2); +} + +template +bool operator==( + const BasicPersistent& + p, + const BasicMember& m) { + return p.Get() == m.Get(); +} + +template +bool operator!=( + const BasicPersistent& + p, + const BasicMember& m) { + return !(p == m); +} + +template +bool operator==( + const BasicMember& m, + const BasicPersistent& + p) { + return m.Get() == p.Get(); +} + +template +bool operator!=( + const BasicMember& m, + const BasicPersistent& + p) { + return !(m == p); +} + +template +struct IsWeak> : std::true_type {}; +} // namespace internal + +/** + * Persistent is a way to create a strong pointer from an off-heap object to + * another on-heap object. As long as the Persistent handle is alive the GC will + * keep the object pointed to alive. The Persistent handle is always a GC root + * from the point of view of the GC. Persistent must be constructed and + * destructed in the same thread. + */ +template +using Persistent = + internal::BasicPersistent; + +/** + * WeakPersistent is a way to create a weak pointer from an off-heap object to + * an on-heap object. The pointer is automatically cleared when the pointee gets + * collected. WeakPersistent must be constructed and destructed in the same + * thread. + */ +template +using WeakPersistent = + internal::BasicPersistent; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_PERSISTENT_H_ diff --git a/deps/include/cppgc/platform.h b/deps/include/cppgc/platform.h new file mode 100755 index 0000000..5a0a40e --- /dev/null +++ b/deps/include/cppgc/platform.h @@ -0,0 +1,158 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_PLATFORM_H_ +#define INCLUDE_CPPGC_PLATFORM_H_ + +#include + +#include "cppgc/source-location.h" +#include "v8-platform.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +// TODO(v8:10346): Create separate includes for concepts that are not +// V8-specific. +using IdleTask = v8::IdleTask; +using JobHandle = v8::JobHandle; +using JobDelegate = v8::JobDelegate; +using JobTask = v8::JobTask; +using PageAllocator = v8::PageAllocator; +using Task = v8::Task; +using TaskPriority = v8::TaskPriority; +using TaskRunner = v8::TaskRunner; +using TracingController = v8::TracingController; + +/** + * Platform interface used by Heap. Contains allocators and executors. + */ +class V8_EXPORT Platform { + public: + virtual ~Platform() = default; + + /** + * \returns the allocator used by cppgc to allocate its heap and various + * support structures. Returning nullptr results in using the `PageAllocator` + * provided by `cppgc::InitializeProcess()` instead. + */ + virtual PageAllocator* GetPageAllocator() = 0; + + /** + * Monotonically increasing time in seconds from an arbitrary fixed point in + * the past. This function is expected to return at least + * millisecond-precision values. For this reason, + * it is recommended that the fixed point be no further in the past than + * the epoch. + **/ + virtual double MonotonicallyIncreasingTime() = 0; + + /** + * Foreground task runner that should be used by a Heap. + */ + virtual std::shared_ptr GetForegroundTaskRunner() { + return nullptr; + } + + /** + * Posts `job_task` to run in parallel. Returns a `JobHandle` associated with + * the `Job`, which can be joined or canceled. + * This avoids degenerate cases: + * - Calling `CallOnWorkerThread()` for each work item, causing significant + * overhead. + * - Fixed number of `CallOnWorkerThread()` calls that split the work and + * might run for a long time. This is problematic when many components post + * "num cores" tasks and all expect to use all the cores. In these cases, + * the scheduler lacks context to be fair to multiple same-priority requests + * and/or ability to request lower priority work to yield when high priority + * work comes in. + * A canonical implementation of `job_task` looks like: + * \code + * class MyJobTask : public JobTask { + * public: + * MyJobTask(...) : worker_queue_(...) {} + * // JobTask implementation. + * void Run(JobDelegate* delegate) override { + * while (!delegate->ShouldYield()) { + * // Smallest unit of work. + * auto work_item = worker_queue_.TakeWorkItem(); // Thread safe. + * if (!work_item) return; + * ProcessWork(work_item); + * } + * } + * + * size_t GetMaxConcurrency() const override { + * return worker_queue_.GetSize(); // Thread safe. + * } + * }; + * + * // ... + * auto handle = PostJob(TaskPriority::kUserVisible, + * std::make_unique(...)); + * handle->Join(); + * \endcode + * + * `PostJob()` and methods of the returned JobHandle/JobDelegate, must never + * be called while holding a lock that could be acquired by `JobTask::Run()` + * or `JobTask::GetMaxConcurrency()` -- that could result in a deadlock. This + * is because (1) `JobTask::GetMaxConcurrency()` may be invoked while holding + * internal lock (A), hence `JobTask::GetMaxConcurrency()` can only use a lock + * (B) if that lock is *never* held while calling back into `JobHandle` from + * any thread (A=>B/B=>A deadlock) and (2) `JobTask::Run()` or + * `JobTask::GetMaxConcurrency()` may be invoked synchronously from + * `JobHandle` (B=>JobHandle::foo=>B deadlock). + * + * A sufficient `PostJob()` implementation that uses the default Job provided + * in libplatform looks like: + * \code + * std::unique_ptr PostJob( + * TaskPriority priority, std::unique_ptr job_task) override { + * return std::make_unique( + * std::make_shared( + * this, std::move(job_task), kNumThreads)); + * } + * \endcode + */ + virtual std::unique_ptr PostJob( + TaskPriority priority, std::unique_ptr job_task) { + return nullptr; + } + + /** + * Returns an instance of a `TracingController`. This must be non-nullptr. The + * default implementation returns an empty `TracingController` that consumes + * trace data without effect. + */ + virtual TracingController* GetTracingController(); +}; + +/** + * Process-global initialization of the garbage collector. Must be called before + * creating a Heap. + * + * Can be called multiple times when paired with `ShutdownProcess()`. + * + * \param page_allocator The allocator used for maintaining meta data. Must stay + * always alive and not change between multiple calls to InitializeProcess. If + * no allocator is provided, a default internal version will be used. + */ +V8_EXPORT void InitializeProcess(PageAllocator* page_allocator = nullptr); + +/** + * Must be called after destroying the last used heap. Some process-global + * metadata may not be returned and reused upon a subsequent + * `InitializeProcess()` call. + */ +V8_EXPORT void ShutdownProcess(); + +namespace internal { + +V8_EXPORT void Fatal(const std::string& reason = std::string(), + const SourceLocation& = SourceLocation::Current()); + +} // namespace internal + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_PLATFORM_H_ diff --git a/deps/include/cppgc/prefinalizer.h b/deps/include/cppgc/prefinalizer.h new file mode 100755 index 0000000..51f2eac --- /dev/null +++ b/deps/include/cppgc/prefinalizer.h @@ -0,0 +1,75 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_PREFINALIZER_H_ +#define INCLUDE_CPPGC_PREFINALIZER_H_ + +#include "cppgc/internal/compiler-specific.h" +#include "cppgc/liveness-broker.h" + +namespace cppgc { + +namespace internal { + +class V8_EXPORT PrefinalizerRegistration final { + public: + using Callback = bool (*)(const cppgc::LivenessBroker&, void*); + + PrefinalizerRegistration(void*, Callback); + + void* operator new(size_t, void* location) = delete; + void* operator new(size_t) = delete; +}; + +} // namespace internal + +/** + * Macro must be used in the private section of `Class` and registers a + * prefinalization callback `void Class::PreFinalizer()`. The callback is + * invoked on garbage collection after the collector has found an object to be + * dead. + * + * Callback properties: + * - The callback is invoked before a possible destructor for the corresponding + * object. + * - The callback may access the whole object graph, irrespective of whether + * objects are considered dead or alive. + * - The callback is invoked on the same thread as the object was created on. + * + * Example: + * \code + * class WithPrefinalizer : public GarbageCollected { + * CPPGC_USING_PRE_FINALIZER(WithPrefinalizer, Dispose); + * + * public: + * void Trace(Visitor*) const {} + * void Dispose() { prefinalizer_called = true; } + * ~WithPrefinalizer() { + * // prefinalizer_called == true + * } + * private: + * bool prefinalizer_called = false; + * }; + * \endcode + */ +#define CPPGC_USING_PRE_FINALIZER(Class, PreFinalizer) \ + public: \ + static bool InvokePreFinalizer(const cppgc::LivenessBroker& liveness_broker, \ + void* object) { \ + static_assert(cppgc::IsGarbageCollectedOrMixinTypeV, \ + "Only garbage collected objects can have prefinalizers"); \ + Class* self = static_cast(object); \ + if (liveness_broker.IsHeapObjectAlive(self)) return false; \ + self->PreFinalizer(); \ + return true; \ + } \ + \ + private: \ + CPPGC_NO_UNIQUE_ADDRESS cppgc::internal::PrefinalizerRegistration \ + prefinalizer_dummy_{this, Class::InvokePreFinalizer}; \ + static_assert(true, "Force semicolon.") + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_PREFINALIZER_H_ diff --git a/deps/include/cppgc/process-heap-statistics.h b/deps/include/cppgc/process-heap-statistics.h new file mode 100755 index 0000000..774cc92 --- /dev/null +++ b/deps/include/cppgc/process-heap-statistics.h @@ -0,0 +1,36 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_PROCESS_HEAP_STATISTICS_H_ +#define INCLUDE_CPPGC_PROCESS_HEAP_STATISTICS_H_ + +#include +#include + +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { +namespace internal { +class ProcessHeapStatisticsUpdater; +} // namespace internal + +class V8_EXPORT ProcessHeapStatistics final { + public: + static size_t TotalAllocatedObjectSize() { + return total_allocated_object_size_.load(std::memory_order_relaxed); + } + static size_t TotalAllocatedSpace() { + return total_allocated_space_.load(std::memory_order_relaxed); + } + + private: + static std::atomic_size_t total_allocated_space_; + static std::atomic_size_t total_allocated_object_size_; + + friend class internal::ProcessHeapStatisticsUpdater; +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_PROCESS_HEAP_STATISTICS_H_ diff --git a/deps/include/cppgc/sentinel-pointer.h b/deps/include/cppgc/sentinel-pointer.h new file mode 100755 index 0000000..8dbbab0 --- /dev/null +++ b/deps/include/cppgc/sentinel-pointer.h @@ -0,0 +1,32 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_SENTINEL_POINTER_H_ +#define INCLUDE_CPPGC_SENTINEL_POINTER_H_ + +#include + +namespace cppgc { +namespace internal { + +// Special tag type used to denote some sentinel member. The semantics of the +// sentinel is defined by the embedder. +struct SentinelPointer { + static constexpr intptr_t kSentinelValue = 0b10; + template + operator T*() const { + return reinterpret_cast(kSentinelValue); + } + // Hidden friends. + friend bool operator==(SentinelPointer, SentinelPointer) { return true; } + friend bool operator!=(SentinelPointer, SentinelPointer) { return false; } +}; + +} // namespace internal + +constexpr internal::SentinelPointer kSentinelPointer; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_SENTINEL_POINTER_H_ diff --git a/deps/include/cppgc/source-location.h b/deps/include/cppgc/source-location.h new file mode 100755 index 0000000..da5a5ed --- /dev/null +++ b/deps/include/cppgc/source-location.h @@ -0,0 +1,92 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_SOURCE_LOCATION_H_ +#define INCLUDE_CPPGC_SOURCE_LOCATION_H_ + +#include +#include + +#include "v8config.h" // NOLINT(build/include_directory) + +#if defined(__has_builtin) +#define CPPGC_SUPPORTS_SOURCE_LOCATION \ + (__has_builtin(__builtin_FUNCTION) && __has_builtin(__builtin_FILE) && \ + __has_builtin(__builtin_LINE)) // NOLINT +#elif defined(V8_CC_GNU) && __GNUC__ >= 7 +#define CPPGC_SUPPORTS_SOURCE_LOCATION 1 +#elif defined(V8_CC_INTEL) && __ICC >= 1800 +#define CPPGC_SUPPORTS_SOURCE_LOCATION 1 +#else +#define CPPGC_SUPPORTS_SOURCE_LOCATION 0 +#endif + +namespace cppgc { + +/** + * Encapsulates source location information. Mimics C++20's + * `std::source_location`. + */ +class V8_EXPORT SourceLocation final { + public: + /** + * Construct source location information corresponding to the location of the + * call site. + */ +#if CPPGC_SUPPORTS_SOURCE_LOCATION + static constexpr SourceLocation Current( + const char* function = __builtin_FUNCTION(), + const char* file = __builtin_FILE(), size_t line = __builtin_LINE()) { + return SourceLocation(function, file, line); + } +#else + static constexpr SourceLocation Current() { return SourceLocation(); } +#endif // CPPGC_SUPPORTS_SOURCE_LOCATION + + /** + * Constructs unspecified source location information. + */ + constexpr SourceLocation() = default; + + /** + * Returns the name of the function associated with the position represented + * by this object, if any. + * + * \returns the function name as cstring. + */ + constexpr const char* Function() const { return function_; } + + /** + * Returns the name of the current source file represented by this object. + * + * \returns the file name as cstring. + */ + constexpr const char* FileName() const { return file_; } + + /** + * Returns the line number represented by this object. + * + * \returns the line number. + */ + constexpr size_t Line() const { return line_; } + + /** + * Returns a human-readable string representing this object. + * + * \returns a human-readable string representing source location information. + */ + std::string ToString() const; + + private: + constexpr SourceLocation(const char* function, const char* file, size_t line) + : function_(function), file_(file), line_(line) {} + + const char* function_ = nullptr; + const char* file_ = nullptr; + size_t line_ = 0u; +}; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_SOURCE_LOCATION_H_ diff --git a/deps/include/cppgc/testing.h b/deps/include/cppgc/testing.h new file mode 100755 index 0000000..bddd1fc --- /dev/null +++ b/deps/include/cppgc/testing.h @@ -0,0 +1,106 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_TESTING_H_ +#define INCLUDE_CPPGC_TESTING_H_ + +#include "cppgc/common.h" +#include "cppgc/macros.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +class HeapHandle; + +/** + * Namespace contains testing helpers. + */ +namespace testing { + +/** + * Overrides the state of the stack with the provided value. Parameters passed + * to explicit garbage collection calls still take precedence. Must not be + * nested. + * + * This scope is useful to make the garbage collector consider the stack when + * tasks that invoke garbage collection (through the provided platform) contain + * interesting pointers on its stack. + */ +class V8_EXPORT V8_NODISCARD OverrideEmbedderStackStateScope final { + CPPGC_STACK_ALLOCATED(); + + public: + /** + * Constructs a scoped object that automatically enters and leaves the scope. + * + * \param heap_handle The corresponding heap. + */ + explicit OverrideEmbedderStackStateScope(HeapHandle& heap_handle, + EmbedderStackState state); + ~OverrideEmbedderStackStateScope(); + + OverrideEmbedderStackStateScope(const OverrideEmbedderStackStateScope&) = + delete; + OverrideEmbedderStackStateScope& operator=( + const OverrideEmbedderStackStateScope&) = delete; + + private: + HeapHandle& heap_handle_; +}; + +/** + * Testing interface for managed heaps that allows for controlling garbage + * collection timings. Embedders should use this class when testing the + * interaction of their code with incremental/concurrent garbage collection. + */ +class V8_EXPORT StandaloneTestingHeap final { + public: + explicit StandaloneTestingHeap(HeapHandle&); + + /** + * Start an incremental garbage collection. + */ + void StartGarbageCollection(); + + /** + * Perform an incremental step. This will also schedule concurrent steps if + * needed. + * + * \param stack_state The state of the stack during the step. + */ + bool PerformMarkingStep(EmbedderStackState stack_state); + + /** + * Finalize the current garbage collection cycle atomically. + * Assumes that garbage collection is in progress. + * + * \param stack_state The state of the stack for finalizing the garbage + * collection cycle. + */ + void FinalizeGarbageCollection(EmbedderStackState stack_state); + + /** + * Toggle main thread marking on/off. Allows to stress concurrent marking + * (e.g. to better detect data races). + * + * \param should_mark Denotes whether the main thread should contribute to + * marking. Defaults to true. + */ + void ToggleMainThreadMarking(bool should_mark); + + /** + * Force enable compaction for the next garbage collection cycle. + */ + void ForceCompactionForNextGarbageCollection(); + + private: + HeapHandle& heap_handle_; +}; + +V8_EXPORT bool IsHeapObjectOld(void*); + +} // namespace testing +} // namespace cppgc + +#endif // INCLUDE_CPPGC_TESTING_H_ diff --git a/deps/include/cppgc/trace-trait.h b/deps/include/cppgc/trace-trait.h new file mode 100755 index 0000000..694fbfd --- /dev/null +++ b/deps/include/cppgc/trace-trait.h @@ -0,0 +1,120 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_TRACE_TRAIT_H_ +#define INCLUDE_CPPGC_TRACE_TRAIT_H_ + +#include + +#include "cppgc/type-traits.h" +#include "v8config.h" // NOLINT(build/include_directory) + +namespace cppgc { + +class Visitor; + +namespace internal { + +class RootVisitor; + +using TraceRootCallback = void (*)(RootVisitor&, const void* object); + +// Implementation of the default TraceTrait handling GarbageCollected and +// GarbageCollectedMixin. +template ::type>> +struct TraceTraitImpl; + +} // namespace internal + +/** + * Callback for invoking tracing on a given object. + * + * \param visitor The visitor to dispatch to. + * \param object The object to invoke tracing on. + */ +using TraceCallback = void (*)(Visitor* visitor, const void* object); + +/** + * Describes how to trace an object, i.e., how to visit all Oilpan-relevant + * fields of an object. + */ +struct TraceDescriptor { + /** + * Adjusted base pointer, i.e., the pointer to the class inheriting directly + * from GarbageCollected, of the object that is being traced. + */ + const void* base_object_payload; + /** + * Callback for tracing the object. + */ + TraceCallback callback; +}; + +namespace internal { + +struct V8_EXPORT TraceTraitFromInnerAddressImpl { + static TraceDescriptor GetTraceDescriptor(const void* address); +}; + +/** + * Trait specifying how the garbage collector processes an object of type T. + * + * Advanced users may override handling by creating a specialization for their + * type. + */ +template +struct TraceTraitBase { + static_assert(internal::IsTraceableV, "T must have a Trace() method"); + + /** + * Accessor for retrieving a TraceDescriptor to process an object of type T. + * + * \param self The object to be processed. + * \returns a TraceDescriptor to process the object. + */ + static TraceDescriptor GetTraceDescriptor(const void* self) { + return internal::TraceTraitImpl::GetTraceDescriptor( + static_cast(self)); + } + + /** + * Function invoking the tracing for an object of type T. + * + * \param visitor The visitor to dispatch to. + * \param self The object to invoke tracing on. + */ + static void Trace(Visitor* visitor, const void* self) { + static_cast(self)->Trace(visitor); + } +}; + +} // namespace internal + +template +struct TraceTrait : public internal::TraceTraitBase {}; + +namespace internal { + +template +struct TraceTraitImpl { + static_assert(IsGarbageCollectedTypeV, + "T must be of type GarbageCollected or GarbageCollectedMixin"); + static TraceDescriptor GetTraceDescriptor(const void* self) { + return {self, TraceTrait::Trace}; + } +}; + +template +struct TraceTraitImpl { + static TraceDescriptor GetTraceDescriptor(const void* self) { + return internal::TraceTraitFromInnerAddressImpl::GetTraceDescriptor(self); + } +}; + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_TRACE_TRAIT_H_ diff --git a/deps/include/cppgc/type-traits.h b/deps/include/cppgc/type-traits.h new file mode 100755 index 0000000..2f499e6 --- /dev/null +++ b/deps/include/cppgc/type-traits.h @@ -0,0 +1,249 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_TYPE_TRAITS_H_ +#define INCLUDE_CPPGC_TYPE_TRAITS_H_ + +// This file should stay with minimal dependencies to allow embedder to check +// against Oilpan types without including any other parts. +#include +#include + +namespace cppgc { + +class Visitor; + +namespace internal { +template +class BasicMember; +struct DijkstraWriteBarrierPolicy; +struct NoWriteBarrierPolicy; +class StrongMemberTag; +class UntracedMemberTag; +class WeakMemberTag; + +// Not supposed to be specialized by the user. +template +struct IsWeak : std::false_type {}; + +// IsTraceMethodConst is used to verify that all Trace methods are marked as +// const. It is equivalent to IsTraceable but for a non-const object. +template +struct IsTraceMethodConst : std::false_type {}; + +template +struct IsTraceMethodConst().Trace( + std::declval()))>> : std::true_type { +}; + +template +struct IsTraceable : std::false_type { + static_assert(sizeof(T), "T must be fully defined"); +}; + +template +struct IsTraceable< + T, std::void_t().Trace(std::declval()))>> + : std::true_type { + // All Trace methods should be marked as const. If an object of type + // 'T' is traceable then any object of type 'const T' should also + // be traceable. + static_assert(IsTraceMethodConst(), + "Trace methods should be marked as const."); +}; + +template +constexpr bool IsTraceableV = IsTraceable::value; + +template +struct HasGarbageCollectedMixinTypeMarker : std::false_type { + static_assert(sizeof(T), "T must be fully defined"); +}; + +template +struct HasGarbageCollectedMixinTypeMarker< + T, std::void_t< + typename std::remove_const_t::IsGarbageCollectedMixinTypeMarker>> + : std::true_type { + static_assert(sizeof(T), "T must be fully defined"); +}; + +template +struct HasGarbageCollectedTypeMarker : std::false_type { + static_assert(sizeof(T), "T must be fully defined"); +}; + +template +struct HasGarbageCollectedTypeMarker< + T, + std::void_t::IsGarbageCollectedTypeMarker>> + : std::true_type { + static_assert(sizeof(T), "T must be fully defined"); +}; + +template ::value, + bool = HasGarbageCollectedMixinTypeMarker::value> +struct IsGarbageCollectedMixinType : std::false_type { + static_assert(sizeof(T), "T must be fully defined"); +}; + +template +struct IsGarbageCollectedMixinType : std::true_type { + static_assert(sizeof(T), "T must be fully defined"); +}; + +template ::value> +struct IsGarbageCollectedType : std::false_type { + static_assert(sizeof(T), "T must be fully defined"); +}; + +template +struct IsGarbageCollectedType : std::true_type { + static_assert(sizeof(T), "T must be fully defined"); +}; + +template +struct IsGarbageCollectedOrMixinType + : std::integral_constant::value || + IsGarbageCollectedMixinType::value> { + static_assert(sizeof(T), "T must be fully defined"); +}; + +template ::value && + HasGarbageCollectedMixinTypeMarker::value)> +struct IsGarbageCollectedWithMixinType : std::false_type { + static_assert(sizeof(T), "T must be fully defined"); +}; + +template +struct IsGarbageCollectedWithMixinType : std::true_type { + static_assert(sizeof(T), "T must be fully defined"); +}; + +template +struct IsSubclassOfBasicMemberTemplate { + private: + template + static std::true_type SubclassCheck( + BasicMember*); + static std::false_type SubclassCheck(...); + + public: + static constexpr bool value = + decltype(SubclassCheck(std::declval()))::value; +}; + +template ::value> +struct IsMemberType : std::false_type {}; + +template +struct IsMemberType : std::true_type {}; + +template ::value> +struct IsWeakMemberType : std::false_type {}; + +template +struct IsWeakMemberType : std::true_type {}; + +template ::value> +struct IsUntracedMemberType : std::false_type {}; + +template +struct IsUntracedMemberType : std::true_type {}; + +template +struct IsComplete { + private: + template + static std::true_type IsSizeOfKnown(U*); + static std::false_type IsSizeOfKnown(...); + + public: + static constexpr bool value = + decltype(IsSizeOfKnown(std::declval()))::value; +}; + +template +constexpr bool IsDecayedSameV = + std::is_same_v, std::decay_t>; + +template +constexpr bool IsStrictlyBaseOfV = + std::is_base_of_v, std::decay_t> && + !IsDecayedSameV; + +} // namespace internal + +/** + * Value is true for types that inherit from `GarbageCollectedMixin` but not + * `GarbageCollected` (i.e., they are free mixins), and false otherwise. + */ +template +constexpr bool IsGarbageCollectedMixinTypeV = + internal::IsGarbageCollectedMixinType::value; + +/** + * Value is true for types that inherit from `GarbageCollected`, and false + * otherwise. + */ +template +constexpr bool IsGarbageCollectedTypeV = + internal::IsGarbageCollectedType::value; + +/** + * Value is true for types that inherit from either `GarbageCollected` or + * `GarbageCollectedMixin`, and false otherwise. + */ +template +constexpr bool IsGarbageCollectedOrMixinTypeV = + internal::IsGarbageCollectedOrMixinType::value; + +/** + * Value is true for types that inherit from `GarbageCollected` and + * `GarbageCollectedMixin`, and false otherwise. + */ +template +constexpr bool IsGarbageCollectedWithMixinTypeV = + internal::IsGarbageCollectedWithMixinType::value; + +/** + * Value is true for types of type `Member`, and false otherwise. + */ +template +constexpr bool IsMemberTypeV = internal::IsMemberType::value; + +/** + * Value is true for types of type `UntracedMember`, and false otherwise. + */ +template +constexpr bool IsUntracedMemberTypeV = internal::IsUntracedMemberType::value; + +/** + * Value is true for types of type `WeakMember`, and false otherwise. + */ +template +constexpr bool IsWeakMemberTypeV = internal::IsWeakMemberType::value; + +/** + * Value is true for types that are considered weak references, and false + * otherwise. + */ +template +constexpr bool IsWeakV = internal::IsWeak::value; + +/** + * Value is true for types that are complete, and false otherwise. + */ +template +constexpr bool IsCompleteV = internal::IsComplete::value; + +} // namespace cppgc + +#endif // INCLUDE_CPPGC_TYPE_TRAITS_H_ diff --git a/deps/include/cppgc/vendor.go b/deps/include/cppgc/vendor.go new file mode 100755 index 0000000..2890b3c --- /dev/null +++ b/deps/include/cppgc/vendor.go @@ -0,0 +1,3 @@ +// Generated by deps/upgrade_v8.py, DO NOT REMOVE/EDIT MANUALLY. +// Package cppgc is required to provide support for vendoring modules +package cppgc diff --git a/deps/include/cppgc/visitor.h b/deps/include/cppgc/visitor.h new file mode 100755 index 0000000..f7ebc1d --- /dev/null +++ b/deps/include/cppgc/visitor.h @@ -0,0 +1,411 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_CPPGC_VISITOR_H_ +#define INCLUDE_CPPGC_VISITOR_H_ + +#include "cppgc/custom-space.h" +#include "cppgc/ephemeron-pair.h" +#include "cppgc/garbage-collected.h" +#include "cppgc/internal/logging.h" +#include "cppgc/internal/pointer-policies.h" +#include "cppgc/liveness-broker.h" +#include "cppgc/member.h" +#include "cppgc/sentinel-pointer.h" +#include "cppgc/source-location.h" +#include "cppgc/trace-trait.h" +#include "cppgc/type-traits.h" + +namespace cppgc { + +namespace internal { +template +class BasicCrossThreadPersistent; +template +class BasicPersistent; +class ConservativeTracingVisitor; +class VisitorBase; +class VisitorFactory; +} // namespace internal + +using WeakCallback = void (*)(const LivenessBroker&, const void*); + +/** + * Visitor passed to trace methods. All managed pointers must have called the + * Visitor's trace method on them. + * + * \code + * class Foo final : public GarbageCollected { + * public: + * void Trace(Visitor* visitor) const { + * visitor->Trace(foo_); + * visitor->Trace(weak_foo_); + * } + * private: + * Member foo_; + * WeakMember weak_foo_; + * }; + * \endcode + */ +class V8_EXPORT Visitor { + public: + class Key { + private: + Key() = default; + friend class internal::VisitorFactory; + }; + + explicit Visitor(Key) {} + + virtual ~Visitor() = default; + + /** + * Trace method for Member. + * + * \param member Member reference retaining an object. + */ + template + void Trace(const Member& member) { + const T* value = member.GetRawAtomic(); + CPPGC_DCHECK(value != kSentinelPointer); + TraceImpl(value); + } + + /** + * Trace method for WeakMember. + * + * \param weak_member WeakMember reference weakly retaining an object. + */ + template + void Trace(const WeakMember& weak_member) { + static_assert(sizeof(T), "Pointee type must be fully defined."); + static_assert(internal::IsGarbageCollectedOrMixinType::value, + "T must be GarbageCollected or GarbageCollectedMixin type"); + static_assert(!internal::IsAllocatedOnCompactableSpace::value, + "Weak references to compactable objects are not allowed"); + + const T* value = weak_member.GetRawAtomic(); + + // Bailout assumes that WeakMember emits write barrier. + if (!value) { + return; + } + + CPPGC_DCHECK(value != kSentinelPointer); + VisitWeak(value, TraceTrait::GetTraceDescriptor(value), + &HandleWeak>, &weak_member); + } + + /** + * Trace method for inlined objects that are not allocated themselves but + * otherwise follow managed heap layout and have a Trace() method. + * + * \param object reference of the inlined object. + */ + template + void Trace(const T& object) { +#if V8_ENABLE_CHECKS + // This object is embedded in potentially multiple nested objects. The + // outermost object must not be in construction as such objects are (a) not + // processed immediately, and (b) only processed conservatively if not + // otherwise possible. + CheckObjectNotInConstruction(&object); +#endif // V8_ENABLE_CHECKS + TraceTrait::Trace(this, &object); + } + + /** + * Registers a weak callback method on the object of type T. See + * LivenessBroker for an usage example. + * + * \param object of type T specifying a weak callback method. + */ + template + void RegisterWeakCallbackMethod(const T* object) { + RegisterWeakCallback(&WeakCallbackMethodDelegate, object); + } + + /** + * Trace method for EphemeronPair. + * + * \param ephemeron_pair EphemeronPair reference weakly retaining a key object + * and strongly retaining a value object in case the key object is alive. + */ + template + void Trace(const EphemeronPair& ephemeron_pair) { + TraceEphemeron(ephemeron_pair.key, &ephemeron_pair.value); + RegisterWeakCallbackMethod, + &EphemeronPair::ClearValueIfKeyIsDead>( + &ephemeron_pair); + } + + /** + * Trace method for a single ephemeron. Used for tracing a raw ephemeron in + * which the `key` and `value` are kept separately. + * + * \param weak_member_key WeakMember reference weakly retaining a key object. + * \param member_value Member reference with ephemeron semantics. + */ + template + void TraceEphemeron(const WeakMember& weak_member_key, + const Member* member_value) { + const KeyType* key = weak_member_key.GetRawAtomic(); + if (!key) return; + + // `value` must always be non-null. + CPPGC_DCHECK(member_value); + const ValueType* value = member_value->GetRawAtomic(); + if (!value) return; + + // KeyType and ValueType may refer to GarbageCollectedMixin. + TraceDescriptor value_desc = + TraceTrait::GetTraceDescriptor(value); + CPPGC_DCHECK(value_desc.base_object_payload); + const void* key_base_object_payload = + TraceTrait::GetTraceDescriptor(key).base_object_payload; + CPPGC_DCHECK(key_base_object_payload); + + VisitEphemeron(key_base_object_payload, value, value_desc); + } + + /** + * Trace method for a single ephemeron. Used for tracing a raw ephemeron in + * which the `key` and `value` are kept separately. Note that this overload + * is for non-GarbageCollected `value`s that can be traced though. + * + * \param key `WeakMember` reference weakly retaining a key object. + * \param value Reference weakly retaining a value object. Note that + * `ValueType` here should not be `Member`. It is expected that + * `TraceTrait::GetTraceDescriptor(value)` returns a + * `TraceDescriptor` with a null base pointer but a valid trace method. + */ + template + void TraceEphemeron(const WeakMember& weak_member_key, + const ValueType* value) { + static_assert(!IsGarbageCollectedOrMixinTypeV, + "garbage-collected types must use WeakMember and Member"); + const KeyType* key = weak_member_key.GetRawAtomic(); + if (!key) return; + + // `value` must always be non-null. + CPPGC_DCHECK(value); + TraceDescriptor value_desc = + TraceTrait::GetTraceDescriptor(value); + // `value_desc.base_object_payload` must be null as this override is only + // taken for non-garbage-collected values. + CPPGC_DCHECK(!value_desc.base_object_payload); + + // KeyType might be a GarbageCollectedMixin. + const void* key_base_object_payload = + TraceTrait::GetTraceDescriptor(key).base_object_payload; + CPPGC_DCHECK(key_base_object_payload); + + VisitEphemeron(key_base_object_payload, value, value_desc); + } + + /** + * Trace method that strongifies a WeakMember. + * + * \param weak_member WeakMember reference retaining an object. + */ + template + void TraceStrongly(const WeakMember& weak_member) { + const T* value = weak_member.GetRawAtomic(); + CPPGC_DCHECK(value != kSentinelPointer); + TraceImpl(value); + } + + /** + * Trace method for retaining containers strongly. + * + * \param object reference to the container. + */ + template + void TraceStrongContainer(const T* object) { + TraceImpl(object); + } + + /** + * Trace method for retaining containers weakly. + * + * \param object reference to the container. + * \param callback to be invoked. + * \param callback_data custom data that is passed to the callback. + */ + template + void TraceWeakContainer(const T* object, WeakCallback callback, + const void* callback_data) { + if (!object) return; + VisitWeakContainer(object, TraceTrait::GetTraceDescriptor(object), + TraceTrait::GetWeakTraceDescriptor(object), callback, + callback_data); + } + + /** + * Registers a slot containing a reference to an object allocated on a + * compactable space. Such references maybe be arbitrarily moved by the GC. + * + * \param slot location of reference to object that might be moved by the GC. + * The slot must contain an uncompressed pointer. + */ + template + void RegisterMovableReference(const T** slot) { + static_assert(internal::IsAllocatedOnCompactableSpace::value, + "Only references to objects allocated on compactable spaces " + "should be registered as movable slots."); + static_assert(!IsGarbageCollectedMixinTypeV, + "Mixin types do not support compaction."); + HandleMovableReference(reinterpret_cast(slot)); + } + + /** + * Registers a weak callback that is invoked during garbage collection. + * + * \param callback to be invoked. + * \param data custom data that is passed to the callback. + */ + virtual void RegisterWeakCallback(WeakCallback callback, const void* data) {} + + /** + * Defers tracing an object from a concurrent thread to the mutator thread. + * Should be called by Trace methods of types that are not safe to trace + * concurrently. + * + * \param parameter tells the trace callback which object was deferred. + * \param callback to be invoked for tracing on the mutator thread. + * \param deferred_size size of deferred object. + * + * \returns false if the object does not need to be deferred (i.e. currently + * traced on the mutator thread) and true otherwise (i.e. currently traced on + * a concurrent thread). + */ + virtual V8_WARN_UNUSED_RESULT bool DeferTraceToMutatorThreadIfConcurrent( + const void* parameter, TraceCallback callback, size_t deferred_size) { + // By default tracing is not deferred. + return false; + } + + protected: + virtual void Visit(const void* self, TraceDescriptor) {} + virtual void VisitWeak(const void* self, TraceDescriptor, WeakCallback, + const void* weak_member) {} + virtual void VisitEphemeron(const void* key, const void* value, + TraceDescriptor value_desc) {} + virtual void VisitWeakContainer(const void* self, TraceDescriptor strong_desc, + TraceDescriptor weak_desc, + WeakCallback callback, const void* data) {} + virtual void HandleMovableReference(const void**) {} + + private: + template + static void WeakCallbackMethodDelegate(const LivenessBroker& info, + const void* self) { + // Callback is registered through a potential const Trace method but needs + // to be able to modify fields. See HandleWeak. + (const_cast(static_cast(self))->*method)(info); + } + + template + static void HandleWeak(const LivenessBroker& info, const void* object) { + const PointerType* weak = static_cast(object); + auto* raw_ptr = weak->GetFromGC(); + if (!info.IsHeapObjectAlive(raw_ptr)) { + weak->ClearFromGC(); + } + } + + template + void TraceImpl(const T* t) { + static_assert(sizeof(T), "Pointee type must be fully defined."); + static_assert(internal::IsGarbageCollectedOrMixinType::value, + "T must be GarbageCollected or GarbageCollectedMixin type"); + if (!t) { + return; + } + Visit(t, TraceTrait::GetTraceDescriptor(t)); + } + +#if V8_ENABLE_CHECKS + void CheckObjectNotInConstruction(const void* address); +#endif // V8_ENABLE_CHECKS + + template + friend class internal::BasicCrossThreadPersistent; + template + friend class internal::BasicPersistent; + friend class internal::ConservativeTracingVisitor; + friend class internal::VisitorBase; +}; + +namespace internal { + +class V8_EXPORT RootVisitor { + public: + explicit RootVisitor(Visitor::Key) {} + + virtual ~RootVisitor() = default; + + template * = nullptr> + void Trace(const AnyStrongPersistentType& p) { + using PointeeType = typename AnyStrongPersistentType::PointeeType; + const void* object = Extract(p); + if (!object) { + return; + } + VisitRoot(object, TraceTrait::GetTraceDescriptor(object), + p.Location()); + } + + template * = nullptr> + void Trace(const AnyWeakPersistentType& p) { + using PointeeType = typename AnyWeakPersistentType::PointeeType; + static_assert(!internal::IsAllocatedOnCompactableSpace::value, + "Weak references to compactable objects are not allowed"); + const void* object = Extract(p); + if (!object) { + return; + } + VisitWeakRoot(object, TraceTrait::GetTraceDescriptor(object), + &HandleWeak, &p, p.Location()); + } + + protected: + virtual void VisitRoot(const void*, TraceDescriptor, const SourceLocation&) {} + virtual void VisitWeakRoot(const void* self, TraceDescriptor, WeakCallback, + const void* weak_root, const SourceLocation&) {} + + private: + template + static const void* Extract(AnyPersistentType& p) { + using PointeeType = typename AnyPersistentType::PointeeType; + static_assert(sizeof(PointeeType), + "Persistent's pointee type must be fully defined"); + static_assert(internal::IsGarbageCollectedOrMixinType::value, + "Persistent's pointee type must be GarbageCollected or " + "GarbageCollectedMixin"); + return p.GetFromGC(); + } + + template + static void HandleWeak(const LivenessBroker& info, const void* object) { + const PointerType* weak = static_cast(object); + auto* raw_ptr = weak->GetFromGC(); + if (!info.IsHeapObjectAlive(raw_ptr)) { + weak->ClearFromGC(); + } + } +}; + +} // namespace internal +} // namespace cppgc + +#endif // INCLUDE_CPPGC_VISITOR_H_ diff --git a/deps/include/js_protocol-1.2.json b/deps/include/js_protocol-1.2.json new file mode 100755 index 0000000..aff6806 --- /dev/null +++ b/deps/include/js_protocol-1.2.json @@ -0,0 +1,997 @@ +{ + "version": { "major": "1", "minor": "2" }, + "domains": [ + { + "domain": "Schema", + "description": "Provides information about the protocol schema.", + "types": [ + { + "id": "Domain", + "type": "object", + "description": "Description of the protocol domain.", + "exported": true, + "properties": [ + { "name": "name", "type": "string", "description": "Domain name." }, + { "name": "version", "type": "string", "description": "Domain version." } + ] + } + ], + "commands": [ + { + "name": "getDomains", + "description": "Returns supported domains.", + "handlers": ["browser", "renderer"], + "returns": [ + { "name": "domains", "type": "array", "items": { "$ref": "Domain" }, "description": "List of supported domains." } + ] + } + ] + }, + { + "domain": "Runtime", + "description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. Evaluation results are returned as mirror object that expose object type, string representation and unique identifier that can be used for further object reference. Original objects are maintained in memory unless they are either explicitly released or are released along with the other objects in their object group.", + "types": [ + { + "id": "ScriptId", + "type": "string", + "description": "Unique script identifier." + }, + { + "id": "RemoteObjectId", + "type": "string", + "description": "Unique object identifier." + }, + { + "id": "UnserializableValue", + "type": "string", + "enum": ["Infinity", "NaN", "-Infinity", "-0"], + "description": "Primitive value which cannot be JSON-stringified." + }, + { + "id": "RemoteObject", + "type": "object", + "description": "Mirror object referencing original JavaScript object.", + "exported": true, + "properties": [ + { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." }, + { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error", "proxy", "promise", "typedarray"], "description": "Object subtype hint. Specified for object type values only." }, + { "name": "className", "type": "string", "optional": true, "description": "Object class (constructor) name. Specified for object type values only." }, + { "name": "value", "type": "any", "optional": true, "description": "Remote object value in case of primitive values or JSON values (if it was requested)." }, + { "name": "unserializableValue", "$ref": "UnserializableValue", "optional": true, "description": "Primitive value which can not be JSON-stringified does not have value, but gets this property." }, + { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, + { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Unique object identifier (for non-primitive values)." }, + { "name": "preview", "$ref": "ObjectPreview", "optional": true, "description": "Preview containing abbreviated property values. Specified for object type values only.", "experimental": true }, + { "name": "customPreview", "$ref": "CustomPreview", "optional": true, "experimental": true} + ] + }, + { + "id": "CustomPreview", + "type": "object", + "experimental": true, + "properties": [ + { "name": "header", "type": "string"}, + { "name": "hasBody", "type": "boolean"}, + { "name": "formatterObjectId", "$ref": "RemoteObjectId"}, + { "name": "bindRemoteObjectFunctionId", "$ref": "RemoteObjectId" }, + { "name": "configObjectId", "$ref": "RemoteObjectId", "optional": true } + ] + }, + { + "id": "ObjectPreview", + "type": "object", + "experimental": true, + "description": "Object containing abbreviated remote object value.", + "properties": [ + { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." }, + { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for object type values only." }, + { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, + { "name": "overflow", "type": "boolean", "description": "True iff some of the properties or entries of the original object did not fit." }, + { "name": "properties", "type": "array", "items": { "$ref": "PropertyPreview" }, "description": "List of the properties." }, + { "name": "entries", "type": "array", "items": { "$ref": "EntryPreview" }, "optional": true, "description": "List of the entries. Specified for map and set subtype values only." } + ] + }, + { + "id": "PropertyPreview", + "type": "object", + "experimental": true, + "properties": [ + { "name": "name", "type": "string", "description": "Property name." }, + { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol", "accessor"], "description": "Object type. Accessor means that the property itself is an accessor property." }, + { "name": "value", "type": "string", "optional": true, "description": "User-friendly property value string." }, + { "name": "valuePreview", "$ref": "ObjectPreview", "optional": true, "description": "Nested value preview." }, + { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for object type values only." } + ] + }, + { + "id": "EntryPreview", + "type": "object", + "experimental": true, + "properties": [ + { "name": "key", "$ref": "ObjectPreview", "optional": true, "description": "Preview of the key. Specified for map-like collection entries." }, + { "name": "value", "$ref": "ObjectPreview", "description": "Preview of the value." } + ] + }, + { + "id": "PropertyDescriptor", + "type": "object", + "description": "Object property descriptor.", + "properties": [ + { "name": "name", "type": "string", "description": "Property name or symbol description." }, + { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." }, + { "name": "writable", "type": "boolean", "optional": true, "description": "True if the value associated with the property may be changed (data descriptors only)." }, + { "name": "get", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a getter for the property, or undefined if there is no getter (accessor descriptors only)." }, + { "name": "set", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a setter for the property, or undefined if there is no setter (accessor descriptors only)." }, + { "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." }, + { "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." }, + { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, + { "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object." }, + { "name": "symbol", "$ref": "RemoteObject", "optional": true, "description": "Property symbol object, if the property is of the symbol type." } + ] + }, + { + "id": "InternalPropertyDescriptor", + "type": "object", + "description": "Object internal property descriptor. This property isn't normally visible in JavaScript code.", + "properties": [ + { "name": "name", "type": "string", "description": "Conventional property name." }, + { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." } + ] + }, + { + "id": "CallArgument", + "type": "object", + "description": "Represents function call argument. Either remote object id objectId, primitive value, unserializable primitive value or neither of (for undefined) them should be specified.", + "properties": [ + { "name": "value", "type": "any", "optional": true, "description": "Primitive value." }, + { "name": "unserializableValue", "$ref": "UnserializableValue", "optional": true, "description": "Primitive value which can not be JSON-stringified." }, + { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Remote object handle." } + ] + }, + { + "id": "ExecutionContextId", + "type": "integer", + "description": "Id of an execution context." + }, + { + "id": "ExecutionContextDescription", + "type": "object", + "description": "Description of an isolated world.", + "properties": [ + { "name": "id", "$ref": "ExecutionContextId", "description": "Unique id of the execution context. It can be used to specify in which execution context script evaluation should be performed." }, + { "name": "origin", "type": "string", "description": "Execution context origin." }, + { "name": "name", "type": "string", "description": "Human readable name describing given context." }, + { "name": "auxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." } + ] + }, + { + "id": "ExceptionDetails", + "type": "object", + "description": "Detailed information about exception (or error) that was thrown during script compilation or execution.", + "properties": [ + { "name": "exceptionId", "type": "integer", "description": "Exception id." }, + { "name": "text", "type": "string", "description": "Exception text, which should be used together with exception object when available." }, + { "name": "lineNumber", "type": "integer", "description": "Line number of the exception location (0-based)." }, + { "name": "columnNumber", "type": "integer", "description": "Column number of the exception location (0-based)." }, + { "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Script ID of the exception location." }, + { "name": "url", "type": "string", "optional": true, "description": "URL of the exception location, to be used when the script was not reported." }, + { "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace if available." }, + { "name": "exception", "$ref": "RemoteObject", "optional": true, "description": "Exception object if available." }, + { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Identifier of the context where exception happened." } + ] + }, + { + "id": "Timestamp", + "type": "number", + "description": "Number of milliseconds since epoch." + }, + { + "id": "CallFrame", + "type": "object", + "description": "Stack entry for runtime errors and assertions.", + "properties": [ + { "name": "functionName", "type": "string", "description": "JavaScript function name." }, + { "name": "scriptId", "$ref": "ScriptId", "description": "JavaScript script id." }, + { "name": "url", "type": "string", "description": "JavaScript script name or url." }, + { "name": "lineNumber", "type": "integer", "description": "JavaScript script line number (0-based)." }, + { "name": "columnNumber", "type": "integer", "description": "JavaScript script column number (0-based)." } + ] + }, + { + "id": "StackTrace", + "type": "object", + "description": "Call frames for assertions or error messages.", + "exported": true, + "properties": [ + { "name": "description", "type": "string", "optional": true, "description": "String label of this stack trace. For async traces this may be a name of the function that initiated the async call." }, + { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "JavaScript function name." }, + { "name": "parent", "$ref": "StackTrace", "optional": true, "description": "Asynchronous JavaScript stack trace that preceded this stack, if available." } + ] + } + ], + "commands": [ + { + "name": "evaluate", + "async": true, + "parameters": [ + { "name": "expression", "type": "string", "description": "Expression to evaluate." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, + { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." }, + { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides setPauseOnException state." }, + { "name": "contextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform evaluation. If the parameter is omitted the evaluation will be performed in the context of the inspected page." }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." }, + { "name": "userGesture", "type": "boolean", "optional": true, "experimental": true, "description": "Whether execution should be treated as initiated by user in the UI." }, + { "name": "awaitPromise", "type": "boolean", "optional":true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." } + ], + "returns": [ + { "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} + ], + "description": "Evaluates expression on global object." + }, + { + "name": "awaitPromise", + "async": true, + "parameters": [ + { "name": "promiseObjectId", "$ref": "RemoteObjectId", "description": "Identifier of the promise." }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." } + ], + "returns": [ + { "name": "result", "$ref": "RemoteObject", "description": "Promise result. Will contain rejected value if promise was rejected." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details if stack strace is available."} + ], + "description": "Add handler to promise with given promise object id." + }, + { + "name": "callFunctionOn", + "async": true, + "parameters": [ + { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to call function on." }, + { "name": "functionDeclaration", "type": "string", "description": "Declaration of the function to call." }, + { "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." }, + { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides setPauseOnException state." }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." }, + { "name": "userGesture", "type": "boolean", "optional": true, "experimental": true, "description": "Whether execution should be treated as initiated by user in the UI." }, + { "name": "awaitPromise", "type": "boolean", "optional":true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." } + ], + "returns": [ + { "name": "result", "$ref": "RemoteObject", "description": "Call result." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} + ], + "description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object." + }, + { + "name": "getProperties", + "parameters": [ + { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." }, + { "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." }, + { "name": "accessorPropertiesOnly", "optional": true, "type": "boolean", "description": "If true, returns accessor properties (with getter/setter) only; internal properties are not returned either.", "experimental": true }, + { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the results." } + ], + "returns": [ + { "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor" }, "description": "Object properties." }, + { "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor" }, "description": "Internal object properties (only of the element itself)." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} + ], + "description": "Returns properties of a given object. Object group of the result is inherited from the target object." + }, + { + "name": "releaseObject", + "parameters": [ + { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to release." } + ], + "description": "Releases remote object with given id." + }, + { + "name": "releaseObjectGroup", + "parameters": [ + { "name": "objectGroup", "type": "string", "description": "Symbolic object group name." } + ], + "description": "Releases all remote objects that belong to a given group." + }, + { + "name": "runIfWaitingForDebugger", + "description": "Tells inspected instance to run if it was waiting for debugger to attach." + }, + { + "name": "enable", + "description": "Enables reporting of execution contexts creation by means of executionContextCreated event. When the reporting gets enabled the event will be sent immediately for each existing execution context." + }, + { + "name": "disable", + "description": "Disables reporting of execution contexts creation." + }, + { + "name": "discardConsoleEntries", + "description": "Discards collected exceptions and console API calls." + }, + { + "name": "setCustomObjectFormatterEnabled", + "parameters": [ + { + "name": "enabled", + "type": "boolean" + } + ], + "experimental": true + }, + { + "name": "compileScript", + "parameters": [ + { "name": "expression", "type": "string", "description": "Expression to compile." }, + { "name": "sourceURL", "type": "string", "description": "Source url to be set for the script." }, + { "name": "persistScript", "type": "boolean", "description": "Specifies whether the compiled script should be persisted." }, + { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform script run. If the parameter is omitted the evaluation will be performed in the context of the inspected page." } + ], + "returns": [ + { "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Id of the script." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} + ], + "description": "Compiles expression." + }, + { + "name": "runScript", + "async": true, + "parameters": [ + { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to run." }, + { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform script run. If the parameter is omitted the evaluation will be performed in the context of the inspected page." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, + { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides setPauseOnException state." }, + { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." }, + { "name": "awaitPromise", "type": "boolean", "optional": true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." } + ], + "returns": [ + { "name": "result", "$ref": "RemoteObject", "description": "Run result." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} + ], + "description": "Runs script with given id in a given context." + } + ], + "events": [ + { + "name": "executionContextCreated", + "parameters": [ + { "name": "context", "$ref": "ExecutionContextDescription", "description": "A newly created execution contex." } + ], + "description": "Issued when new execution context is created." + }, + { + "name": "executionContextDestroyed", + "parameters": [ + { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Id of the destroyed context" } + ], + "description": "Issued when execution context is destroyed." + }, + { + "name": "executionContextsCleared", + "description": "Issued when all executionContexts were cleared in browser" + }, + { + "name": "exceptionThrown", + "description": "Issued when exception was thrown and unhandled.", + "parameters": [ + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp of the exception." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails" } + ] + }, + { + "name": "exceptionRevoked", + "description": "Issued when unhandled exception was revoked.", + "parameters": [ + { "name": "reason", "type": "string", "description": "Reason describing why exception was revoked." }, + { "name": "exceptionId", "type": "integer", "description": "The id of revoked exception, as reported in exceptionUnhandled." } + ] + }, + { + "name": "consoleAPICalled", + "description": "Issued when console API was called.", + "parameters": [ + { "name": "type", "type": "string", "enum": ["log", "debug", "info", "error", "warning", "dir", "dirxml", "table", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert", "profile", "profileEnd"], "description": "Type of the call." }, + { "name": "args", "type": "array", "items": { "$ref": "RemoteObject" }, "description": "Call arguments." }, + { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Identifier of the context where the call was made." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Call timestamp." }, + { "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "Stack trace captured when the call was made." } + ] + }, + { + "name": "inspectRequested", + "description": "Issued when object should be inspected (for example, as a result of inspect() command line API call).", + "parameters": [ + { "name": "object", "$ref": "RemoteObject" }, + { "name": "hints", "type": "object" } + ] + } + ] + }, + { + "domain": "Debugger", + "description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing breakpoints, stepping through execution, exploring stack traces, etc.", + "dependencies": ["Runtime"], + "types": [ + { + "id": "BreakpointId", + "type": "string", + "description": "Breakpoint identifier." + }, + { + "id": "CallFrameId", + "type": "string", + "description": "Call frame identifier." + }, + { + "id": "Location", + "type": "object", + "properties": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier as reported in the Debugger.scriptParsed." }, + { "name": "lineNumber", "type": "integer", "description": "Line number in the script (0-based)." }, + { "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the script (0-based)." } + ], + "description": "Location in the source code." + }, + { + "id": "ScriptPosition", + "experimental": true, + "type": "object", + "properties": [ + { "name": "lineNumber", "type": "integer" }, + { "name": "columnNumber", "type": "integer" } + ], + "description": "Location in the source code." + }, + { + "id": "CallFrame", + "type": "object", + "properties": [ + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused." }, + { "name": "functionName", "type": "string", "description": "Name of the JavaScript function called on this call frame." }, + { "name": "functionLocation", "$ref": "Location", "optional": true, "experimental": true, "description": "Location in the source code." }, + { "name": "location", "$ref": "Location", "description": "Location in the source code." }, + { "name": "scopeChain", "type": "array", "items": { "$ref": "Scope" }, "description": "Scope chain for this call frame." }, + { "name": "this", "$ref": "Runtime.RemoteObject", "description": "this object for this call frame." }, + { "name": "returnValue", "$ref": "Runtime.RemoteObject", "optional": true, "description": "The value being returned, if the function is at return point." } + ], + "description": "JavaScript call frame. Array of call frames form the call stack." + }, + { + "id": "Scope", + "type": "object", + "properties": [ + { "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch", "block", "script"], "description": "Scope type." }, + { "name": "object", "$ref": "Runtime.RemoteObject", "description": "Object representing the scope. For global and with scopes it represents the actual object; for the rest of the scopes, it is artificial transient object enumerating scope variables as its properties." }, + { "name": "name", "type": "string", "optional": true }, + { "name": "startLocation", "$ref": "Location", "optional": true, "description": "Location in the source code where scope starts" }, + { "name": "endLocation", "$ref": "Location", "optional": true, "description": "Location in the source code where scope ends" } + ], + "description": "Scope description." + }, + { + "id": "SearchMatch", + "type": "object", + "description": "Search match for resource.", + "exported": true, + "properties": [ + { "name": "lineNumber", "type": "number", "description": "Line number in resource content." }, + { "name": "lineContent", "type": "string", "description": "Line with match content." } + ], + "experimental": true + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables debugger for the given page. Clients should not assume that the debugging has been enabled until the result for this command is received." + }, + { + "name": "disable", + "description": "Disables debugger for given page." + }, + { + "name": "setBreakpointsActive", + "parameters": [ + { "name": "active", "type": "boolean", "description": "New value for breakpoints active state." } + ], + "description": "Activates / deactivates all breakpoints on the page." + }, + { + "name": "setSkipAllPauses", + "parameters": [ + { "name": "skip", "type": "boolean", "description": "New value for skip pauses state." } + ], + "description": "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc)." + }, + { + "name": "setBreakpointByUrl", + "parameters": [ + { "name": "lineNumber", "type": "integer", "description": "Line number to set breakpoint at." }, + { "name": "url", "type": "string", "optional": true, "description": "URL of the resources to set breakpoint on." }, + { "name": "urlRegex", "type": "string", "optional": true, "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either url or urlRegex must be specified." }, + { "name": "columnNumber", "type": "integer", "optional": true, "description": "Offset in the line to set breakpoint at." }, + { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } + ], + "returns": [ + { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, + { "name": "locations", "type": "array", "items": { "$ref": "Location" }, "description": "List of the locations this breakpoint resolved into upon addition." } + ], + "description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in locations property. Further matching script parsing will result in subsequent breakpointResolved events issued. This logical breakpoint will survive page reloads." + }, + { + "name": "setBreakpoint", + "parameters": [ + { "name": "location", "$ref": "Location", "description": "Location to set breakpoint in." }, + { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } + ], + "returns": [ + { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, + { "name": "actualLocation", "$ref": "Location", "description": "Location this breakpoint resolved into." } + ], + "description": "Sets JavaScript breakpoint at a given location." + }, + { + "name": "removeBreakpoint", + "parameters": [ + { "name": "breakpointId", "$ref": "BreakpointId" } + ], + "description": "Removes JavaScript breakpoint." + }, + { + "name": "continueToLocation", + "parameters": [ + { "name": "location", "$ref": "Location", "description": "Location to continue to." } + ], + "description": "Continues execution until specific location is reached." + }, + { + "name": "stepOver", + "description": "Steps over the statement." + }, + { + "name": "stepInto", + "description": "Steps into the function call." + }, + { + "name": "stepOut", + "description": "Steps out of the function call." + }, + { + "name": "pause", + "description": "Stops on the next JavaScript statement." + }, + { + "name": "resume", + "description": "Resumes JavaScript execution." + }, + { + "name": "searchInContent", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to search in." }, + { "name": "query", "type": "string", "description": "String to search for." }, + { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." }, + { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." } + ], + "returns": [ + { "name": "result", "type": "array", "items": { "$ref": "SearchMatch" }, "description": "List of search matches." } + ], + "experimental": true, + "description": "Searches for given string in script content." + }, + { + "name": "setScriptSource", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to edit." }, + { "name": "scriptSource", "type": "string", "description": "New content of the script." }, + { "name": "dryRun", "type": "boolean", "optional": true, "description": " If true the change will not actually be applied. Dry run may be used to get result description without actually modifying the code." } + ], + "returns": [ + { "name": "callFrames", "type": "array", "optional": true, "items": { "$ref": "CallFrame" }, "description": "New stack trace in case editing has happened while VM was stopped." }, + { "name": "stackChanged", "type": "boolean", "optional": true, "description": "Whether current call stack was modified after applying the changes." }, + { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." }, + { "name": "exceptionDetails", "optional": true, "$ref": "Runtime.ExceptionDetails", "description": "Exception details if any." } + ], + "description": "Edits JavaScript source live." + }, + { + "name": "restartFrame", + "parameters": [ + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." } + ], + "returns": [ + { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "New stack trace." }, + { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." } + ], + "description": "Restarts particular call frame from the beginning." + }, + { + "name": "getScriptSource", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to get source for." } + ], + "returns": [ + { "name": "scriptSource", "type": "string", "description": "Script source." } + ], + "description": "Returns source for the script with given id." + }, + { + "name": "setPauseOnExceptions", + "parameters": [ + { "name": "state", "type": "string", "enum": ["none", "uncaught", "all"], "description": "Pause on exceptions mode." } + ], + "description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or no exceptions. Initial pause on exceptions state is none." + }, + { + "name": "evaluateOnCallFrame", + "parameters": [ + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." }, + { "name": "expression", "type": "string", "description": "Expression to evaluate." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using releaseObjectGroup)." }, + { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false." }, + { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides setPauseOnException state." }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." } + ], + "returns": [ + { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." }, + { "name": "exceptionDetails", "$ref": "Runtime.ExceptionDetails", "optional": true, "description": "Exception details."} + ], + "description": "Evaluates expression on a given call frame." + }, + { + "name": "setVariableValue", + "parameters": [ + { "name": "scopeNumber", "type": "integer", "description": "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' scope types are allowed. Other scopes could be manipulated manually." }, + { "name": "variableName", "type": "string", "description": "Variable name." }, + { "name": "newValue", "$ref": "Runtime.CallArgument", "description": "New variable value." }, + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Id of callframe that holds variable." } + ], + "description": "Changes value of variable in a callframe. Object-based scopes are not supported and must be mutated manually." + }, + { + "name": "setAsyncCallStackDepth", + "parameters": [ + { "name": "maxDepth", "type": "integer", "description": "Maximum depth of async call stacks. Setting to 0 will effectively disable collecting async call stacks (default)." } + ], + "description": "Enables or disables async call stacks tracking." + }, + { + "name": "setBlackboxPatterns", + "parameters": [ + { "name": "patterns", "type": "array", "items": { "type": "string" }, "description": "Array of regexps that will be used to check script url for blackbox state." } + ], + "experimental": true, + "description": "Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in scripts with url matching one of the patterns. VM will try to leave blackboxed script by performing 'step in' several times, finally resorting to 'step out' if unsuccessful." + }, + { + "name": "setBlackboxedRanges", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script." }, + { "name": "positions", "type": "array", "items": { "$ref": "ScriptPosition" } } + ], + "experimental": true, + "description": "Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted scripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful. Positions array contains positions where blackbox state is changed. First interval isn't blackboxed. Array should be sorted." + } + ], + "events": [ + { + "name": "scriptParsed", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." }, + { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, + { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, + { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, + { "name": "endLine", "type": "integer", "description": "Last line of the script." }, + { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, + { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context." }, + { "name": "hash", "type": "string", "description": "Content hash of the script."}, + { "name": "executionContextAuxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." }, + { "name": "isLiveEdit", "type": "boolean", "optional": true, "description": "True, if this script is generated as a result of the live edit operation.", "experimental": true }, + { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, + { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "experimental": true } + ], + "description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger." + }, + { + "name": "scriptFailedToParse", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." }, + { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, + { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, + { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, + { "name": "endLine", "type": "integer", "description": "Last line of the script." }, + { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, + { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context." }, + { "name": "hash", "type": "string", "description": "Content hash of the script."}, + { "name": "executionContextAuxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." }, + { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, + { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "experimental": true } + ], + "description": "Fired when virtual machine fails to parse the script." + }, + { + "name": "breakpointResolved", + "parameters": [ + { "name": "breakpointId", "$ref": "BreakpointId", "description": "Breakpoint unique identifier." }, + { "name": "location", "$ref": "Location", "description": "Actual breakpoint location." } + ], + "description": "Fired when breakpoint is resolved to an actual script and location." + }, + { + "name": "paused", + "parameters": [ + { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." }, + { "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "assert", "debugCommand", "promiseRejection", "other" ], "description": "Pause reason.", "exported": true }, + { "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." }, + { "name": "hitBreakpoints", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Hit breakpoints IDs" }, + { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." } + ], + "description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria." + }, + { + "name": "resumed", + "description": "Fired when the virtual machine resumed execution." + } + ] + }, + { + "domain": "Console", + "description": "This domain is deprecated - use Runtime or Log instead.", + "dependencies": ["Runtime"], + "deprecated": true, + "types": [ + { + "id": "ConsoleMessage", + "type": "object", + "description": "Console message.", + "properties": [ + { "name": "source", "type": "string", "enum": ["xml", "javascript", "network", "console-api", "storage", "appcache", "rendering", "security", "other", "deprecation", "worker"], "description": "Message source." }, + { "name": "level", "type": "string", "enum": ["log", "warning", "error", "debug", "info"], "description": "Message severity." }, + { "name": "text", "type": "string", "description": "Message text." }, + { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." }, + { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message (1-based)." }, + { "name": "column", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message (1-based)." } + ] + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables console domain, sends the messages collected so far to the client by means of the messageAdded notification." + }, + { + "name": "disable", + "description": "Disables console domain, prevents further console messages from being reported to the client." + }, + { + "name": "clearMessages", + "description": "Does nothing." + } + ], + "events": [ + { + "name": "messageAdded", + "parameters": [ + { "name": "message", "$ref": "ConsoleMessage", "description": "Console message that has been added." } + ], + "description": "Issued when new console message is added." + } + ] + }, + { + "domain": "Profiler", + "dependencies": ["Runtime", "Debugger"], + "types": [ + { + "id": "ProfileNode", + "type": "object", + "description": "Profile node. Holds callsite information, execution statistics and child nodes.", + "properties": [ + { "name": "id", "type": "integer", "description": "Unique id of the node." }, + { "name": "callFrame", "$ref": "Runtime.CallFrame", "description": "Function location." }, + { "name": "hitCount", "type": "integer", "optional": true, "experimental": true, "description": "Number of samples where this node was on top of the call stack." }, + { "name": "children", "type": "array", "items": { "type": "integer" }, "optional": true, "description": "Child node ids." }, + { "name": "deoptReason", "type": "string", "optional": true, "description": "The reason of being not optimized. The function may be deoptimized or marked as don't optimize."}, + { "name": "positionTicks", "type": "array", "items": { "$ref": "PositionTickInfo" }, "optional": true, "experimental": true, "description": "An array of source position ticks." } + ] + }, + { + "id": "Profile", + "type": "object", + "description": "Profile.", + "properties": [ + { "name": "nodes", "type": "array", "items": { "$ref": "ProfileNode" }, "description": "The list of profile nodes. First item is the root node." }, + { "name": "startTime", "type": "number", "description": "Profiling start timestamp in microseconds." }, + { "name": "endTime", "type": "number", "description": "Profiling end timestamp in microseconds." }, + { "name": "samples", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Ids of samples top nodes." }, + { "name": "timeDeltas", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Time intervals between adjacent samples in microseconds. The first delta is relative to the profile startTime." } + ] + }, + { + "id": "PositionTickInfo", + "type": "object", + "experimental": true, + "description": "Specifies a number of samples attributed to a certain source position.", + "properties": [ + { "name": "line", "type": "integer", "description": "Source line number (1-based)." }, + { "name": "ticks", "type": "integer", "description": "Number of samples attributed to the source line." } + ] + } + ], + "commands": [ + { + "name": "enable" + }, + { + "name": "disable" + }, + { + "name": "setSamplingInterval", + "parameters": [ + { "name": "interval", "type": "integer", "description": "New sampling interval in microseconds." } + ], + "description": "Changes CPU profiler sampling interval. Must be called before CPU profiles recording started." + }, + { + "name": "start" + }, + { + "name": "stop", + "returns": [ + { "name": "profile", "$ref": "Profile", "description": "Recorded profile." } + ] + } + ], + "events": [ + { + "name": "consoleProfileStarted", + "parameters": [ + { "name": "id", "type": "string" }, + { "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profile()." }, + { "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." } + ], + "description": "Sent when new profile recodring is started using console.profile() call." + }, + { + "name": "consoleProfileFinished", + "parameters": [ + { "name": "id", "type": "string" }, + { "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profileEnd()." }, + { "name": "profile", "$ref": "Profile" }, + { "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." } + ] + } + ] + }, + { + "domain": "HeapProfiler", + "dependencies": ["Runtime"], + "experimental": true, + "types": [ + { + "id": "HeapSnapshotObjectId", + "type": "string", + "description": "Heap snapshot object id." + }, + { + "id": "SamplingHeapProfileNode", + "type": "object", + "description": "Sampling Heap Profile node. Holds callsite information, allocation statistics and child nodes.", + "properties": [ + { "name": "callFrame", "$ref": "Runtime.CallFrame", "description": "Function location." }, + { "name": "selfSize", "type": "number", "description": "Allocations size in bytes for the node excluding children." }, + { "name": "children", "type": "array", "items": { "$ref": "SamplingHeapProfileNode" }, "description": "Child nodes." } + ] + }, + { + "id": "SamplingHeapProfile", + "type": "object", + "description": "Profile.", + "properties": [ + { "name": "head", "$ref": "SamplingHeapProfileNode" } + ] + } + ], + "commands": [ + { + "name": "enable" + }, + { + "name": "disable" + }, + { + "name": "startTrackingHeapObjects", + "parameters": [ + { "name": "trackAllocations", "type": "boolean", "optional": true } + ] + }, + { + "name": "stopTrackingHeapObjects", + "parameters": [ + { "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken when the tracking is stopped." } + ] + }, + { + "name": "takeHeapSnapshot", + "parameters": [ + { "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken." } + ] + }, + { + "name": "collectGarbage" + }, + { + "name": "getObjectByHeapObjectId", + "parameters": [ + { "name": "objectId", "$ref": "HeapSnapshotObjectId" }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." } + ], + "returns": [ + { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Evaluation result." } + ] + }, + { + "name": "addInspectedHeapObject", + "parameters": [ + { "name": "heapObjectId", "$ref": "HeapSnapshotObjectId", "description": "Heap snapshot object id to be accessible by means of $x command line API." } + ], + "description": "Enables console to refer to the node with given id via $x (see Command Line API for more details $x functions)." + }, + { + "name": "getHeapObjectId", + "parameters": [ + { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Identifier of the object to get heap object id for." } + ], + "returns": [ + { "name": "heapSnapshotObjectId", "$ref": "HeapSnapshotObjectId", "description": "Id of the heap snapshot object corresponding to the passed remote object id." } + ] + }, + { + "name": "startSampling", + "parameters": [ + { "name": "samplingInterval", "type": "number", "optional": true, "description": "Average sample interval in bytes. Poisson distribution is used for the intervals. The default value is 32768 bytes." } + ] + }, + { + "name": "stopSampling", + "returns": [ + { "name": "profile", "$ref": "SamplingHeapProfile", "description": "Recorded sampling heap profile." } + ] + } + ], + "events": [ + { + "name": "addHeapSnapshotChunk", + "parameters": [ + { "name": "chunk", "type": "string" } + ] + }, + { + "name": "resetProfiles" + }, + { + "name": "reportHeapSnapshotProgress", + "parameters": [ + { "name": "done", "type": "integer" }, + { "name": "total", "type": "integer" }, + { "name": "finished", "type": "boolean", "optional": true } + ] + }, + { + "name": "lastSeenObjectId", + "description": "If heap objects tracking has been started then backend regulary sends a current value for last seen object id and corresponding timestamp. If the were changes in the heap since last event then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.", + "parameters": [ + { "name": "lastSeenObjectId", "type": "integer" }, + { "name": "timestamp", "type": "number" } + ] + }, + { + "name": "heapStatsUpdate", + "description": "If heap objects tracking has been started then backend may send update for one or more fragments", + "parameters": [ + { "name": "statsUpdate", "type": "array", "items": { "type": "integer" }, "description": "An array of triplets. Each triplet describes a fragment. The first integer is the fragment index, the second integer is a total count of objects for the fragment, the third integer is a total size of the objects for the fragment."} + ] + } + ] + }] +} diff --git a/deps/include/js_protocol-1.3.json b/deps/include/js_protocol-1.3.json new file mode 100755 index 0000000..a998d46 --- /dev/null +++ b/deps/include/js_protocol-1.3.json @@ -0,0 +1,1159 @@ +{ + "version": { "major": "1", "minor": "3" }, + "domains": [ + { + "domain": "Schema", + "description": "This domain is deprecated.", + "deprecated": true, + "types": [ + { + "id": "Domain", + "type": "object", + "description": "Description of the protocol domain.", + "properties": [ + { "name": "name", "type": "string", "description": "Domain name." }, + { "name": "version", "type": "string", "description": "Domain version." } + ] + } + ], + "commands": [ + { + "name": "getDomains", + "description": "Returns supported domains.", + "handlers": ["browser", "renderer"], + "returns": [ + { "name": "domains", "type": "array", "items": { "$ref": "Domain" }, "description": "List of supported domains." } + ] + } + ] + }, + { + "domain": "Runtime", + "description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. Evaluation results are returned as mirror object that expose object type, string representation and unique identifier that can be used for further object reference. Original objects are maintained in memory unless they are either explicitly released or are released along with the other objects in their object group.", + "types": [ + { + "id": "ScriptId", + "type": "string", + "description": "Unique script identifier." + }, + { + "id": "RemoteObjectId", + "type": "string", + "description": "Unique object identifier." + }, + { + "id": "UnserializableValue", + "type": "string", + "enum": ["Infinity", "NaN", "-Infinity", "-0"], + "description": "Primitive value which cannot be JSON-stringified." + }, + { + "id": "RemoteObject", + "type": "object", + "description": "Mirror object referencing original JavaScript object.", + "properties": [ + { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." }, + { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "weakmap", "weakset", "iterator", "generator", "error", "proxy", "promise", "typedarray"], "description": "Object subtype hint. Specified for object type values only." }, + { "name": "className", "type": "string", "optional": true, "description": "Object class (constructor) name. Specified for object type values only." }, + { "name": "value", "type": "any", "optional": true, "description": "Remote object value in case of primitive values or JSON values (if it was requested)." }, + { "name": "unserializableValue", "$ref": "UnserializableValue", "optional": true, "description": "Primitive value which can not be JSON-stringified does not have value, but gets this property." }, + { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, + { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Unique object identifier (for non-primitive values)." }, + { "name": "preview", "$ref": "ObjectPreview", "optional": true, "description": "Preview containing abbreviated property values. Specified for object type values only.", "experimental": true }, + { "name": "customPreview", "$ref": "CustomPreview", "optional": true, "experimental": true} + ] + }, + { + "id": "CustomPreview", + "type": "object", + "experimental": true, + "properties": [ + { "name": "header", "type": "string"}, + { "name": "hasBody", "type": "boolean"}, + { "name": "formatterObjectId", "$ref": "RemoteObjectId"}, + { "name": "bindRemoteObjectFunctionId", "$ref": "RemoteObjectId" }, + { "name": "configObjectId", "$ref": "RemoteObjectId", "optional": true } + ] + }, + { + "id": "ObjectPreview", + "type": "object", + "experimental": true, + "description": "Object containing abbreviated remote object value.", + "properties": [ + { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." }, + { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "weakmap", "weakset", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for object type values only." }, + { "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, + { "name": "overflow", "type": "boolean", "description": "True iff some of the properties or entries of the original object did not fit." }, + { "name": "properties", "type": "array", "items": { "$ref": "PropertyPreview" }, "description": "List of the properties." }, + { "name": "entries", "type": "array", "items": { "$ref": "EntryPreview" }, "optional": true, "description": "List of the entries. Specified for map and set subtype values only." } + ] + }, + { + "id": "PropertyPreview", + "type": "object", + "experimental": true, + "properties": [ + { "name": "name", "type": "string", "description": "Property name." }, + { "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol", "accessor"], "description": "Object type. Accessor means that the property itself is an accessor property." }, + { "name": "value", "type": "string", "optional": true, "description": "User-friendly property value string." }, + { "name": "valuePreview", "$ref": "ObjectPreview", "optional": true, "description": "Nested value preview." }, + { "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "weakmap", "weakset", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for object type values only." } + ] + }, + { + "id": "EntryPreview", + "type": "object", + "experimental": true, + "properties": [ + { "name": "key", "$ref": "ObjectPreview", "optional": true, "description": "Preview of the key. Specified for map-like collection entries." }, + { "name": "value", "$ref": "ObjectPreview", "description": "Preview of the value." } + ] + }, + { + "id": "PropertyDescriptor", + "type": "object", + "description": "Object property descriptor.", + "properties": [ + { "name": "name", "type": "string", "description": "Property name or symbol description." }, + { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." }, + { "name": "writable", "type": "boolean", "optional": true, "description": "True if the value associated with the property may be changed (data descriptors only)." }, + { "name": "get", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a getter for the property, or undefined if there is no getter (accessor descriptors only)." }, + { "name": "set", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a setter for the property, or undefined if there is no setter (accessor descriptors only)." }, + { "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." }, + { "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." }, + { "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, + { "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object." }, + { "name": "symbol", "$ref": "RemoteObject", "optional": true, "description": "Property symbol object, if the property is of the symbol type." } + ] + }, + { + "id": "InternalPropertyDescriptor", + "type": "object", + "description": "Object internal property descriptor. This property isn't normally visible in JavaScript code.", + "properties": [ + { "name": "name", "type": "string", "description": "Conventional property name." }, + { "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." } + ] + }, + { + "id": "CallArgument", + "type": "object", + "description": "Represents function call argument. Either remote object id objectId, primitive value, unserializable primitive value or neither of (for undefined) them should be specified.", + "properties": [ + { "name": "value", "type": "any", "optional": true, "description": "Primitive value or serializable javascript object." }, + { "name": "unserializableValue", "$ref": "UnserializableValue", "optional": true, "description": "Primitive value which can not be JSON-stringified." }, + { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Remote object handle." } + ] + }, + { + "id": "ExecutionContextId", + "type": "integer", + "description": "Id of an execution context." + }, + { + "id": "ExecutionContextDescription", + "type": "object", + "description": "Description of an isolated world.", + "properties": [ + { "name": "id", "$ref": "ExecutionContextId", "description": "Unique id of the execution context. It can be used to specify in which execution context script evaluation should be performed." }, + { "name": "origin", "type": "string", "description": "Execution context origin." }, + { "name": "name", "type": "string", "description": "Human readable name describing given context." }, + { "name": "auxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." } + ] + }, + { + "id": "ExceptionDetails", + "type": "object", + "description": "Detailed information about exception (or error) that was thrown during script compilation or execution.", + "properties": [ + { "name": "exceptionId", "type": "integer", "description": "Exception id." }, + { "name": "text", "type": "string", "description": "Exception text, which should be used together with exception object when available." }, + { "name": "lineNumber", "type": "integer", "description": "Line number of the exception location (0-based)." }, + { "name": "columnNumber", "type": "integer", "description": "Column number of the exception location (0-based)." }, + { "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Script ID of the exception location." }, + { "name": "url", "type": "string", "optional": true, "description": "URL of the exception location, to be used when the script was not reported." }, + { "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace if available." }, + { "name": "exception", "$ref": "RemoteObject", "optional": true, "description": "Exception object if available." }, + { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Identifier of the context where exception happened." } + ] + }, + { + "id": "Timestamp", + "type": "number", + "description": "Number of milliseconds since epoch." + }, + { + "id": "CallFrame", + "type": "object", + "description": "Stack entry for runtime errors and assertions.", + "properties": [ + { "name": "functionName", "type": "string", "description": "JavaScript function name." }, + { "name": "scriptId", "$ref": "ScriptId", "description": "JavaScript script id." }, + { "name": "url", "type": "string", "description": "JavaScript script name or url." }, + { "name": "lineNumber", "type": "integer", "description": "JavaScript script line number (0-based)." }, + { "name": "columnNumber", "type": "integer", "description": "JavaScript script column number (0-based)." } + ] + }, + { + "id": "StackTrace", + "type": "object", + "description": "Call frames for assertions or error messages.", + "properties": [ + { "name": "description", "type": "string", "optional": true, "description": "String label of this stack trace. For async traces this may be a name of the function that initiated the async call." }, + { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "JavaScript function name." }, + { "name": "parent", "$ref": "StackTrace", "optional": true, "description": "Asynchronous JavaScript stack trace that preceded this stack, if available." }, + { "name": "parentId", "$ref": "StackTraceId", "optional": true, "experimental": true, "description": "Asynchronous JavaScript stack trace that preceded this stack, if available." } + ] + }, + { + "id": "UniqueDebuggerId", + "type": "string", + "description": "Unique identifier of current debugger.", + "experimental": true + }, + { + "id": "StackTraceId", + "type": "object", + "description": "If debuggerId is set stack trace comes from another debugger and can be resolved there. This allows to track cross-debugger calls. See Runtime.StackTrace and Debugger.paused for usages.", + "properties": [ + { "name": "id", "type": "string" }, + { "name": "debuggerId", "$ref": "UniqueDebuggerId", "optional": true } + ], + "experimental": true + } + ], + "commands": [ + { + "name": "evaluate", + "parameters": [ + { "name": "expression", "type": "string", "description": "Expression to evaluate." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, + { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." }, + { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides setPauseOnException state." }, + { "name": "contextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform evaluation. If the parameter is omitted the evaluation will be performed in the context of the inspected page." }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." }, + { "name": "userGesture", "type": "boolean", "optional": true, "description": "Whether execution should be treated as initiated by user in the UI." }, + { "name": "awaitPromise", "type": "boolean", "optional":true, "description": "Whether execution should await for resulting value and return once awaited promise is resolved." } + ], + "returns": [ + { "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} + ], + "description": "Evaluates expression on global object." + }, + { + "name": "awaitPromise", + "parameters": [ + { "name": "promiseObjectId", "$ref": "RemoteObjectId", "description": "Identifier of the promise." }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." } + ], + "returns": [ + { "name": "result", "$ref": "RemoteObject", "description": "Promise result. Will contain rejected value if promise was rejected." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details if stack strace is available."} + ], + "description": "Add handler to promise with given promise object id." + }, + { + "name": "callFunctionOn", + "parameters": [ + { "name": "functionDeclaration", "type": "string", "description": "Declaration of the function to call." }, + { "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Identifier of the object to call function on. Either objectId or executionContextId should be specified." }, + { "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." }, + { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides setPauseOnException state." }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." }, + { "name": "userGesture", "type": "boolean", "optional": true, "description": "Whether execution should be treated as initiated by user in the UI." }, + { "name": "awaitPromise", "type": "boolean", "optional":true, "description": "Whether execution should await for resulting value and return once awaited promise is resolved." }, + { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies execution context which global object will be used to call function on. Either executionContextId or objectId should be specified." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects. If objectGroup is not specified and objectId is, objectGroup will be inherited from object." } + ], + "returns": [ + { "name": "result", "$ref": "RemoteObject", "description": "Call result." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} + ], + "description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object." + }, + { + "name": "getProperties", + "parameters": [ + { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." }, + { "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." }, + { "name": "accessorPropertiesOnly", "optional": true, "type": "boolean", "description": "If true, returns accessor properties (with getter/setter) only; internal properties are not returned either.", "experimental": true }, + { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the results." } + ], + "returns": [ + { "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor" }, "description": "Object properties." }, + { "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor" }, "description": "Internal object properties (only of the element itself)." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} + ], + "description": "Returns properties of a given object. Object group of the result is inherited from the target object." + }, + { + "name": "releaseObject", + "parameters": [ + { "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to release." } + ], + "description": "Releases remote object with given id." + }, + { + "name": "releaseObjectGroup", + "parameters": [ + { "name": "objectGroup", "type": "string", "description": "Symbolic object group name." } + ], + "description": "Releases all remote objects that belong to a given group." + }, + { + "name": "runIfWaitingForDebugger", + "description": "Tells inspected instance to run if it was waiting for debugger to attach." + }, + { + "name": "enable", + "description": "Enables reporting of execution contexts creation by means of executionContextCreated event. When the reporting gets enabled the event will be sent immediately for each existing execution context." + }, + { + "name": "disable", + "description": "Disables reporting of execution contexts creation." + }, + { + "name": "discardConsoleEntries", + "description": "Discards collected exceptions and console API calls." + }, + { + "name": "setCustomObjectFormatterEnabled", + "parameters": [ + { + "name": "enabled", + "type": "boolean" + } + ], + "experimental": true + }, + { + "name": "compileScript", + "parameters": [ + { "name": "expression", "type": "string", "description": "Expression to compile." }, + { "name": "sourceURL", "type": "string", "description": "Source url to be set for the script." }, + { "name": "persistScript", "type": "boolean", "description": "Specifies whether the compiled script should be persisted." }, + { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform script run. If the parameter is omitted the evaluation will be performed in the context of the inspected page." } + ], + "returns": [ + { "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Id of the script." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} + ], + "description": "Compiles expression." + }, + { + "name": "runScript", + "parameters": [ + { "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to run." }, + { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform script run. If the parameter is omitted the evaluation will be performed in the context of the inspected page." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, + { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides setPauseOnException state." }, + { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." }, + { "name": "awaitPromise", "type": "boolean", "optional": true, "description": "Whether execution should await for resulting value and return once awaited promise is resolved." } + ], + "returns": [ + { "name": "result", "$ref": "RemoteObject", "description": "Run result." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} + ], + "description": "Runs script with given id in a given context." + }, + { + "name": "queryObjects", + "parameters": [ + { "name": "prototypeObjectId", "$ref": "RemoteObjectId", "description": "Identifier of the prototype to return objects for." } + ], + "returns": [ + { "name": "objects", "$ref": "RemoteObject", "description": "Array with objects." } + ] + }, + { + "name": "globalLexicalScopeNames", + "parameters": [ + { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to lookup global scope variables." } + ], + "returns": [ + { "name": "names", "type": "array", "items": { "type": "string" } } + ], + "description": "Returns all let, const and class variables from global scope." + } + ], + "events": [ + { + "name": "executionContextCreated", + "parameters": [ + { "name": "context", "$ref": "ExecutionContextDescription", "description": "A newly created execution context." } + ], + "description": "Issued when new execution context is created." + }, + { + "name": "executionContextDestroyed", + "parameters": [ + { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Id of the destroyed context" } + ], + "description": "Issued when execution context is destroyed." + }, + { + "name": "executionContextsCleared", + "description": "Issued when all executionContexts were cleared in browser" + }, + { + "name": "exceptionThrown", + "description": "Issued when exception was thrown and unhandled.", + "parameters": [ + { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp of the exception." }, + { "name": "exceptionDetails", "$ref": "ExceptionDetails" } + ] + }, + { + "name": "exceptionRevoked", + "description": "Issued when unhandled exception was revoked.", + "parameters": [ + { "name": "reason", "type": "string", "description": "Reason describing why exception was revoked." }, + { "name": "exceptionId", "type": "integer", "description": "The id of revoked exception, as reported in exceptionThrown." } + ] + }, + { + "name": "consoleAPICalled", + "description": "Issued when console API was called.", + "parameters": [ + { "name": "type", "type": "string", "enum": ["log", "debug", "info", "error", "warning", "dir", "dirxml", "table", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert", "profile", "profileEnd", "count", "timeEnd"], "description": "Type of the call." }, + { "name": "args", "type": "array", "items": { "$ref": "RemoteObject" }, "description": "Call arguments." }, + { "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Identifier of the context where the call was made." }, + { "name": "timestamp", "$ref": "Timestamp", "description": "Call timestamp." }, + { "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "Stack trace captured when the call was made." }, + { "name": "context", "type": "string", "optional": true, "experimental": true, "description": "Console context descriptor for calls on non-default console context (not console.*): 'anonymous#unique-logger-id' for call on unnamed context, 'name#unique-logger-id' for call on named context." } + ] + }, + { + "name": "inspectRequested", + "description": "Issued when object should be inspected (for example, as a result of inspect() command line API call).", + "parameters": [ + { "name": "object", "$ref": "RemoteObject" }, + { "name": "hints", "type": "object" } + ] + } + ] + }, + { + "domain": "Debugger", + "description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing breakpoints, stepping through execution, exploring stack traces, etc.", + "dependencies": ["Runtime"], + "types": [ + { + "id": "BreakpointId", + "type": "string", + "description": "Breakpoint identifier." + }, + { + "id": "CallFrameId", + "type": "string", + "description": "Call frame identifier." + }, + { + "id": "Location", + "type": "object", + "properties": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier as reported in the Debugger.scriptParsed." }, + { "name": "lineNumber", "type": "integer", "description": "Line number in the script (0-based)." }, + { "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the script (0-based)." } + ], + "description": "Location in the source code." + }, + { + "id": "ScriptPosition", + "experimental": true, + "type": "object", + "properties": [ + { "name": "lineNumber", "type": "integer" }, + { "name": "columnNumber", "type": "integer" } + ], + "description": "Location in the source code." + }, + { + "id": "CallFrame", + "type": "object", + "properties": [ + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused." }, + { "name": "functionName", "type": "string", "description": "Name of the JavaScript function called on this call frame." }, + { "name": "functionLocation", "$ref": "Location", "optional": true, "description": "Location in the source code." }, + { "name": "location", "$ref": "Location", "description": "Location in the source code." }, + { "name": "url", "type": "string", "description": "JavaScript script name or url." }, + { "name": "scopeChain", "type": "array", "items": { "$ref": "Scope" }, "description": "Scope chain for this call frame." }, + { "name": "this", "$ref": "Runtime.RemoteObject", "description": "this object for this call frame." }, + { "name": "returnValue", "$ref": "Runtime.RemoteObject", "optional": true, "description": "The value being returned, if the function is at return point." } + ], + "description": "JavaScript call frame. Array of call frames form the call stack." + }, + { + "id": "Scope", + "type": "object", + "properties": [ + { "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch", "block", "script", "eval", "module"], "description": "Scope type." }, + { "name": "object", "$ref": "Runtime.RemoteObject", "description": "Object representing the scope. For global and with scopes it represents the actual object; for the rest of the scopes, it is artificial transient object enumerating scope variables as its properties." }, + { "name": "name", "type": "string", "optional": true }, + { "name": "startLocation", "$ref": "Location", "optional": true, "description": "Location in the source code where scope starts" }, + { "name": "endLocation", "$ref": "Location", "optional": true, "description": "Location in the source code where scope ends" } + ], + "description": "Scope description." + }, + { + "id": "SearchMatch", + "type": "object", + "description": "Search match for resource.", + "properties": [ + { "name": "lineNumber", "type": "number", "description": "Line number in resource content." }, + { "name": "lineContent", "type": "string", "description": "Line with match content." } + ] + }, + { + "id": "BreakLocation", + "type": "object", + "properties": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier as reported in the Debugger.scriptParsed." }, + { "name": "lineNumber", "type": "integer", "description": "Line number in the script (0-based)." }, + { "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the script (0-based)." }, + { "name": "type", "type": "string", "enum": [ "debuggerStatement", "call", "return" ], "optional": true } + ] + } + ], + "commands": [ + { + "name": "enable", + "returns": [ + { "name": "debuggerId", "$ref": "Runtime.UniqueDebuggerId", "experimental": true, "description": "Unique identifier of the debugger." } + ], + "description": "Enables debugger for the given page. Clients should not assume that the debugging has been enabled until the result for this command is received." + }, + { + "name": "disable", + "description": "Disables debugger for given page." + }, + { + "name": "setBreakpointsActive", + "parameters": [ + { "name": "active", "type": "boolean", "description": "New value for breakpoints active state." } + ], + "description": "Activates / deactivates all breakpoints on the page." + }, + { + "name": "setSkipAllPauses", + "parameters": [ + { "name": "skip", "type": "boolean", "description": "New value for skip pauses state." } + ], + "description": "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc)." + }, + { + "name": "setBreakpointByUrl", + "parameters": [ + { "name": "lineNumber", "type": "integer", "description": "Line number to set breakpoint at." }, + { "name": "url", "type": "string", "optional": true, "description": "URL of the resources to set breakpoint on." }, + { "name": "urlRegex", "type": "string", "optional": true, "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either url or urlRegex must be specified." }, + { "name": "scriptHash", "type": "string", "optional": true, "description": "Script hash of the resources to set breakpoint on." }, + { "name": "columnNumber", "type": "integer", "optional": true, "description": "Offset in the line to set breakpoint at." }, + { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } + ], + "returns": [ + { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, + { "name": "locations", "type": "array", "items": { "$ref": "Location" }, "description": "List of the locations this breakpoint resolved into upon addition." } + ], + "description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in locations property. Further matching script parsing will result in subsequent breakpointResolved events issued. This logical breakpoint will survive page reloads." + }, + { + "name": "setBreakpoint", + "parameters": [ + { "name": "location", "$ref": "Location", "description": "Location to set breakpoint in." }, + { "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } + ], + "returns": [ + { "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, + { "name": "actualLocation", "$ref": "Location", "description": "Location this breakpoint resolved into." } + ], + "description": "Sets JavaScript breakpoint at a given location." + }, + { + "name": "removeBreakpoint", + "parameters": [ + { "name": "breakpointId", "$ref": "BreakpointId" } + ], + "description": "Removes JavaScript breakpoint." + }, + { + "name": "getPossibleBreakpoints", + "parameters": [ + { "name": "start", "$ref": "Location", "description": "Start of range to search possible breakpoint locations in." }, + { "name": "end", "$ref": "Location", "optional": true, "description": "End of range to search possible breakpoint locations in (excluding). When not specified, end of scripts is used as end of range." }, + { "name": "restrictToFunction", "type": "boolean", "optional": true, "description": "Only consider locations which are in the same (non-nested) function as start." } + ], + "returns": [ + { "name": "locations", "type": "array", "items": { "$ref": "BreakLocation" }, "description": "List of the possible breakpoint locations." } + ], + "description": "Returns possible locations for breakpoint. scriptId in start and end range locations should be the same." + }, + { + "name": "continueToLocation", + "parameters": [ + { "name": "location", "$ref": "Location", "description": "Location to continue to." }, + { "name": "targetCallFrames", "type": "string", "enum": ["any", "current"], "optional": true } + ], + "description": "Continues execution until specific location is reached." + }, + { + "name": "pauseOnAsyncCall", + "parameters": [ + { "name": "parentStackTraceId", "$ref": "Runtime.StackTraceId", "description": "Debugger will pause when async call with given stack trace is started." } + ], + "experimental": true + }, + { + "name": "stepOver", + "description": "Steps over the statement." + }, + { + "name": "stepInto", + "parameters": [ + { "name": "breakOnAsyncCall", "type": "boolean", "optional": true, "experimental": true, "description": "Debugger will issue additional Debugger.paused notification if any async task is scheduled before next pause." } + ], + "description": "Steps into the function call." + }, + { + "name": "stepOut", + "description": "Steps out of the function call." + }, + { + "name": "pause", + "description": "Stops on the next JavaScript statement." + }, + { + "name": "scheduleStepIntoAsync", + "description": "This method is deprecated - use Debugger.stepInto with breakOnAsyncCall and Debugger.pauseOnAsyncTask instead. Steps into next scheduled async task if any is scheduled before next pause. Returns success when async task is actually scheduled, returns error if no task were scheduled or another scheduleStepIntoAsync was called.", + "experimental": true + }, + { + "name": "resume", + "description": "Resumes JavaScript execution." + }, + { + "name": "getStackTrace", + "parameters": [ + { "name": "stackTraceId", "$ref": "Runtime.StackTraceId" } + ], + "returns": [ + { "name": "stackTrace", "$ref": "Runtime.StackTrace" } + ], + "description": "Returns stack trace with given stackTraceId.", + "experimental": true + }, + { + "name": "searchInContent", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to search in." }, + { "name": "query", "type": "string", "description": "String to search for." }, + { "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." }, + { "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." } + ], + "returns": [ + { "name": "result", "type": "array", "items": { "$ref": "SearchMatch" }, "description": "List of search matches." } + ], + "description": "Searches for given string in script content." + }, + { + "name": "setScriptSource", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to edit." }, + { "name": "scriptSource", "type": "string", "description": "New content of the script." }, + { "name": "dryRun", "type": "boolean", "optional": true, "description": " If true the change will not actually be applied. Dry run may be used to get result description without actually modifying the code." } + ], + "returns": [ + { "name": "callFrames", "type": "array", "optional": true, "items": { "$ref": "CallFrame" }, "description": "New stack trace in case editing has happened while VM was stopped." }, + { "name": "stackChanged", "type": "boolean", "optional": true, "description": "Whether current call stack was modified after applying the changes." }, + { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." }, + { "name": "asyncStackTraceId", "$ref": "Runtime.StackTraceId", "optional": true, "experimental": true, "description": "Async stack trace, if any." }, + { "name": "exceptionDetails", "optional": true, "$ref": "Runtime.ExceptionDetails", "description": "Exception details if any." } + ], + "description": "Edits JavaScript source live." + }, + { + "name": "restartFrame", + "parameters": [ + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." } + ], + "returns": [ + { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "New stack trace." }, + { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." }, + { "name": "asyncStackTraceId", "$ref": "Runtime.StackTraceId", "optional": true, "experimental": true, "description": "Async stack trace, if any." } + ], + "description": "Restarts particular call frame from the beginning." + }, + { + "name": "getScriptSource", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to get source for." } + ], + "returns": [ + { "name": "scriptSource", "type": "string", "description": "Script source." } + ], + "description": "Returns source for the script with given id." + }, + { + "name": "setPauseOnExceptions", + "parameters": [ + { "name": "state", "type": "string", "enum": ["none", "uncaught", "all"], "description": "Pause on exceptions mode." } + ], + "description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or no exceptions. Initial pause on exceptions state is none." + }, + { + "name": "evaluateOnCallFrame", + "parameters": [ + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." }, + { "name": "expression", "type": "string", "description": "Expression to evaluate." }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using releaseObjectGroup)." }, + { "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false." }, + { "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides setPauseOnException state." }, + { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, + { "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." }, + { "name": "throwOnSideEffect", "type": "boolean", "optional": true, "description": "Whether to throw an exception if side effect cannot be ruled out during evaluation." } + ], + "returns": [ + { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." }, + { "name": "exceptionDetails", "$ref": "Runtime.ExceptionDetails", "optional": true, "description": "Exception details."} + ], + "description": "Evaluates expression on a given call frame." + }, + { + "name": "setVariableValue", + "parameters": [ + { "name": "scopeNumber", "type": "integer", "description": "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' scope types are allowed. Other scopes could be manipulated manually." }, + { "name": "variableName", "type": "string", "description": "Variable name." }, + { "name": "newValue", "$ref": "Runtime.CallArgument", "description": "New variable value." }, + { "name": "callFrameId", "$ref": "CallFrameId", "description": "Id of callframe that holds variable." } + ], + "description": "Changes value of variable in a callframe. Object-based scopes are not supported and must be mutated manually." + }, + { + "name": "setReturnValue", + "parameters": [ + { "name": "newValue", "$ref": "Runtime.CallArgument", "description": "New return value." } + ], + "experimental": true, + "description": "Changes return value in top frame. Available only at return break position." + }, + { + "name": "setAsyncCallStackDepth", + "parameters": [ + { "name": "maxDepth", "type": "integer", "description": "Maximum depth of async call stacks. Setting to 0 will effectively disable collecting async call stacks (default)." } + ], + "description": "Enables or disables async call stacks tracking." + }, + { + "name": "setBlackboxPatterns", + "parameters": [ + { "name": "patterns", "type": "array", "items": { "type": "string" }, "description": "Array of regexps that will be used to check script url for blackbox state." } + ], + "experimental": true, + "description": "Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in scripts with url matching one of the patterns. VM will try to leave blackboxed script by performing 'step in' several times, finally resorting to 'step out' if unsuccessful." + }, + { + "name": "setBlackboxedRanges", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script." }, + { "name": "positions", "type": "array", "items": { "$ref": "ScriptPosition" } } + ], + "experimental": true, + "description": "Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted scripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful. Positions array contains positions where blackbox state is changed. First interval isn't blackboxed. Array should be sorted." + } + ], + "events": [ + { + "name": "scriptParsed", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." }, + { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, + { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, + { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, + { "name": "endLine", "type": "integer", "description": "Last line of the script." }, + { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, + { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context." }, + { "name": "hash", "type": "string", "description": "Content hash of the script."}, + { "name": "executionContextAuxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." }, + { "name": "isLiveEdit", "type": "boolean", "optional": true, "description": "True, if this script is generated as a result of the live edit operation.", "experimental": true }, + { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, + { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL." }, + { "name": "isModule", "type": "boolean", "optional": true, "description": "True, if this script is ES6 module." }, + { "name": "length", "type": "integer", "optional": true, "description": "This script length." }, + { "name": "stackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "JavaScript top stack frame of where the script parsed event was triggered if available.", "experimental": true } + ], + "description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger." + }, + { + "name": "scriptFailedToParse", + "parameters": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." }, + { "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, + { "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, + { "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, + { "name": "endLine", "type": "integer", "description": "Last line of the script." }, + { "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, + { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context." }, + { "name": "hash", "type": "string", "description": "Content hash of the script."}, + { "name": "executionContextAuxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." }, + { "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, + { "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL." }, + { "name": "isModule", "type": "boolean", "optional": true, "description": "True, if this script is ES6 module." }, + { "name": "length", "type": "integer", "optional": true, "description": "This script length." }, + { "name": "stackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "JavaScript top stack frame of where the script parsed event was triggered if available.", "experimental": true } + ], + "description": "Fired when virtual machine fails to parse the script." + }, + { + "name": "breakpointResolved", + "parameters": [ + { "name": "breakpointId", "$ref": "BreakpointId", "description": "Breakpoint unique identifier." }, + { "name": "location", "$ref": "Location", "description": "Actual breakpoint location." } + ], + "description": "Fired when breakpoint is resolved to an actual script and location." + }, + { + "name": "paused", + "parameters": [ + { "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." }, + { "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "assert", "debugCommand", "promiseRejection", "OOM", "other", "ambiguous" ], "description": "Pause reason." }, + { "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." }, + { "name": "hitBreakpoints", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Hit breakpoints IDs" }, + { "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." }, + { "name": "asyncStackTraceId", "$ref": "Runtime.StackTraceId", "optional": true, "experimental": true, "description": "Async stack trace, if any." }, + { "name": "asyncCallStackTraceId", "$ref": "Runtime.StackTraceId", "optional": true, "experimental": true, "description": "Just scheduled async call will have this stack trace as parent stack during async execution. This field is available only after Debugger.stepInto call with breakOnAsynCall flag." } + ], + "description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria." + }, + { + "name": "resumed", + "description": "Fired when the virtual machine resumed execution." + } + ] + }, + { + "domain": "Console", + "description": "This domain is deprecated - use Runtime or Log instead.", + "dependencies": ["Runtime"], + "deprecated": true, + "types": [ + { + "id": "ConsoleMessage", + "type": "object", + "description": "Console message.", + "properties": [ + { "name": "source", "type": "string", "enum": ["xml", "javascript", "network", "console-api", "storage", "appcache", "rendering", "security", "other", "deprecation", "worker"], "description": "Message source." }, + { "name": "level", "type": "string", "enum": ["log", "warning", "error", "debug", "info"], "description": "Message severity." }, + { "name": "text", "type": "string", "description": "Message text." }, + { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." }, + { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message (1-based)." }, + { "name": "column", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message (1-based)." } + ] + } + ], + "commands": [ + { + "name": "enable", + "description": "Enables console domain, sends the messages collected so far to the client by means of the messageAdded notification." + }, + { + "name": "disable", + "description": "Disables console domain, prevents further console messages from being reported to the client." + }, + { + "name": "clearMessages", + "description": "Does nothing." + } + ], + "events": [ + { + "name": "messageAdded", + "parameters": [ + { "name": "message", "$ref": "ConsoleMessage", "description": "Console message that has been added." } + ], + "description": "Issued when new console message is added." + } + ] + }, + { + "domain": "Profiler", + "dependencies": ["Runtime", "Debugger"], + "types": [ + { + "id": "ProfileNode", + "type": "object", + "description": "Profile node. Holds callsite information, execution statistics and child nodes.", + "properties": [ + { "name": "id", "type": "integer", "description": "Unique id of the node." }, + { "name": "callFrame", "$ref": "Runtime.CallFrame", "description": "Function location." }, + { "name": "hitCount", "type": "integer", "optional": true, "description": "Number of samples where this node was on top of the call stack." }, + { "name": "children", "type": "array", "items": { "type": "integer" }, "optional": true, "description": "Child node ids." }, + { "name": "deoptReason", "type": "string", "optional": true, "description": "The reason of being not optimized. The function may be deoptimized or marked as don't optimize."}, + { "name": "positionTicks", "type": "array", "items": { "$ref": "PositionTickInfo" }, "optional": true, "description": "An array of source position ticks." } + ] + }, + { + "id": "Profile", + "type": "object", + "description": "Profile.", + "properties": [ + { "name": "nodes", "type": "array", "items": { "$ref": "ProfileNode" }, "description": "The list of profile nodes. First item is the root node." }, + { "name": "startTime", "type": "number", "description": "Profiling start timestamp in microseconds." }, + { "name": "endTime", "type": "number", "description": "Profiling end timestamp in microseconds." }, + { "name": "samples", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Ids of samples top nodes." }, + { "name": "timeDeltas", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Time intervals between adjacent samples in microseconds. The first delta is relative to the profile startTime." } + ] + }, + { + "id": "PositionTickInfo", + "type": "object", + "description": "Specifies a number of samples attributed to a certain source position.", + "properties": [ + { "name": "line", "type": "integer", "description": "Source line number (1-based)." }, + { "name": "ticks", "type": "integer", "description": "Number of samples attributed to the source line." } + ] + }, + { "id": "CoverageRange", + "type": "object", + "description": "Coverage data for a source range.", + "properties": [ + { "name": "startOffset", "type": "integer", "description": "JavaScript script source offset for the range start." }, + { "name": "endOffset", "type": "integer", "description": "JavaScript script source offset for the range end." }, + { "name": "count", "type": "integer", "description": "Collected execution count of the source range." } + ] + }, + { "id": "FunctionCoverage", + "type": "object", + "description": "Coverage data for a JavaScript function.", + "properties": [ + { "name": "functionName", "type": "string", "description": "JavaScript function name." }, + { "name": "ranges", "type": "array", "items": { "$ref": "CoverageRange" }, "description": "Source ranges inside the function with coverage data." }, + { "name": "isBlockCoverage", "type": "boolean", "description": "Whether coverage data for this function has block granularity." } + ] + }, + { + "id": "ScriptCoverage", + "type": "object", + "description": "Coverage data for a JavaScript script.", + "properties": [ + { "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "JavaScript script id." }, + { "name": "url", "type": "string", "description": "JavaScript script name or url." }, + { "name": "functions", "type": "array", "items": { "$ref": "FunctionCoverage" }, "description": "Functions contained in the script that has coverage data." } + ] + } + ], + "commands": [ + { + "name": "enable" + }, + { + "name": "disable" + }, + { + "name": "setSamplingInterval", + "parameters": [ + { "name": "interval", "type": "integer", "description": "New sampling interval in microseconds." } + ], + "description": "Changes CPU profiler sampling interval. Must be called before CPU profiles recording started." + }, + { + "name": "start" + }, + { + "name": "stop", + "returns": [ + { "name": "profile", "$ref": "Profile", "description": "Recorded profile." } + ] + }, + { + "name": "startPreciseCoverage", + "parameters": [ + { "name": "callCount", "type": "boolean", "optional": true, "description": "Collect accurate call counts beyond simple 'covered' or 'not covered'." }, + { "name": "detailed", "type": "boolean", "optional": true, "description": "Collect block-based coverage." } + ], + "description": "Enable precise code coverage. Coverage data for JavaScript executed before enabling precise code coverage may be incomplete. Enabling prevents running optimized code and resets execution counters." + }, + { + "name": "stopPreciseCoverage", + "description": "Disable precise code coverage. Disabling releases unnecessary execution count records and allows executing optimized code." + }, + { + "name": "takePreciseCoverage", + "returns": [ + { "name": "result", "type": "array", "items": { "$ref": "ScriptCoverage" }, "description": "Coverage data for the current isolate." } + ], + "description": "Collect coverage data for the current isolate, and resets execution counters. Precise code coverage needs to have started." + }, + { + "name": "getBestEffortCoverage", + "returns": [ + { "name": "result", "type": "array", "items": { "$ref": "ScriptCoverage" }, "description": "Coverage data for the current isolate." } + ], + "description": "Collect coverage data for the current isolate. The coverage data may be incomplete due to garbage collection." + } + ], + "events": [ + { + "name": "consoleProfileStarted", + "parameters": [ + { "name": "id", "type": "string" }, + { "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profile()." }, + { "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." } + ], + "description": "Sent when new profile recording is started using console.profile() call." + }, + { + "name": "consoleProfileFinished", + "parameters": [ + { "name": "id", "type": "string" }, + { "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profileEnd()." }, + { "name": "profile", "$ref": "Profile" }, + { "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." } + ] + } + ] + }, + { + "domain": "HeapProfiler", + "dependencies": ["Runtime"], + "experimental": true, + "types": [ + { + "id": "HeapSnapshotObjectId", + "type": "string", + "description": "Heap snapshot object id." + }, + { + "id": "SamplingHeapProfileNode", + "type": "object", + "description": "Sampling Heap Profile node. Holds callsite information, allocation statistics and child nodes.", + "properties": [ + { "name": "callFrame", "$ref": "Runtime.CallFrame", "description": "Function location." }, + { "name": "selfSize", "type": "number", "description": "Allocations size in bytes for the node excluding children." }, + { "name": "children", "type": "array", "items": { "$ref": "SamplingHeapProfileNode" }, "description": "Child nodes." } + ] + }, + { + "id": "SamplingHeapProfile", + "type": "object", + "description": "Profile.", + "properties": [ + { "name": "head", "$ref": "SamplingHeapProfileNode" } + ] + } + ], + "commands": [ + { + "name": "enable" + }, + { + "name": "disable" + }, + { + "name": "startTrackingHeapObjects", + "parameters": [ + { "name": "trackAllocations", "type": "boolean", "optional": true } + ] + }, + { + "name": "stopTrackingHeapObjects", + "parameters": [ + { "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken when the tracking is stopped." } + ] + }, + { + "name": "takeHeapSnapshot", + "parameters": [ + { "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken." } + ] + }, + { + "name": "collectGarbage" + }, + { + "name": "getObjectByHeapObjectId", + "parameters": [ + { "name": "objectId", "$ref": "HeapSnapshotObjectId" }, + { "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." } + ], + "returns": [ + { "name": "result", "$ref": "Runtime.RemoteObject", "description": "Evaluation result." } + ] + }, + { + "name": "addInspectedHeapObject", + "parameters": [ + { "name": "heapObjectId", "$ref": "HeapSnapshotObjectId", "description": "Heap snapshot object id to be accessible by means of $x command line API." } + ], + "description": "Enables console to refer to the node with given id via $x (see Command Line API for more details $x functions)." + }, + { + "name": "getHeapObjectId", + "parameters": [ + { "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Identifier of the object to get heap object id for." } + ], + "returns": [ + { "name": "heapSnapshotObjectId", "$ref": "HeapSnapshotObjectId", "description": "Id of the heap snapshot object corresponding to the passed remote object id." } + ] + }, + { + "name": "startSampling", + "parameters": [ + { "name": "samplingInterval", "type": "number", "optional": true, "description": "Average sample interval in bytes. Poisson distribution is used for the intervals. The default value is 32768 bytes." } + ] + }, + { + "name": "stopSampling", + "returns": [ + { "name": "profile", "$ref": "SamplingHeapProfile", "description": "Recorded sampling heap profile." } + ] + }, + { + "name": "getSamplingProfile", + "returns": [ + { "name": "profile", "$ref": "SamplingHeapProfile", "description": "Return the sampling profile being collected." } + ] + } + ], + "events": [ + { + "name": "addHeapSnapshotChunk", + "parameters": [ + { "name": "chunk", "type": "string" } + ] + }, + { + "name": "resetProfiles" + }, + { + "name": "reportHeapSnapshotProgress", + "parameters": [ + { "name": "done", "type": "integer" }, + { "name": "total", "type": "integer" }, + { "name": "finished", "type": "boolean", "optional": true } + ] + }, + { + "name": "lastSeenObjectId", + "description": "If heap objects tracking has been started then backend regularly sends a current value for last seen object id and corresponding timestamp. If the were changes in the heap since last event then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.", + "parameters": [ + { "name": "lastSeenObjectId", "type": "integer" }, + { "name": "timestamp", "type": "number" } + ] + }, + { + "name": "heapStatsUpdate", + "description": "If heap objects tracking has been started then backend may send update for one or more fragments", + "parameters": [ + { "name": "statsUpdate", "type": "array", "items": { "type": "integer" }, "description": "An array of triplets. Each triplet describes a fragment. The first integer is the fragment index, the second integer is a total count of objects for the fragment, the third integer is a total size of the objects for the fragment."} + ] + } + ] + }] +} diff --git a/deps/include/js_protocol.pdl b/deps/include/js_protocol.pdl new file mode 100755 index 0000000..6efcf78 --- /dev/null +++ b/deps/include/js_protocol.pdl @@ -0,0 +1,1766 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +version + major 1 + minor 3 + +# This domain is deprecated - use Runtime or Log instead. +deprecated domain Console + depends on Runtime + + # Console message. + type ConsoleMessage extends object + properties + # Message source. + enum source + xml + javascript + network + console-api + storage + appcache + rendering + security + other + deprecation + worker + # Message severity. + enum level + log + warning + error + debug + info + # Message text. + string text + # URL of the message origin. + optional string url + # Line number in the resource that generated this message (1-based). + optional integer line + # Column number in the resource that generated this message (1-based). + optional integer column + + # Does nothing. + command clearMessages + + # Disables console domain, prevents further console messages from being reported to the client. + command disable + + # Enables console domain, sends the messages collected so far to the client by means of the + # `messageAdded` notification. + command enable + + # Issued when new console message is added. + event messageAdded + parameters + # Console message that has been added. + ConsoleMessage message + +# Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing +# breakpoints, stepping through execution, exploring stack traces, etc. +domain Debugger + depends on Runtime + + # Breakpoint identifier. + type BreakpointId extends string + + # Call frame identifier. + type CallFrameId extends string + + # Location in the source code. + type Location extends object + properties + # Script identifier as reported in the `Debugger.scriptParsed`. + Runtime.ScriptId scriptId + # Line number in the script (0-based). + integer lineNumber + # Column number in the script (0-based). + optional integer columnNumber + + # Location in the source code. + experimental type ScriptPosition extends object + properties + integer lineNumber + integer columnNumber + + # Location range within one script. + experimental type LocationRange extends object + properties + Runtime.ScriptId scriptId + ScriptPosition start + ScriptPosition end + + # JavaScript call frame. Array of call frames form the call stack. + type CallFrame extends object + properties + # Call frame identifier. This identifier is only valid while the virtual machine is paused. + CallFrameId callFrameId + # Name of the JavaScript function called on this call frame. + string functionName + # Location in the source code. + optional Location functionLocation + # Location in the source code. + Location location + # JavaScript script name or url. + # Deprecated in favor of using the `location.scriptId` to resolve the URL via a previously + # sent `Debugger.scriptParsed` event. + deprecated string url + # Scope chain for this call frame. + array of Scope scopeChain + # `this` object for this call frame. + Runtime.RemoteObject this + # The value being returned, if the function is at return point. + optional Runtime.RemoteObject returnValue + # Valid only while the VM is paused and indicates whether this frame + # can be restarted or not. Note that a `true` value here does not + # guarantee that Debugger#restartFrame with this CallFrameId will be + # successful, but it is very likely. + experimental optional boolean canBeRestarted + + # Scope description. + type Scope extends object + properties + # Scope type. + enum type + global + local + with + closure + catch + block + script + eval + module + wasm-expression-stack + # Object representing the scope. For `global` and `with` scopes it represents the actual + # object; for the rest of the scopes, it is artificial transient object enumerating scope + # variables as its properties. + Runtime.RemoteObject object + optional string name + # Location in the source code where scope starts + optional Location startLocation + # Location in the source code where scope ends + optional Location endLocation + + # Search match for resource. + type SearchMatch extends object + properties + # Line number in resource content. + number lineNumber + # Line with match content. + string lineContent + + type BreakLocation extends object + properties + # Script identifier as reported in the `Debugger.scriptParsed`. + Runtime.ScriptId scriptId + # Line number in the script (0-based). + integer lineNumber + # Column number in the script (0-based). + optional integer columnNumber + optional enum type + debuggerStatement + call + return + + # Continues execution until specific location is reached. + command continueToLocation + parameters + # Location to continue to. + Location location + optional enum targetCallFrames + any + current + + # Disables debugger for given page. + command disable + + # Enables debugger for the given page. Clients should not assume that the debugging has been + # enabled until the result for this command is received. + command enable + parameters + # The maximum size in bytes of collected scripts (not referenced by other heap objects) + # the debugger can hold. Puts no limit if parameter is omitted. + experimental optional number maxScriptsCacheSize + returns + # Unique identifier of the debugger. + experimental Runtime.UniqueDebuggerId debuggerId + + # Evaluates expression on a given call frame. + command evaluateOnCallFrame + parameters + # Call frame identifier to evaluate on. + CallFrameId callFrameId + # Expression to evaluate. + string expression + # String object group name to put result into (allows rapid releasing resulting object handles + # using `releaseObjectGroup`). + optional string objectGroup + # Specifies whether command line API should be available to the evaluated expression, defaults + # to false. + optional boolean includeCommandLineAPI + # In silent mode exceptions thrown during evaluation are not reported and do not pause + # execution. Overrides `setPauseOnException` state. + optional boolean silent + # Whether the result is expected to be a JSON object that should be sent by value. + optional boolean returnByValue + # Whether preview should be generated for the result. + experimental optional boolean generatePreview + # Whether to throw an exception if side effect cannot be ruled out during evaluation. + optional boolean throwOnSideEffect + # Terminate execution after timing out (number of milliseconds). + experimental optional Runtime.TimeDelta timeout + returns + # Object wrapper for the evaluation result. + Runtime.RemoteObject result + # Exception details. + optional Runtime.ExceptionDetails exceptionDetails + + # Returns possible locations for breakpoint. scriptId in start and end range locations should be + # the same. + command getPossibleBreakpoints + parameters + # Start of range to search possible breakpoint locations in. + Location start + # End of range to search possible breakpoint locations in (excluding). When not specified, end + # of scripts is used as end of range. + optional Location end + # Only consider locations which are in the same (non-nested) function as start. + optional boolean restrictToFunction + returns + # List of the possible breakpoint locations. + array of BreakLocation locations + + # Returns source for the script with given id. + command getScriptSource + parameters + # Id of the script to get source for. + Runtime.ScriptId scriptId + returns + # Script source (empty in case of Wasm bytecode). + string scriptSource + # Wasm bytecode. + optional binary bytecode + + experimental type WasmDisassemblyChunk extends object + properties + # The next chunk of disassembled lines. + array of string lines + # The bytecode offsets describing the start of each line. + array of integer bytecodeOffsets + + experimental command disassembleWasmModule + parameters + # Id of the script to disassemble + Runtime.ScriptId scriptId + returns + # For large modules, return a stream from which additional chunks of + # disassembly can be read successively. + optional string streamId + # The total number of lines in the disassembly text. + integer totalNumberOfLines + # The offsets of all function bodies, in the format [start1, end1, + # start2, end2, ...] where all ends are exclusive. + array of integer functionBodyOffsets + # The first chunk of disassembly. + WasmDisassemblyChunk chunk + + # Disassemble the next chunk of lines for the module corresponding to the + # stream. If disassembly is complete, this API will invalidate the streamId + # and return an empty chunk. Any subsequent calls for the now invalid stream + # will return errors. + experimental command nextWasmDisassemblyChunk + parameters + string streamId + returns + # The next chunk of disassembly. + WasmDisassemblyChunk chunk + + # This command is deprecated. Use getScriptSource instead. + deprecated command getWasmBytecode + parameters + # Id of the Wasm script to get source for. + Runtime.ScriptId scriptId + returns + # Script source. + binary bytecode + + # Returns stack trace with given `stackTraceId`. + experimental command getStackTrace + parameters + Runtime.StackTraceId stackTraceId + returns + Runtime.StackTrace stackTrace + + # Stops on the next JavaScript statement. + command pause + + experimental deprecated command pauseOnAsyncCall + parameters + # Debugger will pause when async call with given stack trace is started. + Runtime.StackTraceId parentStackTraceId + + # Removes JavaScript breakpoint. + command removeBreakpoint + parameters + BreakpointId breakpointId + + # Restarts particular call frame from the beginning. The old, deprecated + # behavior of `restartFrame` is to stay paused and allow further CDP commands + # after a restart was scheduled. This can cause problems with restarting, so + # we now continue execution immediatly after it has been scheduled until we + # reach the beginning of the restarted frame. + # + # To stay back-wards compatible, `restartFrame` now expects a `mode` + # parameter to be present. If the `mode` parameter is missing, `restartFrame` + # errors out. + # + # The various return values are deprecated and `callFrames` is always empty. + # Use the call frames from the `Debugger#paused` events instead, that fires + # once V8 pauses at the beginning of the restarted function. + command restartFrame + parameters + # Call frame identifier to evaluate on. + CallFrameId callFrameId + # The `mode` parameter must be present and set to 'StepInto', otherwise + # `restartFrame` will error out. + experimental optional enum mode + # Pause at the beginning of the restarted function + StepInto + returns + # New stack trace. + deprecated array of CallFrame callFrames + # Async stack trace, if any. + deprecated optional Runtime.StackTrace asyncStackTrace + # Async stack trace, if any. + deprecated optional Runtime.StackTraceId asyncStackTraceId + + # Resumes JavaScript execution. + command resume + parameters + # Set to true to terminate execution upon resuming execution. In contrast + # to Runtime.terminateExecution, this will allows to execute further + # JavaScript (i.e. via evaluation) until execution of the paused code + # is actually resumed, at which point termination is triggered. + # If execution is currently not paused, this parameter has no effect. + optional boolean terminateOnResume + + # Searches for given string in script content. + command searchInContent + parameters + # Id of the script to search in. + Runtime.ScriptId scriptId + # String to search for. + string query + # If true, search is case sensitive. + optional boolean caseSensitive + # If true, treats string parameter as regex. + optional boolean isRegex + returns + # List of search matches. + array of SearchMatch result + + # Enables or disables async call stacks tracking. + command setAsyncCallStackDepth + parameters + # Maximum depth of async call stacks. Setting to `0` will effectively disable collecting async + # call stacks (default). + integer maxDepth + + # Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in + # scripts with url matching one of the patterns. VM will try to leave blackboxed script by + # performing 'step in' several times, finally resorting to 'step out' if unsuccessful. + experimental command setBlackboxPatterns + parameters + # Array of regexps that will be used to check script url for blackbox state. + array of string patterns + + # Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted + # scripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful. + # Positions array contains positions where blackbox state is changed. First interval isn't + # blackboxed. Array should be sorted. + experimental command setBlackboxedRanges + parameters + # Id of the script. + Runtime.ScriptId scriptId + array of ScriptPosition positions + + # Sets JavaScript breakpoint at a given location. + command setBreakpoint + parameters + # Location to set breakpoint in. + Location location + # Expression to use as a breakpoint condition. When specified, debugger will only stop on the + # breakpoint if this expression evaluates to true. + optional string condition + returns + # Id of the created breakpoint for further reference. + BreakpointId breakpointId + # Location this breakpoint resolved into. + Location actualLocation + + # Sets instrumentation breakpoint. + command setInstrumentationBreakpoint + parameters + # Instrumentation name. + enum instrumentation + beforeScriptExecution + beforeScriptWithSourceMapExecution + returns + # Id of the created breakpoint for further reference. + BreakpointId breakpointId + + # Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this + # command is issued, all existing parsed scripts will have breakpoints resolved and returned in + # `locations` property. Further matching script parsing will result in subsequent + # `breakpointResolved` events issued. This logical breakpoint will survive page reloads. + command setBreakpointByUrl + parameters + # Line number to set breakpoint at. + integer lineNumber + # URL of the resources to set breakpoint on. + optional string url + # Regex pattern for the URLs of the resources to set breakpoints on. Either `url` or + # `urlRegex` must be specified. + optional string urlRegex + # Script hash of the resources to set breakpoint on. + optional string scriptHash + # Offset in the line to set breakpoint at. + optional integer columnNumber + # Expression to use as a breakpoint condition. When specified, debugger will only stop on the + # breakpoint if this expression evaluates to true. + optional string condition + returns + # Id of the created breakpoint for further reference. + BreakpointId breakpointId + # List of the locations this breakpoint resolved into upon addition. + array of Location locations + + # Sets JavaScript breakpoint before each call to the given function. + # If another function was created from the same source as a given one, + # calling it will also trigger the breakpoint. + experimental command setBreakpointOnFunctionCall + parameters + # Function object id. + Runtime.RemoteObjectId objectId + # Expression to use as a breakpoint condition. When specified, debugger will + # stop on the breakpoint if this expression evaluates to true. + optional string condition + returns + # Id of the created breakpoint for further reference. + BreakpointId breakpointId + + # Activates / deactivates all breakpoints on the page. + command setBreakpointsActive + parameters + # New value for breakpoints active state. + boolean active + + # Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions, + # or caught exceptions, no exceptions. Initial pause on exceptions state is `none`. + command setPauseOnExceptions + parameters + # Pause on exceptions mode. + enum state + none + caught + uncaught + all + + # Changes return value in top frame. Available only at return break position. + experimental command setReturnValue + parameters + # New return value. + Runtime.CallArgument newValue + + # Edits JavaScript source live. + # + # In general, functions that are currently on the stack can not be edited with + # a single exception: If the edited function is the top-most stack frame and + # that is the only activation of that function on the stack. In this case + # the live edit will be successful and a `Debugger.restartFrame` for the + # top-most function is automatically triggered. + command setScriptSource + parameters + # Id of the script to edit. + Runtime.ScriptId scriptId + # New content of the script. + string scriptSource + # If true the change will not actually be applied. Dry run may be used to get result + # description without actually modifying the code. + optional boolean dryRun + # If true, then `scriptSource` is allowed to change the function on top of the stack + # as long as the top-most stack frame is the only activation of that function. + experimental optional boolean allowTopFrameEditing + returns + # New stack trace in case editing has happened while VM was stopped. + deprecated optional array of CallFrame callFrames + # Whether current call stack was modified after applying the changes. + deprecated optional boolean stackChanged + # Async stack trace, if any. + deprecated optional Runtime.StackTrace asyncStackTrace + # Async stack trace, if any. + deprecated optional Runtime.StackTraceId asyncStackTraceId + # Whether the operation was successful or not. Only `Ok` denotes a + # successful live edit while the other enum variants denote why + # the live edit failed. + experimental enum status + Ok + CompileError + BlockedByActiveGenerator + BlockedByActiveFunction + # Exception details if any. Only present when `status` is `CompileError`. + optional Runtime.ExceptionDetails exceptionDetails + + # Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc). + command setSkipAllPauses + parameters + # New value for skip pauses state. + boolean skip + + # Changes value of variable in a callframe. Object-based scopes are not supported and must be + # mutated manually. + command setVariableValue + parameters + # 0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' + # scope types are allowed. Other scopes could be manipulated manually. + integer scopeNumber + # Variable name. + string variableName + # New variable value. + Runtime.CallArgument newValue + # Id of callframe that holds variable. + CallFrameId callFrameId + + # Steps into the function call. + command stepInto + parameters + # Debugger will pause on the execution of the first async task which was scheduled + # before next pause. + experimental optional boolean breakOnAsyncCall + # The skipList specifies location ranges that should be skipped on step into. + experimental optional array of LocationRange skipList + + # Steps out of the function call. + command stepOut + + # Steps over the statement. + command stepOver + parameters + # The skipList specifies location ranges that should be skipped on step over. + experimental optional array of LocationRange skipList + + # Fired when breakpoint is resolved to an actual script and location. + event breakpointResolved + parameters + # Breakpoint unique identifier. + BreakpointId breakpointId + # Actual breakpoint location. + Location location + + # Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria. + event paused + parameters + # Call stack the virtual machine stopped on. + array of CallFrame callFrames + # Pause reason. + enum reason + ambiguous + assert + CSPViolation + debugCommand + DOM + EventListener + exception + instrumentation + OOM + other + promiseRejection + XHR + # Object containing break-specific auxiliary properties. + optional object data + # Hit breakpoints IDs + optional array of string hitBreakpoints + # Async stack trace, if any. + optional Runtime.StackTrace asyncStackTrace + # Async stack trace, if any. + experimental optional Runtime.StackTraceId asyncStackTraceId + # Never present, will be removed. + experimental deprecated optional Runtime.StackTraceId asyncCallStackTraceId + + # Fired when the virtual machine resumed execution. + event resumed + + # Enum of possible script languages. + type ScriptLanguage extends string + enum + JavaScript + WebAssembly + + # Debug symbols available for a wasm script. + type DebugSymbols extends object + properties + # Type of the debug symbols. + enum type + None + SourceMap + EmbeddedDWARF + ExternalDWARF + # URL of the external symbol source. + optional string externalURL + + # Fired when virtual machine fails to parse the script. + event scriptFailedToParse + parameters + # Identifier of the script parsed. + Runtime.ScriptId scriptId + # URL or name of the script parsed (if any). + string url + # Line offset of the script within the resource with given URL (for script tags). + integer startLine + # Column offset of the script within the resource with given URL. + integer startColumn + # Last line of the script. + integer endLine + # Length of the last line of the script. + integer endColumn + # Specifies script creation context. + Runtime.ExecutionContextId executionContextId + # Content hash of the script, SHA-256. + string hash + # Embedder-specific auxiliary data. + optional object executionContextAuxData + # URL of source map associated with script (if any). + optional string sourceMapURL + # True, if this script has sourceURL. + optional boolean hasSourceURL + # True, if this script is ES6 module. + optional boolean isModule + # This script length. + optional integer length + # JavaScript top stack frame of where the script parsed event was triggered if available. + experimental optional Runtime.StackTrace stackTrace + # If the scriptLanguage is WebAssembly, the code section offset in the module. + experimental optional integer codeOffset + # The language of the script. + experimental optional Debugger.ScriptLanguage scriptLanguage + # The name the embedder supplied for this script. + experimental optional string embedderName + + # Fired when virtual machine parses script. This event is also fired for all known and uncollected + # scripts upon enabling debugger. + event scriptParsed + parameters + # Identifier of the script parsed. + Runtime.ScriptId scriptId + # URL or name of the script parsed (if any). + string url + # Line offset of the script within the resource with given URL (for script tags). + integer startLine + # Column offset of the script within the resource with given URL. + integer startColumn + # Last line of the script. + integer endLine + # Length of the last line of the script. + integer endColumn + # Specifies script creation context. + Runtime.ExecutionContextId executionContextId + # Content hash of the script, SHA-256. + string hash + # Embedder-specific auxiliary data. + optional object executionContextAuxData + # True, if this script is generated as a result of the live edit operation. + experimental optional boolean isLiveEdit + # URL of source map associated with script (if any). + optional string sourceMapURL + # True, if this script has sourceURL. + optional boolean hasSourceURL + # True, if this script is ES6 module. + optional boolean isModule + # This script length. + optional integer length + # JavaScript top stack frame of where the script parsed event was triggered if available. + experimental optional Runtime.StackTrace stackTrace + # If the scriptLanguage is WebAssembly, the code section offset in the module. + experimental optional integer codeOffset + # The language of the script. + experimental optional Debugger.ScriptLanguage scriptLanguage + # If the scriptLanguage is WebASsembly, the source of debug symbols for the module. + experimental optional Debugger.DebugSymbols debugSymbols + # The name the embedder supplied for this script. + experimental optional string embedderName + +experimental domain HeapProfiler + depends on Runtime + + # Heap snapshot object id. + type HeapSnapshotObjectId extends string + + # Sampling Heap Profile node. Holds callsite information, allocation statistics and child nodes. + type SamplingHeapProfileNode extends object + properties + # Function location. + Runtime.CallFrame callFrame + # Allocations size in bytes for the node excluding children. + number selfSize + # Node id. Ids are unique across all profiles collected between startSampling and stopSampling. + integer id + # Child nodes. + array of SamplingHeapProfileNode children + + # A single sample from a sampling profile. + type SamplingHeapProfileSample extends object + properties + # Allocation size in bytes attributed to the sample. + number size + # Id of the corresponding profile tree node. + integer nodeId + # Time-ordered sample ordinal number. It is unique across all profiles retrieved + # between startSampling and stopSampling. + number ordinal + + # Sampling profile. + type SamplingHeapProfile extends object + properties + SamplingHeapProfileNode head + array of SamplingHeapProfileSample samples + + # Enables console to refer to the node with given id via $x (see Command Line API for more details + # $x functions). + command addInspectedHeapObject + parameters + # Heap snapshot object id to be accessible by means of $x command line API. + HeapSnapshotObjectId heapObjectId + + command collectGarbage + + command disable + + command enable + + command getHeapObjectId + parameters + # Identifier of the object to get heap object id for. + Runtime.RemoteObjectId objectId + returns + # Id of the heap snapshot object corresponding to the passed remote object id. + HeapSnapshotObjectId heapSnapshotObjectId + + command getObjectByHeapObjectId + parameters + HeapSnapshotObjectId objectId + # Symbolic group name that can be used to release multiple objects. + optional string objectGroup + returns + # Evaluation result. + Runtime.RemoteObject result + + command getSamplingProfile + returns + # Return the sampling profile being collected. + SamplingHeapProfile profile + + command startSampling + parameters + # Average sample interval in bytes. Poisson distribution is used for the intervals. The + # default value is 32768 bytes. + optional number samplingInterval + # By default, the sampling heap profiler reports only objects which are + # still alive when the profile is returned via getSamplingProfile or + # stopSampling, which is useful for determining what functions contribute + # the most to steady-state memory usage. This flag instructs the sampling + # heap profiler to also include information about objects discarded by + # major GC, which will show which functions cause large temporary memory + # usage or long GC pauses. + optional boolean includeObjectsCollectedByMajorGC + # By default, the sampling heap profiler reports only objects which are + # still alive when the profile is returned via getSamplingProfile or + # stopSampling, which is useful for determining what functions contribute + # the most to steady-state memory usage. This flag instructs the sampling + # heap profiler to also include information about objects discarded by + # minor GC, which is useful when tuning a latency-sensitive application + # for minimal GC activity. + optional boolean includeObjectsCollectedByMinorGC + + command startTrackingHeapObjects + parameters + optional boolean trackAllocations + + command stopSampling + returns + # Recorded sampling heap profile. + SamplingHeapProfile profile + + command stopTrackingHeapObjects + parameters + # If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken + # when the tracking is stopped. + optional boolean reportProgress + # Deprecated in favor of `exposeInternals`. + deprecated optional boolean treatGlobalObjectsAsRoots + # If true, numerical values are included in the snapshot + optional boolean captureNumericValue + # If true, exposes internals of the snapshot. + experimental optional boolean exposeInternals + + command takeHeapSnapshot + parameters + # If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken. + optional boolean reportProgress + # If true, a raw snapshot without artificial roots will be generated. + # Deprecated in favor of `exposeInternals`. + deprecated optional boolean treatGlobalObjectsAsRoots + # If true, numerical values are included in the snapshot + optional boolean captureNumericValue + # If true, exposes internals of the snapshot. + experimental optional boolean exposeInternals + + event addHeapSnapshotChunk + parameters + string chunk + + # If heap objects tracking has been started then backend may send update for one or more fragments + event heapStatsUpdate + parameters + # An array of triplets. Each triplet describes a fragment. The first integer is the fragment + # index, the second integer is a total count of objects for the fragment, the third integer is + # a total size of the objects for the fragment. + array of integer statsUpdate + + # If heap objects tracking has been started then backend regularly sends a current value for last + # seen object id and corresponding timestamp. If the were changes in the heap since last event + # then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event. + event lastSeenObjectId + parameters + integer lastSeenObjectId + number timestamp + + event reportHeapSnapshotProgress + parameters + integer done + integer total + optional boolean finished + + event resetProfiles + +domain Profiler + depends on Runtime + depends on Debugger + + # Profile node. Holds callsite information, execution statistics and child nodes. + type ProfileNode extends object + properties + # Unique id of the node. + integer id + # Function location. + Runtime.CallFrame callFrame + # Number of samples where this node was on top of the call stack. + optional integer hitCount + # Child node ids. + optional array of integer children + # The reason of being not optimized. The function may be deoptimized or marked as don't + # optimize. + optional string deoptReason + # An array of source position ticks. + optional array of PositionTickInfo positionTicks + + # Profile. + type Profile extends object + properties + # The list of profile nodes. First item is the root node. + array of ProfileNode nodes + # Profiling start timestamp in microseconds. + number startTime + # Profiling end timestamp in microseconds. + number endTime + # Ids of samples top nodes. + optional array of integer samples + # Time intervals between adjacent samples in microseconds. The first delta is relative to the + # profile startTime. + optional array of integer timeDeltas + + # Specifies a number of samples attributed to a certain source position. + type PositionTickInfo extends object + properties + # Source line number (1-based). + integer line + # Number of samples attributed to the source line. + integer ticks + + # Coverage data for a source range. + type CoverageRange extends object + properties + # JavaScript script source offset for the range start. + integer startOffset + # JavaScript script source offset for the range end. + integer endOffset + # Collected execution count of the source range. + integer count + + # Coverage data for a JavaScript function. + type FunctionCoverage extends object + properties + # JavaScript function name. + string functionName + # Source ranges inside the function with coverage data. + array of CoverageRange ranges + # Whether coverage data for this function has block granularity. + boolean isBlockCoverage + + # Coverage data for a JavaScript script. + type ScriptCoverage extends object + properties + # JavaScript script id. + Runtime.ScriptId scriptId + # JavaScript script name or url. + string url + # Functions contained in the script that has coverage data. + array of FunctionCoverage functions + + command disable + + command enable + + # Collect coverage data for the current isolate. The coverage data may be incomplete due to + # garbage collection. + command getBestEffortCoverage + returns + # Coverage data for the current isolate. + array of ScriptCoverage result + + # Changes CPU profiler sampling interval. Must be called before CPU profiles recording started. + command setSamplingInterval + parameters + # New sampling interval in microseconds. + integer interval + + command start + + # Enable precise code coverage. Coverage data for JavaScript executed before enabling precise code + # coverage may be incomplete. Enabling prevents running optimized code and resets execution + # counters. + command startPreciseCoverage + parameters + # Collect accurate call counts beyond simple 'covered' or 'not covered'. + optional boolean callCount + # Collect block-based coverage. + optional boolean detailed + # Allow the backend to send updates on its own initiative + optional boolean allowTriggeredUpdates + returns + # Monotonically increasing time (in seconds) when the coverage update was taken in the backend. + number timestamp + + command stop + returns + # Recorded profile. + Profile profile + + # Disable precise code coverage. Disabling releases unnecessary execution count records and allows + # executing optimized code. + command stopPreciseCoverage + + # Collect coverage data for the current isolate, and resets execution counters. Precise code + # coverage needs to have started. + command takePreciseCoverage + returns + # Coverage data for the current isolate. + array of ScriptCoverage result + # Monotonically increasing time (in seconds) when the coverage update was taken in the backend. + number timestamp + + event consoleProfileFinished + parameters + string id + # Location of console.profileEnd(). + Debugger.Location location + Profile profile + # Profile title passed as an argument to console.profile(). + optional string title + + # Sent when new profile recording is started using console.profile() call. + event consoleProfileStarted + parameters + string id + # Location of console.profile(). + Debugger.Location location + # Profile title passed as an argument to console.profile(). + optional string title + + # Reports coverage delta since the last poll (either from an event like this, or from + # `takePreciseCoverage` for the current isolate. May only be sent if precise code + # coverage has been started. This event can be trigged by the embedder to, for example, + # trigger collection of coverage data immediately at a certain point in time. + experimental event preciseCoverageDeltaUpdate + parameters + # Monotonically increasing time (in seconds) when the coverage update was taken in the backend. + number timestamp + # Identifier for distinguishing coverage events. + string occasion + # Coverage data for the current isolate. + array of ScriptCoverage result + +# Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. +# Evaluation results are returned as mirror object that expose object type, string representation +# and unique identifier that can be used for further object reference. Original objects are +# maintained in memory unless they are either explicitly released or are released along with the +# other objects in their object group. +domain Runtime + + # Unique script identifier. + type ScriptId extends string + + # Represents the value serialiazed by the WebDriver BiDi specification + # https://w3c.github.io/webdriver-bidi. + type WebDriverValue extends object + properties + enum type + undefined + null + string + number + boolean + bigint + regexp + date + symbol + array + object + function + map + set + weakmap + weakset + error + proxy + promise + typedarray + arraybuffer + node + window + optional any value + optional string objectId + + # Unique object identifier. + type RemoteObjectId extends string + + # Primitive value which cannot be JSON-stringified. Includes values `-0`, `NaN`, `Infinity`, + # `-Infinity`, and bigint literals. + type UnserializableValue extends string + + # Mirror object referencing original JavaScript object. + type RemoteObject extends object + properties + # Object type. + enum type + object + function + undefined + string + number + boolean + symbol + bigint + # Object subtype hint. Specified for `object` type values only. + # NOTE: If you change anything here, make sure to also update + # `subtype` in `ObjectPreview` and `PropertyPreview` below. + optional enum subtype + array + null + node + regexp + date + map + set + weakmap + weakset + iterator + generator + error + proxy + promise + typedarray + arraybuffer + dataview + webassemblymemory + wasmvalue + # Object class (constructor) name. Specified for `object` type values only. + optional string className + # Remote object value in case of primitive values or JSON values (if it was requested). + optional any value + # Primitive value which can not be JSON-stringified does not have `value`, but gets this + # property. + optional UnserializableValue unserializableValue + # String representation of the object. + optional string description + # WebDriver BiDi representation of the value. + experimental optional WebDriverValue webDriverValue + # Unique object identifier (for non-primitive values). + optional RemoteObjectId objectId + # Preview containing abbreviated property values. Specified for `object` type values only. + experimental optional ObjectPreview preview + experimental optional CustomPreview customPreview + + experimental type CustomPreview extends object + properties + # The JSON-stringified result of formatter.header(object, config) call. + # It contains json ML array that represents RemoteObject. + string header + # If formatter returns true as a result of formatter.hasBody call then bodyGetterId will + # contain RemoteObjectId for the function that returns result of formatter.body(object, config) call. + # The result value is json ML array. + optional RemoteObjectId bodyGetterId + + # Object containing abbreviated remote object value. + experimental type ObjectPreview extends object + properties + # Object type. + enum type + object + function + undefined + string + number + boolean + symbol + bigint + # Object subtype hint. Specified for `object` type values only. + optional enum subtype + array + null + node + regexp + date + map + set + weakmap + weakset + iterator + generator + error + proxy + promise + typedarray + arraybuffer + dataview + webassemblymemory + wasmvalue + # String representation of the object. + optional string description + # True iff some of the properties or entries of the original object did not fit. + boolean overflow + # List of the properties. + array of PropertyPreview properties + # List of the entries. Specified for `map` and `set` subtype values only. + optional array of EntryPreview entries + + experimental type PropertyPreview extends object + properties + # Property name. + string name + # Object type. Accessor means that the property itself is an accessor property. + enum type + object + function + undefined + string + number + boolean + symbol + accessor + bigint + # User-friendly property value string. + optional string value + # Nested value preview. + optional ObjectPreview valuePreview + # Object subtype hint. Specified for `object` type values only. + optional enum subtype + array + null + node + regexp + date + map + set + weakmap + weakset + iterator + generator + error + proxy + promise + typedarray + arraybuffer + dataview + webassemblymemory + wasmvalue + + experimental type EntryPreview extends object + properties + # Preview of the key. Specified for map-like collection entries. + optional ObjectPreview key + # Preview of the value. + ObjectPreview value + + # Object property descriptor. + type PropertyDescriptor extends object + properties + # Property name or symbol description. + string name + # The value associated with the property. + optional RemoteObject value + # True if the value associated with the property may be changed (data descriptors only). + optional boolean writable + # A function which serves as a getter for the property, or `undefined` if there is no getter + # (accessor descriptors only). + optional RemoteObject get + # A function which serves as a setter for the property, or `undefined` if there is no setter + # (accessor descriptors only). + optional RemoteObject set + # True if the type of this property descriptor may be changed and if the property may be + # deleted from the corresponding object. + boolean configurable + # True if this property shows up during enumeration of the properties on the corresponding + # object. + boolean enumerable + # True if the result was thrown during the evaluation. + optional boolean wasThrown + # True if the property is owned for the object. + optional boolean isOwn + # Property symbol object, if the property is of the `symbol` type. + optional RemoteObject symbol + + # Object internal property descriptor. This property isn't normally visible in JavaScript code. + type InternalPropertyDescriptor extends object + properties + # Conventional property name. + string name + # The value associated with the property. + optional RemoteObject value + + # Object private field descriptor. + experimental type PrivatePropertyDescriptor extends object + properties + # Private property name. + string name + # The value associated with the private property. + optional RemoteObject value + # A function which serves as a getter for the private property, + # or `undefined` if there is no getter (accessor descriptors only). + optional RemoteObject get + # A function which serves as a setter for the private property, + # or `undefined` if there is no setter (accessor descriptors only). + optional RemoteObject set + + # Represents function call argument. Either remote object id `objectId`, primitive `value`, + # unserializable primitive value or neither of (for undefined) them should be specified. + type CallArgument extends object + properties + # Primitive value or serializable javascript object. + optional any value + # Primitive value which can not be JSON-stringified. + optional UnserializableValue unserializableValue + # Remote object handle. + optional RemoteObjectId objectId + + # Id of an execution context. + type ExecutionContextId extends integer + + # Description of an isolated world. + type ExecutionContextDescription extends object + properties + # Unique id of the execution context. It can be used to specify in which execution context + # script evaluation should be performed. + ExecutionContextId id + # Execution context origin. + string origin + # Human readable name describing given context. + string name + # A system-unique execution context identifier. Unlike the id, this is unique across + # multiple processes, so can be reliably used to identify specific context while backend + # performs a cross-process navigation. + experimental string uniqueId + # Embedder-specific auxiliary data. + optional object auxData + + # Detailed information about exception (or error) that was thrown during script compilation or + # execution. + type ExceptionDetails extends object + properties + # Exception id. + integer exceptionId + # Exception text, which should be used together with exception object when available. + string text + # Line number of the exception location (0-based). + integer lineNumber + # Column number of the exception location (0-based). + integer columnNumber + # Script ID of the exception location. + optional ScriptId scriptId + # URL of the exception location, to be used when the script was not reported. + optional string url + # JavaScript stack trace if available. + optional StackTrace stackTrace + # Exception object if available. + optional RemoteObject exception + # Identifier of the context where exception happened. + optional ExecutionContextId executionContextId + # Dictionary with entries of meta data that the client associated + # with this exception, such as information about associated network + # requests, etc. + experimental optional object exceptionMetaData + + # Number of milliseconds since epoch. + type Timestamp extends number + + # Number of milliseconds. + type TimeDelta extends number + + # Stack entry for runtime errors and assertions. + type CallFrame extends object + properties + # JavaScript function name. + string functionName + # JavaScript script id. + ScriptId scriptId + # JavaScript script name or url. + string url + # JavaScript script line number (0-based). + integer lineNumber + # JavaScript script column number (0-based). + integer columnNumber + + # Call frames for assertions or error messages. + type StackTrace extends object + properties + # String label of this stack trace. For async traces this may be a name of the function that + # initiated the async call. + optional string description + # JavaScript function name. + array of CallFrame callFrames + # Asynchronous JavaScript stack trace that preceded this stack, if available. + optional StackTrace parent + # Asynchronous JavaScript stack trace that preceded this stack, if available. + experimental optional StackTraceId parentId + + # Unique identifier of current debugger. + experimental type UniqueDebuggerId extends string + + # If `debuggerId` is set stack trace comes from another debugger and can be resolved there. This + # allows to track cross-debugger calls. See `Runtime.StackTrace` and `Debugger.paused` for usages. + experimental type StackTraceId extends object + properties + string id + optional UniqueDebuggerId debuggerId + + # Add handler to promise with given promise object id. + command awaitPromise + parameters + # Identifier of the promise. + RemoteObjectId promiseObjectId + # Whether the result is expected to be a JSON object that should be sent by value. + optional boolean returnByValue + # Whether preview should be generated for the result. + optional boolean generatePreview + returns + # Promise result. Will contain rejected value if promise was rejected. + RemoteObject result + # Exception details if stack strace is available. + optional ExceptionDetails exceptionDetails + + # Calls function with given declaration on the given object. Object group of the result is + # inherited from the target object. + command callFunctionOn + parameters + # Declaration of the function to call. + string functionDeclaration + # Identifier of the object to call function on. Either objectId or executionContextId should + # be specified. + optional RemoteObjectId objectId + # Call arguments. All call arguments must belong to the same JavaScript world as the target + # object. + optional array of CallArgument arguments + # In silent mode exceptions thrown during evaluation are not reported and do not pause + # execution. Overrides `setPauseOnException` state. + optional boolean silent + # Whether the result is expected to be a JSON object which should be sent by value. + optional boolean returnByValue + # Whether preview should be generated for the result. + experimental optional boolean generatePreview + # Whether execution should be treated as initiated by user in the UI. + optional boolean userGesture + # Whether execution should `await` for resulting value and return once awaited promise is + # resolved. + optional boolean awaitPromise + # Specifies execution context which global object will be used to call function on. Either + # executionContextId or objectId should be specified. + optional ExecutionContextId executionContextId + # Symbolic group name that can be used to release multiple objects. If objectGroup is not + # specified and objectId is, objectGroup will be inherited from object. + optional string objectGroup + # Whether to throw an exception if side effect cannot be ruled out during evaluation. + experimental optional boolean throwOnSideEffect + # Whether the result should contain `webDriverValue`, serialized according to + # https://w3c.github.io/webdriver-bidi. This is mutually exclusive with `returnByValue`, but + # resulting `objectId` is still provided. + experimental optional boolean generateWebDriverValue + returns + # Call result. + RemoteObject result + # Exception details. + optional ExceptionDetails exceptionDetails + + # Compiles expression. + command compileScript + parameters + # Expression to compile. + string expression + # Source url to be set for the script. + string sourceURL + # Specifies whether the compiled script should be persisted. + boolean persistScript + # Specifies in which execution context to perform script run. If the parameter is omitted the + # evaluation will be performed in the context of the inspected page. + optional ExecutionContextId executionContextId + returns + # Id of the script. + optional ScriptId scriptId + # Exception details. + optional ExceptionDetails exceptionDetails + + # Disables reporting of execution contexts creation. + command disable + + # Discards collected exceptions and console API calls. + command discardConsoleEntries + + # Enables reporting of execution contexts creation by means of `executionContextCreated` event. + # When the reporting gets enabled the event will be sent immediately for each existing execution + # context. + command enable + + # Evaluates expression on global object. + command evaluate + parameters + # Expression to evaluate. + string expression + # Symbolic group name that can be used to release multiple objects. + optional string objectGroup + # Determines whether Command Line API should be available during the evaluation. + optional boolean includeCommandLineAPI + # In silent mode exceptions thrown during evaluation are not reported and do not pause + # execution. Overrides `setPauseOnException` state. + optional boolean silent + # Specifies in which execution context to perform evaluation. If the parameter is omitted the + # evaluation will be performed in the context of the inspected page. + # This is mutually exclusive with `uniqueContextId`, which offers an + # alternative way to identify the execution context that is more reliable + # in a multi-process environment. + optional ExecutionContextId contextId + # Whether the result is expected to be a JSON object that should be sent by value. + optional boolean returnByValue + # Whether preview should be generated for the result. + experimental optional boolean generatePreview + # Whether execution should be treated as initiated by user in the UI. + optional boolean userGesture + # Whether execution should `await` for resulting value and return once awaited promise is + # resolved. + optional boolean awaitPromise + # Whether to throw an exception if side effect cannot be ruled out during evaluation. + # This implies `disableBreaks` below. + experimental optional boolean throwOnSideEffect + # Terminate execution after timing out (number of milliseconds). + experimental optional TimeDelta timeout + # Disable breakpoints during execution. + experimental optional boolean disableBreaks + # Setting this flag to true enables `let` re-declaration and top-level `await`. + # Note that `let` variables can only be re-declared if they originate from + # `replMode` themselves. + experimental optional boolean replMode + # The Content Security Policy (CSP) for the target might block 'unsafe-eval' + # which includes eval(), Function(), setTimeout() and setInterval() + # when called with non-callable arguments. This flag bypasses CSP for this + # evaluation and allows unsafe-eval. Defaults to true. + experimental optional boolean allowUnsafeEvalBlockedByCSP + # An alternative way to specify the execution context to evaluate in. + # Compared to contextId that may be reused across processes, this is guaranteed to be + # system-unique, so it can be used to prevent accidental evaluation of the expression + # in context different than intended (e.g. as a result of navigation across process + # boundaries). + # This is mutually exclusive with `contextId`. + experimental optional string uniqueContextId + # Whether the result should be serialized according to https://w3c.github.io/webdriver-bidi. + experimental optional boolean generateWebDriverValue + returns + # Evaluation result. + RemoteObject result + # Exception details. + optional ExceptionDetails exceptionDetails + + # Returns the isolate id. + experimental command getIsolateId + returns + # The isolate id. + string id + + # Returns the JavaScript heap usage. + # It is the total usage of the corresponding isolate not scoped to a particular Runtime. + experimental command getHeapUsage + returns + # Used heap size in bytes. + number usedSize + # Allocated heap size in bytes. + number totalSize + + # Returns properties of a given object. Object group of the result is inherited from the target + # object. + command getProperties + parameters + # Identifier of the object to return properties for. + RemoteObjectId objectId + # If true, returns properties belonging only to the element itself, not to its prototype + # chain. + optional boolean ownProperties + # If true, returns accessor properties (with getter/setter) only; internal properties are not + # returned either. + experimental optional boolean accessorPropertiesOnly + # Whether preview should be generated for the results. + experimental optional boolean generatePreview + # If true, returns non-indexed properties only. + experimental optional boolean nonIndexedPropertiesOnly + returns + # Object properties. + array of PropertyDescriptor result + # Internal object properties (only of the element itself). + optional array of InternalPropertyDescriptor internalProperties + # Object private properties. + experimental optional array of PrivatePropertyDescriptor privateProperties + # Exception details. + optional ExceptionDetails exceptionDetails + + # Returns all let, const and class variables from global scope. + command globalLexicalScopeNames + parameters + # Specifies in which execution context to lookup global scope variables. + optional ExecutionContextId executionContextId + returns + array of string names + + command queryObjects + parameters + # Identifier of the prototype to return objects for. + RemoteObjectId prototypeObjectId + # Symbolic group name that can be used to release the results. + optional string objectGroup + returns + # Array with objects. + RemoteObject objects + + # Releases remote object with given id. + command releaseObject + parameters + # Identifier of the object to release. + RemoteObjectId objectId + + # Releases all remote objects that belong to a given group. + command releaseObjectGroup + parameters + # Symbolic object group name. + string objectGroup + + # Tells inspected instance to run if it was waiting for debugger to attach. + command runIfWaitingForDebugger + + # Runs script with given id in a given context. + command runScript + parameters + # Id of the script to run. + ScriptId scriptId + # Specifies in which execution context to perform script run. If the parameter is omitted the + # evaluation will be performed in the context of the inspected page. + optional ExecutionContextId executionContextId + # Symbolic group name that can be used to release multiple objects. + optional string objectGroup + # In silent mode exceptions thrown during evaluation are not reported and do not pause + # execution. Overrides `setPauseOnException` state. + optional boolean silent + # Determines whether Command Line API should be available during the evaluation. + optional boolean includeCommandLineAPI + # Whether the result is expected to be a JSON object which should be sent by value. + optional boolean returnByValue + # Whether preview should be generated for the result. + optional boolean generatePreview + # Whether execution should `await` for resulting value and return once awaited promise is + # resolved. + optional boolean awaitPromise + returns + # Run result. + RemoteObject result + # Exception details. + optional ExceptionDetails exceptionDetails + + # Enables or disables async call stacks tracking. + command setAsyncCallStackDepth + redirect Debugger + parameters + # Maximum depth of async call stacks. Setting to `0` will effectively disable collecting async + # call stacks (default). + integer maxDepth + + experimental command setCustomObjectFormatterEnabled + parameters + boolean enabled + + experimental command setMaxCallStackSizeToCapture + parameters + integer size + + # Terminate current or next JavaScript execution. + # Will cancel the termination when the outer-most script execution ends. + experimental command terminateExecution + + # If executionContextId is empty, adds binding with the given name on the + # global objects of all inspected contexts, including those created later, + # bindings survive reloads. + # Binding function takes exactly one argument, this argument should be string, + # in case of any other input, function throws an exception. + # Each binding function call produces Runtime.bindingCalled notification. + experimental command addBinding + parameters + string name + # If specified, the binding would only be exposed to the specified + # execution context. If omitted and `executionContextName` is not set, + # the binding is exposed to all execution contexts of the target. + # This parameter is mutually exclusive with `executionContextName`. + # Deprecated in favor of `executionContextName` due to an unclear use case + # and bugs in implementation (crbug.com/1169639). `executionContextId` will be + # removed in the future. + deprecated optional ExecutionContextId executionContextId + # If specified, the binding is exposed to the executionContext with + # matching name, even for contexts created after the binding is added. + # See also `ExecutionContext.name` and `worldName` parameter to + # `Page.addScriptToEvaluateOnNewDocument`. + # This parameter is mutually exclusive with `executionContextId`. + experimental optional string executionContextName + + # This method does not remove binding function from global object but + # unsubscribes current runtime agent from Runtime.bindingCalled notifications. + experimental command removeBinding + parameters + string name + + # This method tries to lookup and populate exception details for a + # JavaScript Error object. + # Note that the stackTrace portion of the resulting exceptionDetails will + # only be populated if the Runtime domain was enabled at the time when the + # Error was thrown. + experimental command getExceptionDetails + parameters + # The error object for which to resolve the exception details. + RemoteObjectId errorObjectId + returns + optional ExceptionDetails exceptionDetails + + # Notification is issued every time when binding is called. + experimental event bindingCalled + parameters + string name + string payload + # Identifier of the context where the call was made. + ExecutionContextId executionContextId + + # Issued when console API was called. + event consoleAPICalled + parameters + # Type of the call. + enum type + log + debug + info + error + warning + dir + dirxml + table + trace + clear + startGroup + startGroupCollapsed + endGroup + assert + profile + profileEnd + count + timeEnd + # Call arguments. + array of RemoteObject args + # Identifier of the context where the call was made. + ExecutionContextId executionContextId + # Call timestamp. + Timestamp timestamp + # Stack trace captured when the call was made. The async stack chain is automatically reported for + # the following call types: `assert`, `error`, `trace`, `warning`. For other types the async call + # chain can be retrieved using `Debugger.getStackTrace` and `stackTrace.parentId` field. + optional StackTrace stackTrace + # Console context descriptor for calls on non-default console context (not console.*): + # 'anonymous#unique-logger-id' for call on unnamed context, 'name#unique-logger-id' for call + # on named context. + experimental optional string context + + # Issued when unhandled exception was revoked. + event exceptionRevoked + parameters + # Reason describing why exception was revoked. + string reason + # The id of revoked exception, as reported in `exceptionThrown`. + integer exceptionId + + # Issued when exception was thrown and unhandled. + event exceptionThrown + parameters + # Timestamp of the exception. + Timestamp timestamp + ExceptionDetails exceptionDetails + + # Issued when new execution context is created. + event executionContextCreated + parameters + # A newly created execution context. + ExecutionContextDescription context + + # Issued when execution context is destroyed. + event executionContextDestroyed + parameters + # Id of the destroyed context + ExecutionContextId executionContextId + + # Issued when all executionContexts were cleared in browser + event executionContextsCleared + + # Issued when object should be inspected (for example, as a result of inspect() command line API + # call). + event inspectRequested + parameters + RemoteObject object + object hints + # Identifier of the context where the call was made. + experimental optional ExecutionContextId executionContextId + +# This domain is deprecated. +deprecated domain Schema + + # Description of the protocol domain. + type Domain extends object + properties + # Domain name. + string name + # Domain version. + string version + + # Returns supported domains. + command getDomains + returns + # List of supported domains. + array of Domain domains diff --git a/deps/include/libplatform/DEPS b/deps/include/libplatform/DEPS new file mode 100755 index 0000000..d8bcf99 --- /dev/null +++ b/deps/include/libplatform/DEPS @@ -0,0 +1,9 @@ +include_rules = [ + "+libplatform/libplatform-export.h", +] + +specific_include_rules = { + "libplatform\.h": [ + "+libplatform/v8-tracing.h", + ], +} diff --git a/deps/include/libplatform/libplatform-export.h b/deps/include/libplatform/libplatform-export.h new file mode 100755 index 0000000..1561843 --- /dev/null +++ b/deps/include/libplatform/libplatform-export.h @@ -0,0 +1,29 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_LIBPLATFORM_LIBPLATFORM_EXPORT_H_ +#define V8_LIBPLATFORM_LIBPLATFORM_EXPORT_H_ + +#if defined(_WIN32) + +#ifdef BUILDING_V8_PLATFORM_SHARED +#define V8_PLATFORM_EXPORT __declspec(dllexport) +#elif USING_V8_PLATFORM_SHARED +#define V8_PLATFORM_EXPORT __declspec(dllimport) +#else +#define V8_PLATFORM_EXPORT +#endif // BUILDING_V8_PLATFORM_SHARED + +#else // defined(_WIN32) + +// Setup for Linux shared library export. +#ifdef BUILDING_V8_PLATFORM_SHARED +#define V8_PLATFORM_EXPORT __attribute__((visibility("default"))) +#else +#define V8_PLATFORM_EXPORT +#endif + +#endif // defined(_WIN32) + +#endif // V8_LIBPLATFORM_LIBPLATFORM_EXPORT_H_ diff --git a/deps/include/libplatform/libplatform.h b/deps/include/libplatform/libplatform.h new file mode 100755 index 0000000..9ec60c0 --- /dev/null +++ b/deps/include/libplatform/libplatform.h @@ -0,0 +1,106 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_LIBPLATFORM_LIBPLATFORM_H_ +#define V8_LIBPLATFORM_LIBPLATFORM_H_ + +#include + +#include "libplatform/libplatform-export.h" +#include "libplatform/v8-tracing.h" +#include "v8-platform.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { +namespace platform { + +enum class IdleTaskSupport { kDisabled, kEnabled }; +enum class InProcessStackDumping { kDisabled, kEnabled }; + +enum class MessageLoopBehavior : bool { + kDoNotWait = false, + kWaitForWork = true +}; + +/** + * Returns a new instance of the default v8::Platform implementation. + * + * The caller will take ownership of the returned pointer. |thread_pool_size| + * is the number of worker threads to allocate for background jobs. If a value + * of zero is passed, a suitable default based on the current number of + * processors online will be chosen. + * If |idle_task_support| is enabled then the platform will accept idle + * tasks (IdleTasksEnabled will return true) and will rely on the embedder + * calling v8::platform::RunIdleTasks to process the idle tasks. + * If |tracing_controller| is nullptr, the default platform will create a + * v8::platform::TracingController instance and use it. + */ +V8_PLATFORM_EXPORT std::unique_ptr NewDefaultPlatform( + int thread_pool_size = 0, + IdleTaskSupport idle_task_support = IdleTaskSupport::kDisabled, + InProcessStackDumping in_process_stack_dumping = + InProcessStackDumping::kDisabled, + std::unique_ptr tracing_controller = {}); + +/** + * The same as NewDefaultPlatform but disables the worker thread pool. + * It must be used with the --single-threaded V8 flag. + */ +V8_PLATFORM_EXPORT std::unique_ptr +NewSingleThreadedDefaultPlatform( + IdleTaskSupport idle_task_support = IdleTaskSupport::kDisabled, + InProcessStackDumping in_process_stack_dumping = + InProcessStackDumping::kDisabled, + std::unique_ptr tracing_controller = {}); + +/** + * Returns a new instance of the default v8::JobHandle implementation. + * + * The job will be executed by spawning up to |num_worker_threads| many worker + * threads on the provided |platform| with the given |priority|. + */ +V8_PLATFORM_EXPORT std::unique_ptr NewDefaultJobHandle( + v8::Platform* platform, v8::TaskPriority priority, + std::unique_ptr job_task, size_t num_worker_threads); + +/** + * Pumps the message loop for the given isolate. + * + * The caller has to make sure that this is called from the right thread. + * Returns true if a task was executed, and false otherwise. If the call to + * PumpMessageLoop is nested within another call to PumpMessageLoop, only + * nestable tasks may run. Otherwise, any task may run. Unless requested through + * the |behavior| parameter, this call does not block if no task is pending. The + * |platform| has to be created using |NewDefaultPlatform|. + */ +V8_PLATFORM_EXPORT bool PumpMessageLoop( + v8::Platform* platform, v8::Isolate* isolate, + MessageLoopBehavior behavior = MessageLoopBehavior::kDoNotWait); + +/** + * Runs pending idle tasks for at most |idle_time_in_seconds| seconds. + * + * The caller has to make sure that this is called from the right thread. + * This call does not block if no task is pending. The |platform| has to be + * created using |NewDefaultPlatform|. + */ +V8_PLATFORM_EXPORT void RunIdleTasks(v8::Platform* platform, + v8::Isolate* isolate, + double idle_time_in_seconds); + +/** + * Notifies the given platform about the Isolate getting deleted soon. Has to be + * called for all Isolates which are deleted - unless we're shutting down the + * platform. + * + * The |platform| has to be created using |NewDefaultPlatform|. + * + */ +V8_PLATFORM_EXPORT void NotifyIsolateShutdown(v8::Platform* platform, + Isolate* isolate); + +} // namespace platform +} // namespace v8 + +#endif // V8_LIBPLATFORM_LIBPLATFORM_H_ diff --git a/deps/include/libplatform/v8-tracing.h b/deps/include/libplatform/v8-tracing.h new file mode 100755 index 0000000..1248932 --- /dev/null +++ b/deps/include/libplatform/v8-tracing.h @@ -0,0 +1,333 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_LIBPLATFORM_V8_TRACING_H_ +#define V8_LIBPLATFORM_V8_TRACING_H_ + +#include +#include +#include +#include +#include + +#include "libplatform/libplatform-export.h" +#include "v8-platform.h" // NOLINT(build/include_directory) + +namespace perfetto { +namespace trace_processor { +class TraceProcessorStorage; +} +class TracingSession; +} + +namespace v8 { + +namespace base { +class Mutex; +} // namespace base + +namespace platform { +namespace tracing { + +class TraceEventListener; + +const int kTraceMaxNumArgs = 2; + +class V8_PLATFORM_EXPORT TraceObject { + public: + union ArgValue { + uint64_t as_uint; + int64_t as_int; + double as_double; + const void* as_pointer; + const char* as_string; + }; + + TraceObject() = default; + ~TraceObject(); + void Initialize( + char phase, const uint8_t* category_enabled_flag, const char* name, + const char* scope, uint64_t id, uint64_t bind_id, int num_args, + const char** arg_names, const uint8_t* arg_types, + const uint64_t* arg_values, + std::unique_ptr* arg_convertables, + unsigned int flags, int64_t timestamp, int64_t cpu_timestamp); + void UpdateDuration(int64_t timestamp, int64_t cpu_timestamp); + void InitializeForTesting( + char phase, const uint8_t* category_enabled_flag, const char* name, + const char* scope, uint64_t id, uint64_t bind_id, int num_args, + const char** arg_names, const uint8_t* arg_types, + const uint64_t* arg_values, + std::unique_ptr* arg_convertables, + unsigned int flags, int pid, int tid, int64_t ts, int64_t tts, + uint64_t duration, uint64_t cpu_duration); + + int pid() const { return pid_; } + int tid() const { return tid_; } + char phase() const { return phase_; } + const uint8_t* category_enabled_flag() const { + return category_enabled_flag_; + } + const char* name() const { return name_; } + const char* scope() const { return scope_; } + uint64_t id() const { return id_; } + uint64_t bind_id() const { return bind_id_; } + int num_args() const { return num_args_; } + const char** arg_names() { return arg_names_; } + uint8_t* arg_types() { return arg_types_; } + ArgValue* arg_values() { return arg_values_; } + std::unique_ptr* arg_convertables() { + return arg_convertables_; + } + unsigned int flags() const { return flags_; } + int64_t ts() { return ts_; } + int64_t tts() { return tts_; } + uint64_t duration() { return duration_; } + uint64_t cpu_duration() { return cpu_duration_; } + + private: + int pid_; + int tid_; + char phase_; + const char* name_; + const char* scope_; + const uint8_t* category_enabled_flag_; + uint64_t id_; + uint64_t bind_id_; + int num_args_ = 0; + const char* arg_names_[kTraceMaxNumArgs]; + uint8_t arg_types_[kTraceMaxNumArgs]; + ArgValue arg_values_[kTraceMaxNumArgs]; + std::unique_ptr + arg_convertables_[kTraceMaxNumArgs]; + char* parameter_copy_storage_ = nullptr; + unsigned int flags_; + int64_t ts_; + int64_t tts_; + uint64_t duration_; + uint64_t cpu_duration_; + + // Disallow copy and assign + TraceObject(const TraceObject&) = delete; + void operator=(const TraceObject&) = delete; +}; + +class V8_PLATFORM_EXPORT TraceWriter { + public: + TraceWriter() = default; + virtual ~TraceWriter() = default; + virtual void AppendTraceEvent(TraceObject* trace_event) = 0; + virtual void Flush() = 0; + + static TraceWriter* CreateJSONTraceWriter(std::ostream& stream); + static TraceWriter* CreateJSONTraceWriter(std::ostream& stream, + const std::string& tag); + + static TraceWriter* CreateSystemInstrumentationTraceWriter(); + + private: + // Disallow copy and assign + TraceWriter(const TraceWriter&) = delete; + void operator=(const TraceWriter&) = delete; +}; + +class V8_PLATFORM_EXPORT TraceBufferChunk { + public: + explicit TraceBufferChunk(uint32_t seq); + + void Reset(uint32_t new_seq); + bool IsFull() const { return next_free_ == kChunkSize; } + TraceObject* AddTraceEvent(size_t* event_index); + TraceObject* GetEventAt(size_t index) { return &chunk_[index]; } + + uint32_t seq() const { return seq_; } + size_t size() const { return next_free_; } + + static const size_t kChunkSize = 64; + + private: + size_t next_free_ = 0; + TraceObject chunk_[kChunkSize]; + uint32_t seq_; + + // Disallow copy and assign + TraceBufferChunk(const TraceBufferChunk&) = delete; + void operator=(const TraceBufferChunk&) = delete; +}; + +class V8_PLATFORM_EXPORT TraceBuffer { + public: + TraceBuffer() = default; + virtual ~TraceBuffer() = default; + + virtual TraceObject* AddTraceEvent(uint64_t* handle) = 0; + virtual TraceObject* GetEventByHandle(uint64_t handle) = 0; + virtual bool Flush() = 0; + + static const size_t kRingBufferChunks = 1024; + + static TraceBuffer* CreateTraceBufferRingBuffer(size_t max_chunks, + TraceWriter* trace_writer); + + private: + // Disallow copy and assign + TraceBuffer(const TraceBuffer&) = delete; + void operator=(const TraceBuffer&) = delete; +}; + +// Options determines how the trace buffer stores data. +enum TraceRecordMode { + // Record until the trace buffer is full. + RECORD_UNTIL_FULL, + + // Record until the user ends the trace. The trace buffer is a fixed size + // and we use it as a ring buffer during recording. + RECORD_CONTINUOUSLY, + + // Record until the trace buffer is full, but with a huge buffer size. + RECORD_AS_MUCH_AS_POSSIBLE, + + // Echo to console. Events are discarded. + ECHO_TO_CONSOLE, +}; + +class V8_PLATFORM_EXPORT TraceConfig { + public: + typedef std::vector StringList; + + static TraceConfig* CreateDefaultTraceConfig(); + + TraceConfig() : enable_systrace_(false), enable_argument_filter_(false) {} + TraceRecordMode GetTraceRecordMode() const { return record_mode_; } + const StringList& GetEnabledCategories() const { + return included_categories_; + } + bool IsSystraceEnabled() const { return enable_systrace_; } + bool IsArgumentFilterEnabled() const { return enable_argument_filter_; } + + void SetTraceRecordMode(TraceRecordMode mode) { record_mode_ = mode; } + void EnableSystrace() { enable_systrace_ = true; } + void EnableArgumentFilter() { enable_argument_filter_ = true; } + + void AddIncludedCategory(const char* included_category); + + bool IsCategoryGroupEnabled(const char* category_group) const; + + private: + TraceRecordMode record_mode_; + bool enable_systrace_ : 1; + bool enable_argument_filter_ : 1; + StringList included_categories_; + + // Disallow copy and assign + TraceConfig(const TraceConfig&) = delete; + void operator=(const TraceConfig&) = delete; +}; + +#if defined(_MSC_VER) +#define V8_PLATFORM_NON_EXPORTED_BASE(code) \ + __pragma(warning(suppress : 4275)) code +#else +#define V8_PLATFORM_NON_EXPORTED_BASE(code) code +#endif // defined(_MSC_VER) + +class V8_PLATFORM_EXPORT TracingController + : public V8_PLATFORM_NON_EXPORTED_BASE(v8::TracingController) { + public: + TracingController(); + ~TracingController() override; + +#if defined(V8_USE_PERFETTO) + // Must be called before StartTracing() if V8_USE_PERFETTO is true. Provides + // the output stream for the JSON trace data. + void InitializeForPerfetto(std::ostream* output_stream); + // Provide an optional listener for testing that will receive trace events. + // Must be called before StartTracing(). + void SetTraceEventListenerForTesting(TraceEventListener* listener); +#else // defined(V8_USE_PERFETTO) + // The pointer returned from GetCategoryGroupEnabled() points to a value with + // zero or more of the following bits. Used in this class only. The + // TRACE_EVENT macros should only use the value as a bool. These values must + // be in sync with macro values in TraceEvent.h in Blink. + enum CategoryGroupEnabledFlags { + // Category group enabled for the recording mode. + ENABLED_FOR_RECORDING = 1 << 0, + // Category group enabled by SetEventCallbackEnabled(). + ENABLED_FOR_EVENT_CALLBACK = 1 << 2, + // Category group enabled to export events to ETW. + ENABLED_FOR_ETW_EXPORT = 1 << 3 + }; + + // Takes ownership of |trace_buffer|. + void Initialize(TraceBuffer* trace_buffer); + + // v8::TracingController implementation. + const uint8_t* GetCategoryGroupEnabled(const char* category_group) override; + uint64_t AddTraceEvent( + char phase, const uint8_t* category_enabled_flag, const char* name, + const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args, + const char** arg_names, const uint8_t* arg_types, + const uint64_t* arg_values, + std::unique_ptr* arg_convertables, + unsigned int flags) override; + uint64_t AddTraceEventWithTimestamp( + char phase, const uint8_t* category_enabled_flag, const char* name, + const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args, + const char** arg_names, const uint8_t* arg_types, + const uint64_t* arg_values, + std::unique_ptr* arg_convertables, + unsigned int flags, int64_t timestamp) override; + void UpdateTraceEventDuration(const uint8_t* category_enabled_flag, + const char* name, uint64_t handle) override; + + static const char* GetCategoryGroupName(const uint8_t* category_enabled_flag); +#endif // !defined(V8_USE_PERFETTO) + + void AddTraceStateObserver( + v8::TracingController::TraceStateObserver* observer) override; + void RemoveTraceStateObserver( + v8::TracingController::TraceStateObserver* observer) override; + + void StartTracing(TraceConfig* trace_config); + void StopTracing(); + + protected: +#if !defined(V8_USE_PERFETTO) + virtual int64_t CurrentTimestampMicroseconds(); + virtual int64_t CurrentCpuTimestampMicroseconds(); +#endif // !defined(V8_USE_PERFETTO) + + private: +#if !defined(V8_USE_PERFETTO) + void UpdateCategoryGroupEnabledFlag(size_t category_index); + void UpdateCategoryGroupEnabledFlags(); +#endif // !defined(V8_USE_PERFETTO) + + std::unique_ptr mutex_; + std::unique_ptr trace_config_; + std::atomic_bool recording_{false}; + std::unordered_set observers_; + +#if defined(V8_USE_PERFETTO) + std::ostream* output_stream_ = nullptr; + std::unique_ptr + trace_processor_; + TraceEventListener* listener_for_testing_ = nullptr; + std::unique_ptr tracing_session_; +#else // !defined(V8_USE_PERFETTO) + std::unique_ptr trace_buffer_; +#endif // !defined(V8_USE_PERFETTO) + + // Disallow copy and assign + TracingController(const TracingController&) = delete; + void operator=(const TracingController&) = delete; +}; + +#undef V8_PLATFORM_NON_EXPORTED_BASE + +} // namespace tracing +} // namespace platform +} // namespace v8 + +#endif // V8_LIBPLATFORM_V8_TRACING_H_ diff --git a/deps/include/libplatform/vendor.go b/deps/include/libplatform/vendor.go new file mode 100755 index 0000000..12e7d71 --- /dev/null +++ b/deps/include/libplatform/vendor.go @@ -0,0 +1,3 @@ +// Generated by deps/upgrade_v8.py, DO NOT REMOVE/EDIT MANUALLY. +// Package libplatform is required to provide support for vendoring modules +package libplatform diff --git a/deps/include/v8-array-buffer.h b/deps/include/v8-array-buffer.h new file mode 100755 index 0000000..841bd02 --- /dev/null +++ b/deps/include/v8-array-buffer.h @@ -0,0 +1,471 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_ARRAY_BUFFER_H_ +#define INCLUDE_V8_ARRAY_BUFFER_H_ + +#include + +#include + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-object.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class SharedArrayBuffer; + +#ifndef V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT +// The number of required internal fields can be defined by embedder. +#define V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT 2 +#endif + +enum class ArrayBufferCreationMode { kInternalized, kExternalized }; + +/** + * A wrapper around the backing store (i.e. the raw memory) of an array buffer. + * See a document linked in http://crbug.com/v8/9908 for more information. + * + * The allocation and destruction of backing stores is generally managed by + * V8. Clients should always use standard C++ memory ownership types (i.e. + * std::unique_ptr and std::shared_ptr) to manage lifetimes of backing stores + * properly, since V8 internal objects may alias backing stores. + * + * This object does not keep the underlying |ArrayBuffer::Allocator| alive by + * default. Use Isolate::CreateParams::array_buffer_allocator_shared when + * creating the Isolate to make it hold a reference to the allocator itself. + */ +class V8_EXPORT BackingStore : public v8::internal::BackingStoreBase { + public: + ~BackingStore(); + + /** + * Return a pointer to the beginning of the memory block for this backing + * store. The pointer is only valid as long as this backing store object + * lives. + */ + void* Data() const; + + /** + * The length (in bytes) of this backing store. + */ + size_t ByteLength() const; + + /** + * Indicates whether the backing store was created for an ArrayBuffer or + * a SharedArrayBuffer. + */ + bool IsShared() const; + + /** + * Prevent implicit instantiation of operator delete with size_t argument. + * The size_t argument would be incorrect because ptr points to the + * internal BackingStore object. + */ + void operator delete(void* ptr) { ::operator delete(ptr); } + + /** + * Wrapper around ArrayBuffer::Allocator::Reallocate that preserves IsShared. + * Assumes that the backing_store was allocated by the ArrayBuffer allocator + * of the given isolate. + */ + static std::unique_ptr Reallocate( + v8::Isolate* isolate, std::unique_ptr backing_store, + size_t byte_length); + + /** + * This callback is used only if the memory block for a BackingStore cannot be + * allocated with an ArrayBuffer::Allocator. In such cases the destructor of + * the BackingStore invokes the callback to free the memory block. + */ + using DeleterCallback = void (*)(void* data, size_t length, + void* deleter_data); + + /** + * If the memory block of a BackingStore is static or is managed manually, + * then this empty deleter along with nullptr deleter_data can be passed to + * ArrayBuffer::NewBackingStore to indicate that. + * + * The manually managed case should be used with caution and only when it + * is guaranteed that the memory block freeing happens after detaching its + * ArrayBuffer. + */ + static void EmptyDeleter(void* data, size_t length, void* deleter_data); + + private: + /** + * See [Shared]ArrayBuffer::GetBackingStore and + * [Shared]ArrayBuffer::NewBackingStore. + */ + BackingStore(); +}; + +#if !defined(V8_IMMINENT_DEPRECATION_WARNINGS) +// Use v8::BackingStore::DeleterCallback instead. +using BackingStoreDeleterCallback = void (*)(void* data, size_t length, + void* deleter_data); + +#endif + +/** + * An instance of the built-in ArrayBuffer constructor (ES6 draft 15.13.5). + */ +class V8_EXPORT ArrayBuffer : public Object { + public: + /** + * A thread-safe allocator that V8 uses to allocate |ArrayBuffer|'s memory. + * The allocator is a global V8 setting. It has to be set via + * Isolate::CreateParams. + * + * Memory allocated through this allocator by V8 is accounted for as external + * memory by V8. Note that V8 keeps track of the memory for all internalized + * |ArrayBuffer|s. Responsibility for tracking external memory (using + * Isolate::AdjustAmountOfExternalAllocatedMemory) is handed over to the + * embedder upon externalization and taken over upon internalization (creating + * an internalized buffer from an existing buffer). + * + * Note that it is unsafe to call back into V8 from any of the allocator + * functions. + */ + class V8_EXPORT Allocator { + public: + virtual ~Allocator() = default; + + /** + * Allocate |length| bytes. Return nullptr if allocation is not successful. + * Memory should be initialized to zeroes. + */ + virtual void* Allocate(size_t length) = 0; + + /** + * Allocate |length| bytes. Return nullptr if allocation is not successful. + * Memory does not have to be initialized. + */ + virtual void* AllocateUninitialized(size_t length) = 0; + + /** + * Free the memory block of size |length|, pointed to by |data|. + * That memory is guaranteed to be previously allocated by |Allocate|. + */ + virtual void Free(void* data, size_t length) = 0; + + /** + * Reallocate the memory block of size |old_length| to a memory block of + * size |new_length| by expanding, contracting, or copying the existing + * memory block. If |new_length| > |old_length|, then the new part of + * the memory must be initialized to zeros. Return nullptr if reallocation + * is not successful. + * + * The caller guarantees that the memory block was previously allocated + * using Allocate or AllocateUninitialized. + * + * The default implementation allocates a new block and copies data. + */ + virtual void* Reallocate(void* data, size_t old_length, size_t new_length); + + /** + * ArrayBuffer allocation mode. kNormal is a malloc/free style allocation, + * while kReservation is for larger allocations with the ability to set + * access permissions. + */ + enum class AllocationMode { kNormal, kReservation }; + + /** + * Convenience allocator. + * + * When the sandbox is enabled, this allocator will allocate its backing + * memory inside the sandbox. Otherwise, it will rely on malloc/free. + * + * Caller takes ownership, i.e. the returned object needs to be freed using + * |delete allocator| once it is no longer in use. + */ + static Allocator* NewDefaultAllocator(); + }; + + /** + * Data length in bytes. + */ + size_t ByteLength() const; + + /** + * Create a new ArrayBuffer. Allocate |byte_length| bytes. + * Allocated memory will be owned by a created ArrayBuffer and + * will be deallocated when it is garbage-collected, + * unless the object is externalized. + */ + static Local New(Isolate* isolate, size_t byte_length); + + /** + * Create a new ArrayBuffer with an existing backing store. + * The created array keeps a reference to the backing store until the array + * is garbage collected. Note that the IsExternal bit does not affect this + * reference from the array to the backing store. + * + * In future IsExternal bit will be removed. Until then the bit is set as + * follows. If the backing store does not own the underlying buffer, then + * the array is created in externalized state. Otherwise, the array is created + * in internalized state. In the latter case the array can be transitioned + * to the externalized state using Externalize(backing_store). + */ + static Local New(Isolate* isolate, + std::shared_ptr backing_store); + + /** + * Returns a new standalone BackingStore that is allocated using the array + * buffer allocator of the isolate. The result can be later passed to + * ArrayBuffer::New. + * + * If the allocator returns nullptr, then the function may cause GCs in the + * given isolate and re-try the allocation. If GCs do not help, then the + * function will crash with an out-of-memory error. + */ + static std::unique_ptr NewBackingStore(Isolate* isolate, + size_t byte_length); + /** + * Returns a new standalone BackingStore that takes over the ownership of + * the given buffer. The destructor of the BackingStore invokes the given + * deleter callback. + * + * The result can be later passed to ArrayBuffer::New. The raw pointer + * to the buffer must not be passed again to any V8 API function. + */ + static std::unique_ptr NewBackingStore( + void* data, size_t byte_length, v8::BackingStore::DeleterCallback deleter, + void* deleter_data); + + /** + * Returns true if this ArrayBuffer may be detached. + */ + bool IsDetachable() const; + + /** + * Returns true if this ArrayBuffer has been detached. + */ + bool WasDetached() const; + + /** + * Detaches this ArrayBuffer and all its views (typed arrays). + * Detaching sets the byte length of the buffer and all typed arrays to zero, + * preventing JavaScript from ever accessing underlying backing store. + * ArrayBuffer should have been externalized and must be detachable. + */ + V8_DEPRECATE_SOON( + "Use the version which takes a key parameter (passing a null handle is " + "ok).") + void Detach(); + + /** + * Detaches this ArrayBuffer and all its views (typed arrays). + * Detaching sets the byte length of the buffer and all typed arrays to zero, + * preventing JavaScript from ever accessing underlying backing store. + * ArrayBuffer should have been externalized and must be detachable. Returns + * Nothing if the key didn't pass the [[ArrayBufferDetachKey]] check, + * Just(true) otherwise. + */ + V8_WARN_UNUSED_RESULT Maybe Detach(v8::Local key); + + /** + * Sets the ArrayBufferDetachKey. + */ + void SetDetachKey(v8::Local key); + + /** + * Get a shared pointer to the backing store of this array buffer. This + * pointer coordinates the lifetime management of the internal storage + * with any live ArrayBuffers on the heap, even across isolates. The embedder + * should not attempt to manage lifetime of the storage through other means. + * + * The returned shared pointer will not be empty, even if the ArrayBuffer has + * been detached. Use |WasDetached| to tell if it has been detached instead. + */ + std::shared_ptr GetBackingStore(); + + /** + * More efficient shortcut for GetBackingStore()->Data(). The returned pointer + * is valid as long as the ArrayBuffer is alive. + */ + void* Data() const; + + V8_INLINE static ArrayBuffer* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT; + static const int kEmbedderFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT; + + private: + ArrayBuffer(); + static void CheckCast(Value* obj); +}; + +#ifndef V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT +// The number of required internal fields can be defined by embedder. +#define V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT 2 +#endif + +/** + * A base class for an instance of one of "views" over ArrayBuffer, + * including TypedArrays and DataView (ES6 draft 15.13). + */ +class V8_EXPORT ArrayBufferView : public Object { + public: + /** + * Returns underlying ArrayBuffer. + */ + Local Buffer(); + /** + * Byte offset in |Buffer|. + */ + size_t ByteOffset(); + /** + * Size of a view in bytes. + */ + size_t ByteLength(); + + /** + * Copy the contents of the ArrayBufferView's buffer to an embedder defined + * memory without additional overhead that calling ArrayBufferView::Buffer + * might incur. + * + * Will write at most min(|byte_length|, ByteLength) bytes starting at + * ByteOffset of the underlying buffer to the memory starting at |dest|. + * Returns the number of bytes actually written. + */ + size_t CopyContents(void* dest, size_t byte_length); + + /** + * Returns true if ArrayBufferView's backing ArrayBuffer has already been + * allocated. + */ + bool HasBuffer() const; + + V8_INLINE static ArrayBufferView* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + static const int kInternalFieldCount = + V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT; + static const int kEmbedderFieldCount = + V8_ARRAY_BUFFER_VIEW_INTERNAL_FIELD_COUNT; + + private: + ArrayBufferView(); + static void CheckCast(Value* obj); +}; + +/** + * An instance of DataView constructor (ES6 draft 15.13.7). + */ +class V8_EXPORT DataView : public ArrayBufferView { + public: + static Local New(Local array_buffer, + size_t byte_offset, size_t length); + static Local New(Local shared_array_buffer, + size_t byte_offset, size_t length); + V8_INLINE static DataView* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + DataView(); + static void CheckCast(Value* obj); +}; + +/** + * An instance of the built-in SharedArrayBuffer constructor. + */ +class V8_EXPORT SharedArrayBuffer : public Object { + public: + /** + * Data length in bytes. + */ + size_t ByteLength() const; + + /** + * Create a new SharedArrayBuffer. Allocate |byte_length| bytes. + * Allocated memory will be owned by a created SharedArrayBuffer and + * will be deallocated when it is garbage-collected, + * unless the object is externalized. + */ + static Local New(Isolate* isolate, size_t byte_length); + + /** + * Create a new SharedArrayBuffer with an existing backing store. + * The created array keeps a reference to the backing store until the array + * is garbage collected. Note that the IsExternal bit does not affect this + * reference from the array to the backing store. + * + * In future IsExternal bit will be removed. Until then the bit is set as + * follows. If the backing store does not own the underlying buffer, then + * the array is created in externalized state. Otherwise, the array is created + * in internalized state. In the latter case the array can be transitioned + * to the externalized state using Externalize(backing_store). + */ + static Local New( + Isolate* isolate, std::shared_ptr backing_store); + + /** + * Returns a new standalone BackingStore that is allocated using the array + * buffer allocator of the isolate. The result can be later passed to + * SharedArrayBuffer::New. + * + * If the allocator returns nullptr, then the function may cause GCs in the + * given isolate and re-try the allocation. If GCs do not help, then the + * function will crash with an out-of-memory error. + */ + static std::unique_ptr NewBackingStore(Isolate* isolate, + size_t byte_length); + /** + * Returns a new standalone BackingStore that takes over the ownership of + * the given buffer. The destructor of the BackingStore invokes the given + * deleter callback. + * + * The result can be later passed to SharedArrayBuffer::New. The raw pointer + * to the buffer must not be passed again to any V8 functions. + */ + static std::unique_ptr NewBackingStore( + void* data, size_t byte_length, v8::BackingStore::DeleterCallback deleter, + void* deleter_data); + + /** + * Get a shared pointer to the backing store of this array buffer. This + * pointer coordinates the lifetime management of the internal storage + * with any live ArrayBuffers on the heap, even across isolates. The embedder + * should not attempt to manage lifetime of the storage through other means. + */ + std::shared_ptr GetBackingStore(); + + /** + * More efficient shortcut for GetBackingStore()->Data(). The returned pointer + * is valid as long as the ArrayBuffer is alive. + */ + void* Data() const; + + V8_INLINE static SharedArrayBuffer* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + static const int kInternalFieldCount = V8_ARRAY_BUFFER_INTERNAL_FIELD_COUNT; + + private: + SharedArrayBuffer(); + static void CheckCast(Value* obj); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_ARRAY_BUFFER_H_ diff --git a/deps/include/v8-callbacks.h b/deps/include/v8-callbacks.h new file mode 100755 index 0000000..0ffdfb6 --- /dev/null +++ b/deps/include/v8-callbacks.h @@ -0,0 +1,412 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_ISOLATE_CALLBACKS_H_ +#define INCLUDE_V8_ISOLATE_CALLBACKS_H_ + +#include + +#include + +#include "cppgc/common.h" +#include "v8-data.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-promise.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +#if defined(V8_OS_WIN) +struct _EXCEPTION_POINTERS; +#endif + +namespace v8 { + +template +class FunctionCallbackInfo; +class Isolate; +class Message; +class Module; +class Object; +class Promise; +class ScriptOrModule; +class String; +class UnboundScript; +class Value; + +/** + * A JIT code event is issued each time code is added, moved or removed. + * + * \note removal events are not currently issued. + */ +struct JitCodeEvent { + enum EventType { + CODE_ADDED, + CODE_MOVED, + CODE_REMOVED, + CODE_ADD_LINE_POS_INFO, + CODE_START_LINE_INFO_RECORDING, + CODE_END_LINE_INFO_RECORDING + }; + // Definition of the code position type. The "POSITION" type means the place + // in the source code which are of interest when making stack traces to + // pin-point the source location of a stack frame as close as possible. + // The "STATEMENT_POSITION" means the place at the beginning of each + // statement, and is used to indicate possible break locations. + enum PositionType { POSITION, STATEMENT_POSITION }; + + // There are three different kinds of CodeType, one for JIT code generated + // by the optimizing compiler, one for byte code generated for the + // interpreter, and one for code generated from Wasm. For JIT_CODE and + // WASM_CODE, |code_start| points to the beginning of jitted assembly code, + // while for BYTE_CODE events, |code_start| points to the first bytecode of + // the interpreted function. + enum CodeType { BYTE_CODE, JIT_CODE, WASM_CODE }; + + // Type of event. + EventType type; + CodeType code_type; + // Start of the instructions. + void* code_start; + // Size of the instructions. + size_t code_len; + // Script info for CODE_ADDED event. + Local script; + // User-defined data for *_LINE_INFO_* event. It's used to hold the source + // code line information which is returned from the + // CODE_START_LINE_INFO_RECORDING event. And it's passed to subsequent + // CODE_ADD_LINE_POS_INFO and CODE_END_LINE_INFO_RECORDING events. + void* user_data; + + struct name_t { + // Name of the object associated with the code, note that the string is not + // zero-terminated. + const char* str; + // Number of chars in str. + size_t len; + }; + + struct line_info_t { + // PC offset + size_t offset; + // Code position + size_t pos; + // The position type. + PositionType position_type; + }; + + struct wasm_source_info_t { + // Source file name. + const char* filename; + // Length of filename. + size_t filename_size; + // Line number table, which maps offsets of JITted code to line numbers of + // source file. + const line_info_t* line_number_table; + // Number of entries in the line number table. + size_t line_number_table_size; + }; + + wasm_source_info_t* wasm_source_info = nullptr; + + union { + // Only valid for CODE_ADDED. + struct name_t name; + + // Only valid for CODE_ADD_LINE_POS_INFO + struct line_info_t line_info; + + // New location of instructions. Only valid for CODE_MOVED. + void* new_code_start; + }; + + Isolate* isolate; +}; + +/** + * Option flags passed to the SetJitCodeEventHandler function. + */ +enum JitCodeEventOptions { + kJitCodeEventDefault = 0, + // Generate callbacks for already existent code. + kJitCodeEventEnumExisting = 1 +}; + +/** + * Callback function passed to SetJitCodeEventHandler. + * + * \param event code add, move or removal event. + */ +using JitCodeEventHandler = void (*)(const JitCodeEvent* event); + +// --- Garbage Collection Callbacks --- + +/** + * Applications can register callback functions which will be called before and + * after certain garbage collection operations. Allocations are not allowed in + * the callback functions, you therefore cannot manipulate objects (set or + * delete properties for example) since it is possible such operations will + * result in the allocation of objects. + */ +enum GCType { + kGCTypeScavenge = 1 << 0, + kGCTypeMinorMarkCompact = 1 << 1, + kGCTypeMarkSweepCompact = 1 << 2, + kGCTypeIncrementalMarking = 1 << 3, + kGCTypeProcessWeakCallbacks = 1 << 4, + kGCTypeAll = kGCTypeScavenge | kGCTypeMinorMarkCompact | + kGCTypeMarkSweepCompact | kGCTypeIncrementalMarking | + kGCTypeProcessWeakCallbacks +}; + +/** + * GCCallbackFlags is used to notify additional information about the GC + * callback. + * - kGCCallbackFlagConstructRetainedObjectInfos: The GC callback is for + * constructing retained object infos. + * - kGCCallbackFlagForced: The GC callback is for a forced GC for testing. + * - kGCCallbackFlagSynchronousPhantomCallbackProcessing: The GC callback + * is called synchronously without getting posted to an idle task. + * - kGCCallbackFlagCollectAllAvailableGarbage: The GC callback is called + * in a phase where V8 is trying to collect all available garbage + * (e.g., handling a low memory notification). + * - kGCCallbackScheduleIdleGarbageCollection: The GC callback is called to + * trigger an idle garbage collection. + */ +enum GCCallbackFlags { + kNoGCCallbackFlags = 0, + kGCCallbackFlagConstructRetainedObjectInfos = 1 << 1, + kGCCallbackFlagForced = 1 << 2, + kGCCallbackFlagSynchronousPhantomCallbackProcessing = 1 << 3, + kGCCallbackFlagCollectAllAvailableGarbage = 1 << 4, + kGCCallbackFlagCollectAllExternalMemory = 1 << 5, + kGCCallbackScheduleIdleGarbageCollection = 1 << 6, +}; + +using GCCallback = void (*)(GCType type, GCCallbackFlags flags); + +using InterruptCallback = void (*)(Isolate* isolate, void* data); + +/** + * This callback is invoked when the heap size is close to the heap limit and + * V8 is likely to abort with out-of-memory error. + * The callback can extend the heap limit by returning a value that is greater + * than the current_heap_limit. The initial heap limit is the limit that was + * set after heap setup. + */ +using NearHeapLimitCallback = size_t (*)(void* data, size_t current_heap_limit, + size_t initial_heap_limit); + +/** + * Callback function passed to SetUnhandledExceptionCallback. + */ +#if defined(V8_OS_WIN) +using UnhandledExceptionCallback = + int (*)(_EXCEPTION_POINTERS* exception_pointers); +#endif + +// --- Counters Callbacks --- + +using CounterLookupCallback = int* (*)(const char* name); + +using CreateHistogramCallback = void* (*)(const char* name, int min, int max, + size_t buckets); + +using AddHistogramSampleCallback = void (*)(void* histogram, int sample); + +// --- Exceptions --- + +using FatalErrorCallback = void (*)(const char* location, const char* message); + +struct OOMDetails { + bool is_heap_oom = false; + const char* detail = nullptr; +}; + +using OOMErrorCallback = void (*)(const char* location, + const OOMDetails& details); + +using MessageCallback = void (*)(Local message, Local data); + +// --- Tracing --- + +enum LogEventStatus : int { kStart = 0, kEnd = 1, kStamp = 2 }; +using LogEventCallback = void (*)(const char* name, + int /* LogEventStatus */ status); + +// --- Crashkeys Callback --- +enum class CrashKeyId { + kIsolateAddress, + kReadonlySpaceFirstPageAddress, + kMapSpaceFirstPageAddress V8_ENUM_DEPRECATE_SOON("Map space got removed"), + kOldSpaceFirstPageAddress, + kCodeRangeBaseAddress, + kCodeSpaceFirstPageAddress, + kDumpType, + kSnapshotChecksumCalculated, + kSnapshotChecksumExpected, +}; + +using AddCrashKeyCallback = void (*)(CrashKeyId id, const std::string& value); + +// --- Enter/Leave Script Callback --- +using BeforeCallEnteredCallback = void (*)(Isolate*); +using CallCompletedCallback = void (*)(Isolate*); + +// --- AllowCodeGenerationFromStrings callbacks --- + +/** + * Callback to check if code generation from strings is allowed. See + * Context::AllowCodeGenerationFromStrings. + */ +using AllowCodeGenerationFromStringsCallback = bool (*)(Local context, + Local source); + +struct ModifyCodeGenerationFromStringsResult { + // If true, proceed with the codegen algorithm. Otherwise, block it. + bool codegen_allowed = false; + // Overwrite the original source with this string, if present. + // Use the original source if empty. + // This field is considered only if codegen_allowed is true. + MaybeLocal modified_source; +}; + +/** + * Access type specification. + */ +enum AccessType { + ACCESS_GET, + ACCESS_SET, + ACCESS_HAS, + ACCESS_DELETE, + ACCESS_KEYS +}; + +// --- Failed Access Check Callback --- + +using FailedAccessCheckCallback = void (*)(Local target, + AccessType type, Local data); + +/** + * Callback to check if codegen is allowed from a source object, and convert + * the source to string if necessary. See: ModifyCodeGenerationFromStrings. + */ +using ModifyCodeGenerationFromStringsCallback = + ModifyCodeGenerationFromStringsResult (*)(Local context, + Local source); +using ModifyCodeGenerationFromStringsCallback2 = + ModifyCodeGenerationFromStringsResult (*)(Local context, + Local source, + bool is_code_like); + +// --- WebAssembly compilation callbacks --- +using ExtensionCallback = bool (*)(const FunctionCallbackInfo&); + +using AllowWasmCodeGenerationCallback = bool (*)(Local context, + Local source); + +// --- Callback for APIs defined on v8-supported objects, but implemented +// by the embedder. Example: WebAssembly.{compile|instantiate}Streaming --- +using ApiImplementationCallback = void (*)(const FunctionCallbackInfo&); + +// --- Callback for WebAssembly.compileStreaming --- +using WasmStreamingCallback = void (*)(const FunctionCallbackInfo&); + +enum class WasmAsyncSuccess { kSuccess, kFail }; + +// --- Callback called when async WebAssembly operations finish --- +using WasmAsyncResolvePromiseCallback = void (*)( + Isolate* isolate, Local context, Local resolver, + Local result, WasmAsyncSuccess success); + +// --- Callback for loading source map file for Wasm profiling support +using WasmLoadSourceMapCallback = Local (*)(Isolate* isolate, + const char* name); + +// --- Callback for checking if WebAssembly Simd is enabled --- +using WasmSimdEnabledCallback = bool (*)(Local context); + +// --- Callback for checking if WebAssembly exceptions are enabled --- +using WasmExceptionsEnabledCallback = bool (*)(Local context); + +// --- Callback for checking if the SharedArrayBuffer constructor is enabled --- +using SharedArrayBufferConstructorEnabledCallback = + bool (*)(Local context); + +/** + * HostImportModuleDynamicallyCallback is called when we + * require the embedder to load a module. This is used as part of the dynamic + * import syntax. + * + * The referrer contains metadata about the script/module that calls + * import. + * + * The specifier is the name of the module that should be imported. + * + * The import_assertions are import assertions for this request in the form: + * [key1, value1, key2, value2, ...] where the keys and values are of type + * v8::String. Note, unlike the FixedArray passed to ResolveModuleCallback and + * returned from ModuleRequest::GetImportAssertions(), this array does not + * contain the source Locations of the assertions. + * + * The embedder must compile, instantiate, evaluate the Module, and + * obtain its namespace object. + * + * The Promise returned from this function is forwarded to userland + * JavaScript. The embedder must resolve this promise with the module + * namespace object. In case of an exception, the embedder must reject + * this promise with the exception. If the promise creation itself + * fails (e.g. due to stack overflow), the embedder must propagate + * that exception by returning an empty MaybeLocal. + */ +using HostImportModuleDynamicallyWithImportAssertionsCallback = + MaybeLocal (*)(Local context, + Local referrer, + Local specifier, + Local import_assertions); +using HostImportModuleDynamicallyCallback = MaybeLocal (*)( + Local context, Local host_defined_options, + Local resource_name, Local specifier, + Local import_assertions); + +/** + * HostInitializeImportMetaObjectCallback is called the first time import.meta + * is accessed for a module. Subsequent access will reuse the same value. + * + * The method combines two implementation-defined abstract operations into one: + * HostGetImportMetaProperties and HostFinalizeImportMeta. + * + * The embedder should use v8::Object::CreateDataProperty to add properties on + * the meta object. + */ +using HostInitializeImportMetaObjectCallback = void (*)(Local context, + Local module, + Local meta); + +/** + * HostCreateShadowRealmContextCallback is called each time a ShadowRealm is + * being constructed in the initiator_context. + * + * The method combines Context creation and implementation defined abstract + * operation HostInitializeShadowRealm into one. + * + * The embedder should use v8::Context::New or v8::Context:NewFromSnapshot to + * create a new context. If the creation fails, the embedder must propagate + * that exception by returning an empty MaybeLocal. + */ +using HostCreateShadowRealmContextCallback = + MaybeLocal (*)(Local initiator_context); + +/** + * PrepareStackTraceCallback is called when the stack property of an error is + * first accessed. The return value will be used as the stack value. If this + * callback is registed, the |Error.prepareStackTrace| API will be disabled. + * |sites| is an array of call sites, specified in + * https://v8.dev/docs/stack-trace-api + */ +using PrepareStackTraceCallback = MaybeLocal (*)(Local context, + Local error, + Local sites); + +} // namespace v8 + +#endif // INCLUDE_V8_ISOLATE_CALLBACKS_H_ diff --git a/deps/include/v8-container.h b/deps/include/v8-container.h new file mode 100755 index 0000000..ce06860 --- /dev/null +++ b/deps/include/v8-container.h @@ -0,0 +1,129 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_CONTAINER_H_ +#define INCLUDE_V8_CONTAINER_H_ + +#include +#include + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-object.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; +class Isolate; + +/** + * An instance of the built-in array constructor (ECMA-262, 15.4.2). + */ +class V8_EXPORT Array : public Object { + public: + uint32_t Length() const; + + /** + * Creates a JavaScript array with the given length. If the length + * is negative the returned array will have length 0. + */ + static Local New(Isolate* isolate, int length = 0); + + /** + * Creates a JavaScript array out of a Local array in C++ + * with a known length. + */ + static Local New(Isolate* isolate, Local* elements, + size_t length); + V8_INLINE static Array* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + Array(); + static void CheckCast(Value* obj); +}; + +/** + * An instance of the built-in Map constructor (ECMA-262, 6th Edition, 23.1.1). + */ +class V8_EXPORT Map : public Object { + public: + size_t Size() const; + void Clear(); + V8_WARN_UNUSED_RESULT MaybeLocal Get(Local context, + Local key); + V8_WARN_UNUSED_RESULT MaybeLocal Set(Local context, + Local key, + Local value); + V8_WARN_UNUSED_RESULT Maybe Has(Local context, + Local key); + V8_WARN_UNUSED_RESULT Maybe Delete(Local context, + Local key); + + /** + * Returns an array of length Size() * 2, where index N is the Nth key and + * index N + 1 is the Nth value. + */ + Local AsArray() const; + + /** + * Creates a new empty Map. + */ + static Local New(Isolate* isolate); + + V8_INLINE static Map* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + Map(); + static void CheckCast(Value* obj); +}; + +/** + * An instance of the built-in Set constructor (ECMA-262, 6th Edition, 23.2.1). + */ +class V8_EXPORT Set : public Object { + public: + size_t Size() const; + void Clear(); + V8_WARN_UNUSED_RESULT MaybeLocal Add(Local context, + Local key); + V8_WARN_UNUSED_RESULT Maybe Has(Local context, + Local key); + V8_WARN_UNUSED_RESULT Maybe Delete(Local context, + Local key); + + /** + * Returns an array of the keys in this Set. + */ + Local AsArray() const; + + /** + * Creates a new empty Set. + */ + static Local New(Isolate* isolate); + + V8_INLINE static Set* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + Set(); + static void CheckCast(Value* obj); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_CONTAINER_H_ diff --git a/deps/include/v8-context.h b/deps/include/v8-context.h new file mode 100755 index 0000000..3ce0eb0 --- /dev/null +++ b/deps/include/v8-context.h @@ -0,0 +1,415 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_CONTEXT_H_ +#define INCLUDE_V8_CONTEXT_H_ + +#include + +#include "v8-data.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-snapshot.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Function; +class MicrotaskQueue; +class Object; +class ObjectTemplate; +class Value; +class String; + +/** + * A container for extension names. + */ +class V8_EXPORT ExtensionConfiguration { + public: + ExtensionConfiguration() : name_count_(0), names_(nullptr) {} + ExtensionConfiguration(int name_count, const char* names[]) + : name_count_(name_count), names_(names) {} + + const char** begin() const { return &names_[0]; } + const char** end() const { return &names_[name_count_]; } + + private: + const int name_count_; + const char** names_; +}; + +/** + * A sandboxed execution context with its own set of built-in objects + * and functions. + */ +class V8_EXPORT Context : public Data { + public: + /** + * Returns the global proxy object. + * + * Global proxy object is a thin wrapper whose prototype points to actual + * context's global object with the properties like Object, etc. This is done + * that way for security reasons (for more details see + * https://wiki.mozilla.org/Gecko:SplitWindow). + * + * Please note that changes to global proxy object prototype most probably + * would break VM---v8 expects only global object as a prototype of global + * proxy object. + */ + Local Global(); + + /** + * Detaches the global object from its context before + * the global object can be reused to create a new context. + */ + void DetachGlobal(); + + /** + * Creates a new context and returns a handle to the newly allocated + * context. + * + * \param isolate The isolate in which to create the context. + * + * \param extensions An optional extension configuration containing + * the extensions to be installed in the newly created context. + * + * \param global_template An optional object template from which the + * global object for the newly created context will be created. + * + * \param global_object An optional global object to be reused for + * the newly created context. This global object must have been + * created by a previous call to Context::New with the same global + * template. The state of the global object will be completely reset + * and only object identify will remain. + */ + static Local New( + Isolate* isolate, ExtensionConfiguration* extensions = nullptr, + MaybeLocal global_template = MaybeLocal(), + MaybeLocal global_object = MaybeLocal(), + DeserializeInternalFieldsCallback internal_fields_deserializer = + DeserializeInternalFieldsCallback(), + MicrotaskQueue* microtask_queue = nullptr); + + /** + * Create a new context from a (non-default) context snapshot. There + * is no way to provide a global object template since we do not create + * a new global object from template, but we can reuse a global object. + * + * \param isolate See v8::Context::New. + * + * \param context_snapshot_index The index of the context snapshot to + * deserialize from. Use v8::Context::New for the default snapshot. + * + * \param embedder_fields_deserializer Optional callback to deserialize + * internal fields. It should match the SerializeInternalFieldCallback used + * to serialize. + * + * \param extensions See v8::Context::New. + * + * \param global_object See v8::Context::New. + */ + static MaybeLocal FromSnapshot( + Isolate* isolate, size_t context_snapshot_index, + DeserializeInternalFieldsCallback embedder_fields_deserializer = + DeserializeInternalFieldsCallback(), + ExtensionConfiguration* extensions = nullptr, + MaybeLocal global_object = MaybeLocal(), + MicrotaskQueue* microtask_queue = nullptr); + + /** + * Returns an global object that isn't backed by an actual context. + * + * The global template needs to have access checks with handlers installed. + * If an existing global object is passed in, the global object is detached + * from its context. + * + * Note that this is different from a detached context where all accesses to + * the global proxy will fail. Instead, the access check handlers are invoked. + * + * It is also not possible to detach an object returned by this method. + * Instead, the access check handlers need to return nothing to achieve the + * same effect. + * + * It is possible, however, to create a new context from the global object + * returned by this method. + */ + static MaybeLocal NewRemoteContext( + Isolate* isolate, Local global_template, + MaybeLocal global_object = MaybeLocal()); + + /** + * Sets the security token for the context. To access an object in + * another context, the security tokens must match. + */ + void SetSecurityToken(Local token); + + /** Restores the security token to the default value. */ + void UseDefaultSecurityToken(); + + /** Returns the security token of this context.*/ + Local GetSecurityToken(); + + /** + * Enter this context. After entering a context, all code compiled + * and run is compiled and run in this context. If another context + * is already entered, this old context is saved so it can be + * restored when the new context is exited. + */ + void Enter(); + + /** + * Exit this context. Exiting the current context restores the + * context that was in place when entering the current context. + */ + void Exit(); + + /** Returns the isolate associated with a current context. */ + Isolate* GetIsolate(); + + /** Returns the microtask queue associated with a current context. */ + MicrotaskQueue* GetMicrotaskQueue(); + + /** Sets the microtask queue associated with the current context. */ + void SetMicrotaskQueue(MicrotaskQueue* queue); + + /** + * The field at kDebugIdIndex used to be reserved for the inspector. + * It now serves no purpose. + */ + enum EmbedderDataFields { kDebugIdIndex = 0 }; + + /** + * Return the number of fields allocated for embedder data. + */ + uint32_t GetNumberOfEmbedderDataFields(); + + /** + * Gets the embedder data with the given index, which must have been set by a + * previous call to SetEmbedderData with the same index. + */ + V8_INLINE Local GetEmbedderData(int index); + + /** + * Gets the binding object used by V8 extras. Extra natives get a reference + * to this object and can use it to "export" functionality by adding + * properties. Extra natives can also "import" functionality by accessing + * properties added by the embedder using the V8 API. + */ + Local GetExtrasBindingObject(); + + /** + * Sets the embedder data with the given index, growing the data as + * needed. Note that index 0 currently has a special meaning for Chrome's + * debugger. + */ + void SetEmbedderData(int index, Local value); + + /** + * Gets a 2-byte-aligned native pointer from the embedder data with the given + * index, which must have been set by a previous call to + * SetAlignedPointerInEmbedderData with the same index. Note that index 0 + * currently has a special meaning for Chrome's debugger. + */ + V8_INLINE void* GetAlignedPointerFromEmbedderData(int index); + + /** + * Sets a 2-byte-aligned native pointer in the embedder data with the given + * index, growing the data as needed. Note that index 0 currently has a + * special meaning for Chrome's debugger. + */ + void SetAlignedPointerInEmbedderData(int index, void* value); + + /** + * Control whether code generation from strings is allowed. Calling + * this method with false will disable 'eval' and the 'Function' + * constructor for code running in this context. If 'eval' or the + * 'Function' constructor are used an exception will be thrown. + * + * If code generation from strings is not allowed the + * V8::AllowCodeGenerationFromStrings callback will be invoked if + * set before blocking the call to 'eval' or the 'Function' + * constructor. If that callback returns true, the call will be + * allowed, otherwise an exception will be thrown. If no callback is + * set an exception will be thrown. + */ + void AllowCodeGenerationFromStrings(bool allow); + + /** + * Returns true if code generation from strings is allowed for the context. + * For more details see AllowCodeGenerationFromStrings(bool) documentation. + */ + bool IsCodeGenerationFromStringsAllowed() const; + + /** + * Sets the error description for the exception that is thrown when + * code generation from strings is not allowed and 'eval' or the 'Function' + * constructor are called. + */ + void SetErrorMessageForCodeGenerationFromStrings(Local message); + + /** + * Sets the error description for the exception that is thrown when + * wasm code generation is not allowed. + */ + void SetErrorMessageForWasmCodeGeneration(Local message); + + /** + * Return data that was previously attached to the context snapshot via + * SnapshotCreator, and removes the reference to it. + * Repeated call with the same index returns an empty MaybeLocal. + */ + template + V8_INLINE MaybeLocal GetDataFromSnapshotOnce(size_t index); + + /** + * If callback is set, abort any attempt to execute JavaScript in this + * context, call the specified callback, and throw an exception. + * To unset abort, pass nullptr as callback. + */ + using AbortScriptExecutionCallback = void (*)(Isolate* isolate, + Local context); + void SetAbortScriptExecution(AbortScriptExecutionCallback callback); + + /** + * Returns the value that was set or restored by + * SetContinuationPreservedEmbedderData(), if any. + */ + Local GetContinuationPreservedEmbedderData() const; + + /** + * Sets a value that will be stored on continuations and reset while the + * continuation runs. + */ + void SetContinuationPreservedEmbedderData(Local context); + + /** + * Set or clear hooks to be invoked for promise lifecycle operations. + * To clear a hook, set it to an empty v8::Function. Each function will + * receive the observed promise as the first argument. If a chaining + * operation is used on a promise, the init will additionally receive + * the parent promise as the second argument. + */ + void SetPromiseHooks(Local init_hook, Local before_hook, + Local after_hook, + Local resolve_hook); + + bool HasTemplateLiteralObject(Local object); + /** + * Stack-allocated class which sets the execution context for all + * operations executed within a local scope. + */ + class V8_NODISCARD Scope { + public: + explicit V8_INLINE Scope(Local context) : context_(context) { + context_->Enter(); + } + V8_INLINE ~Scope() { context_->Exit(); } + + private: + Local context_; + }; + + /** + * Stack-allocated class to support the backup incumbent settings object + * stack. + * https://html.spec.whatwg.org/multipage/webappapis.html#backup-incumbent-settings-object-stack + */ + class V8_EXPORT V8_NODISCARD BackupIncumbentScope final { + public: + /** + * |backup_incumbent_context| is pushed onto the backup incumbent settings + * object stack. + */ + explicit BackupIncumbentScope(Local backup_incumbent_context); + ~BackupIncumbentScope(); + + private: + friend class internal::Isolate; + + uintptr_t JSStackComparableAddressPrivate() const { + return js_stack_comparable_address_; + } + + Local backup_incumbent_context_; + uintptr_t js_stack_comparable_address_ = 0; + const BackupIncumbentScope* prev_ = nullptr; + }; + + V8_INLINE static Context* Cast(Data* data); + + private: + friend class Value; + friend class Script; + friend class Object; + friend class Function; + + static void CheckCast(Data* obj); + + internal::Address* GetDataFromSnapshotOnce(size_t index); + Local SlowGetEmbedderData(int index); + void* SlowGetAlignedPointerFromEmbedderData(int index); +}; + +// --- Implementation --- + +Local Context::GetEmbedderData(int index) { +#ifndef V8_ENABLE_CHECKS + using A = internal::Address; + using I = internal::Internals; + A ctx = *reinterpret_cast(this); + A embedder_data = + I::ReadTaggedPointerField(ctx, I::kNativeContextEmbedderDataOffset); + int value_offset = + I::kEmbedderDataArrayHeaderSize + (I::kEmbedderDataSlotSize * index); + A value = I::ReadRawField(embedder_data, value_offset); +#ifdef V8_COMPRESS_POINTERS + // We read the full pointer value and then decompress it in order to avoid + // dealing with potential endiannes issues. + value = + I::DecompressTaggedAnyField(embedder_data, static_cast(value)); +#endif + internal::Isolate* isolate = internal::IsolateFromNeverReadOnlySpaceObject( + *reinterpret_cast(this)); + A* result = HandleScope::CreateHandle(isolate, value); + return Local(reinterpret_cast(result)); +#else + return SlowGetEmbedderData(index); +#endif +} + +void* Context::GetAlignedPointerFromEmbedderData(int index) { +#if !defined(V8_ENABLE_CHECKS) + using A = internal::Address; + using I = internal::Internals; + A ctx = *reinterpret_cast(this); + A embedder_data = + I::ReadTaggedPointerField(ctx, I::kNativeContextEmbedderDataOffset); + int value_offset = I::kEmbedderDataArrayHeaderSize + + (I::kEmbedderDataSlotSize * index) + + I::kEmbedderDataSlotExternalPointerOffset; + Isolate* isolate = I::GetIsolateForSandbox(ctx); + return reinterpret_cast( + I::ReadExternalPointerField( + isolate, embedder_data, value_offset)); +#else + return SlowGetAlignedPointerFromEmbedderData(index); +#endif +} + +template +MaybeLocal Context::GetDataFromSnapshotOnce(size_t index) { + T* data = reinterpret_cast(GetDataFromSnapshotOnce(index)); + if (data) internal::PerformCastCheck(data); + return Local(data); +} + +Context* Context::Cast(v8::Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); +} + +} // namespace v8 + +#endif // INCLUDE_V8_CONTEXT_H_ diff --git a/deps/include/v8-cppgc.h b/deps/include/v8-cppgc.h new file mode 100755 index 0000000..139af8f --- /dev/null +++ b/deps/include/v8-cppgc.h @@ -0,0 +1,231 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_CPPGC_H_ +#define INCLUDE_V8_CPPGC_H_ + +#include +#include +#include + +#include "cppgc/common.h" +#include "cppgc/custom-space.h" +#include "cppgc/heap-statistics.h" +#include "cppgc/visitor.h" +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8-platform.h" // NOLINT(build/include_directory) +#include "v8-traced-handle.h" // NOLINT(build/include_directory) + +namespace cppgc { +class AllocationHandle; +class HeapHandle; +} // namespace cppgc + +namespace v8 { + +class Object; + +namespace internal { +class CppHeap; +} // namespace internal + +class CustomSpaceStatisticsReceiver; + +/** + * Describes how V8 wrapper objects maintain references to garbage-collected C++ + * objects. + */ +struct WrapperDescriptor final { + /** + * The index used on `v8::Ojbect::SetAlignedPointerFromInternalField()` and + * related APIs to add additional data to an object which is used to identify + * JS->C++ references. + */ + using InternalFieldIndex = int; + + /** + * Unknown embedder id. The value is reserved for internal usages and must not + * be used with `CppHeap`. + */ + static constexpr uint16_t kUnknownEmbedderId = UINT16_MAX; + + constexpr WrapperDescriptor(InternalFieldIndex wrappable_type_index, + InternalFieldIndex wrappable_instance_index, + uint16_t embedder_id_for_garbage_collected) + : wrappable_type_index(wrappable_type_index), + wrappable_instance_index(wrappable_instance_index), + embedder_id_for_garbage_collected(embedder_id_for_garbage_collected) {} + + /** + * Index of the wrappable type. + */ + InternalFieldIndex wrappable_type_index; + + /** + * Index of the wrappable instance. + */ + InternalFieldIndex wrappable_instance_index; + + /** + * Embedder id identifying instances of garbage-collected objects. It is + * expected that the first field of the wrappable type is a uint16_t holding + * the id. Only references to instances of wrappables types with an id of + * `embedder_id_for_garbage_collected` will be considered by CppHeap. + */ + uint16_t embedder_id_for_garbage_collected; +}; + +struct V8_EXPORT CppHeapCreateParams { + std::vector> custom_spaces; + WrapperDescriptor wrapper_descriptor; + /** + * Specifies which kind of marking are supported by the heap. The type may be + * further reduced via runtime flags when attaching the heap to an Isolate. + */ + cppgc::Heap::MarkingType marking_support = + cppgc::Heap::MarkingType::kIncrementalAndConcurrent; + /** + * Specifies which kind of sweeping is supported by the heap. The type may be + * further reduced via runtime flags when attaching the heap to an Isolate. + */ + cppgc::Heap::SweepingType sweeping_support = + cppgc::Heap::SweepingType::kIncrementalAndConcurrent; +}; + +/** + * A heap for allocating managed C++ objects. + * + * Similar to v8::Isolate, the heap may only be accessed from one thread at a + * time. The heap may be used from different threads using the + * v8::Locker/v8::Unlocker APIs which is different from generic Oilpan. + */ +class V8_EXPORT CppHeap { + public: + static std::unique_ptr Create(v8::Platform* platform, + const CppHeapCreateParams& params); + + virtual ~CppHeap() = default; + + /** + * \returns the opaque handle for allocating objects using + * `MakeGarbageCollected()`. + */ + cppgc::AllocationHandle& GetAllocationHandle(); + + /** + * \returns the opaque heap handle which may be used to refer to this heap in + * other APIs. Valid as long as the underlying `CppHeap` is alive. + */ + cppgc::HeapHandle& GetHeapHandle(); + + /** + * Terminate clears all roots and performs multiple garbage collections to + * reclaim potentially newly created objects in destructors. + * + * After this call, object allocation is prohibited. + */ + void Terminate(); + + /** + * \param detail_level specifies whether should return detailed + * statistics or only brief summary statistics. + * \returns current CppHeap statistics regarding memory consumption + * and utilization. + */ + cppgc::HeapStatistics CollectStatistics( + cppgc::HeapStatistics::DetailLevel detail_level); + + /** + * Collects statistics for the given spaces and reports them to the receiver. + * + * \param custom_spaces a collection of custom space indicies. + * \param receiver an object that gets the results. + */ + void CollectCustomSpaceStatisticsAtLastGC( + std::vector custom_spaces, + std::unique_ptr receiver); + + /** + * Enables a detached mode that allows testing garbage collection using + * `cppgc::testing` APIs. Once used, the heap cannot be attached to an + * `Isolate` anymore. + */ + void EnableDetachedGarbageCollectionsForTesting(); + + /** + * Performs a stop-the-world garbage collection for testing purposes. + * + * \param stack_state The stack state to assume for the garbage collection. + */ + void CollectGarbageForTesting(cppgc::EmbedderStackState stack_state); + + /** + * Performs a stop-the-world minor garbage collection for testing purposes. + * + * \param stack_state The stack state to assume for the garbage collection. + */ + void CollectGarbageInYoungGenerationForTesting( + cppgc::EmbedderStackState stack_state); + + private: + CppHeap() = default; + + friend class internal::CppHeap; +}; + +class JSVisitor : public cppgc::Visitor { + public: + explicit JSVisitor(cppgc::Visitor::Key key) : cppgc::Visitor(key) {} + ~JSVisitor() override = default; + + void Trace(const TracedReferenceBase& ref) { + if (ref.IsEmptyThreadSafe()) return; + Visit(ref); + } + + protected: + using cppgc::Visitor::Visit; + + virtual void Visit(const TracedReferenceBase& ref) {} +}; + +/** + * Provided as input to `CppHeap::CollectCustomSpaceStatisticsAtLastGC()`. + * + * Its method is invoked with the results of the statistic collection. + */ +class CustomSpaceStatisticsReceiver { + public: + virtual ~CustomSpaceStatisticsReceiver() = default; + /** + * Reports the size of a space at the last GC. It is called for each space + * that was requested in `CollectCustomSpaceStatisticsAtLastGC()`. + * + * \param space_index The index of the space. + * \param bytes The total size of live objects in the space at the last GC. + * It is zero if there was no GC yet. + */ + virtual void AllocatedBytes(cppgc::CustomSpaceIndex space_index, + size_t bytes) = 0; +}; + +} // namespace v8 + +namespace cppgc { + +template +struct TraceTrait> { + static cppgc::TraceDescriptor GetTraceDescriptor(const void* self) { + return {nullptr, Trace}; + } + + static void Trace(Visitor* visitor, const void* self) { + static_cast(visitor)->Trace( + *static_cast*>(self)); + } +}; + +} // namespace cppgc + +#endif // INCLUDE_V8_CPPGC_H_ diff --git a/deps/include/v8-data.h b/deps/include/v8-data.h new file mode 100755 index 0000000..fc4dea9 --- /dev/null +++ b/deps/include/v8-data.h @@ -0,0 +1,80 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_DATA_H_ +#define INCLUDE_V8_DATA_H_ + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; + +/** + * The superclass of objects that can reside on V8's heap. + */ +class V8_EXPORT Data { + public: + /** + * Returns true if this data is a |v8::Value|. + */ + bool IsValue() const; + + /** + * Returns true if this data is a |v8::Module|. + */ + bool IsModule() const; + + /** + * Returns tru if this data is a |v8::FixedArray| + */ + bool IsFixedArray() const; + + /** + * Returns true if this data is a |v8::Private|. + */ + bool IsPrivate() const; + + /** + * Returns true if this data is a |v8::ObjectTemplate|. + */ + bool IsObjectTemplate() const; + + /** + * Returns true if this data is a |v8::FunctionTemplate|. + */ + bool IsFunctionTemplate() const; + + /** + * Returns true if this data is a |v8::Context|. + */ + bool IsContext() const; + + private: + Data() = delete; +}; + +/** + * A fixed-sized array with elements of type Data. + */ +class V8_EXPORT FixedArray : public Data { + public: + int Length() const; + Local Get(Local context, int i) const; + + V8_INLINE static FixedArray* Cast(Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return reinterpret_cast(data); + } + + private: + static void CheckCast(Data* obj); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_DATA_H_ diff --git a/deps/include/v8-date.h b/deps/include/v8-date.h new file mode 100755 index 0000000..8d82ccc --- /dev/null +++ b/deps/include/v8-date.h @@ -0,0 +1,48 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_DATE_H_ +#define INCLUDE_V8_DATE_H_ + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-object.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; + +/** + * An instance of the built-in Date constructor (ECMA-262, 15.9). + */ +class V8_EXPORT Date : public Object { + public: + static V8_WARN_UNUSED_RESULT MaybeLocal New(Local context, + double time); + + /** + * A specialization of Value::NumberValue that is more efficient + * because we know the structure of this object. + */ + double ValueOf() const; + + /** + * Generates ISO string representation. + */ + v8::Local ToISOString() const; + + V8_INLINE static Date* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + static void CheckCast(Value* obj); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_DATE_H_ diff --git a/deps/include/v8-debug.h b/deps/include/v8-debug.h new file mode 100755 index 0000000..52255f3 --- /dev/null +++ b/deps/include/v8-debug.h @@ -0,0 +1,168 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_DEBUG_H_ +#define INCLUDE_V8_DEBUG_H_ + +#include + +#include "v8-script.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Isolate; +class String; + +/** + * A single JavaScript stack frame. + */ +class V8_EXPORT StackFrame { + public: + /** + * Returns the source location, 0-based, for the associated function call. + */ + Location GetLocation() const; + + /** + * Returns the number, 1-based, of the line for the associate function call. + * This method will return Message::kNoLineNumberInfo if it is unable to + * retrieve the line number, or if kLineNumber was not passed as an option + * when capturing the StackTrace. + */ + int GetLineNumber() const { return GetLocation().GetLineNumber() + 1; } + + /** + * Returns the 1-based column offset on the line for the associated function + * call. + * This method will return Message::kNoColumnInfo if it is unable to retrieve + * the column number, or if kColumnOffset was not passed as an option when + * capturing the StackTrace. + */ + int GetColumn() const { return GetLocation().GetColumnNumber() + 1; } + + /** + * Returns the id of the script for the function for this StackFrame. + * This method will return Message::kNoScriptIdInfo if it is unable to + * retrieve the script id, or if kScriptId was not passed as an option when + * capturing the StackTrace. + */ + int GetScriptId() const; + + /** + * Returns the name of the resource that contains the script for the + * function for this StackFrame. + */ + Local GetScriptName() const; + + /** + * Returns the name of the resource that contains the script for the + * function for this StackFrame or sourceURL value if the script name + * is undefined and its source ends with //# sourceURL=... string or + * deprecated //@ sourceURL=... string. + */ + Local GetScriptNameOrSourceURL() const; + + /** + * Returns the source of the script for the function for this StackFrame. + */ + Local GetScriptSource() const; + + /** + * Returns the source mapping URL (if one is present) of the script for + * the function for this StackFrame. + */ + Local GetScriptSourceMappingURL() const; + + /** + * Returns the name of the function associated with this stack frame. + */ + Local GetFunctionName() const; + + /** + * Returns whether or not the associated function is compiled via a call to + * eval(). + */ + bool IsEval() const; + + /** + * Returns whether or not the associated function is called as a + * constructor via "new". + */ + bool IsConstructor() const; + + /** + * Returns whether or not the associated functions is defined in wasm. + */ + bool IsWasm() const; + + /** + * Returns whether or not the associated function is defined by the user. + */ + bool IsUserJavaScript() const; +}; + +/** + * Representation of a JavaScript stack trace. The information collected is a + * snapshot of the execution stack and the information remains valid after + * execution continues. + */ +class V8_EXPORT StackTrace { + public: + /** + * Flags that determine what information is placed captured for each + * StackFrame when grabbing the current stack trace. + * Note: these options are deprecated and we always collect all available + * information (kDetailed). + */ + enum StackTraceOptions { + kLineNumber = 1, + kColumnOffset = 1 << 1 | kLineNumber, + kScriptName = 1 << 2, + kFunctionName = 1 << 3, + kIsEval = 1 << 4, + kIsConstructor = 1 << 5, + kScriptNameOrSourceURL = 1 << 6, + kScriptId = 1 << 7, + kExposeFramesAcrossSecurityOrigins = 1 << 8, + kOverview = kLineNumber | kColumnOffset | kScriptName | kFunctionName, + kDetailed = kOverview | kIsEval | kIsConstructor | kScriptNameOrSourceURL + }; + + /** + * Returns a StackFrame at a particular index. + */ + Local GetFrame(Isolate* isolate, uint32_t index) const; + + /** + * Returns the number of StackFrames. + */ + int GetFrameCount() const; + + /** + * Grab a snapshot of the current JavaScript execution stack. + * + * \param frame_limit The maximum number of stack frames we want to capture. + * \param options Enumerates the set of things we will capture for each + * StackFrame. + */ + static Local CurrentStackTrace( + Isolate* isolate, int frame_limit, StackTraceOptions options = kDetailed); + + /** + * Returns the first valid script name or source URL starting at the top of + * the JS stack. The returned string is either an empty handle if no script + * name/url was found or a non-zero-length string. + * + * This method is equivalent to calling StackTrace::CurrentStackTrace and + * walking the resulting frames from the beginning until a non-empty script + * name/url is found. The difference is that this method won't allocate + * a stack trace. + */ + static Local CurrentScriptNameOrSourceURL(Isolate* isolate); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_DEBUG_H_ diff --git a/deps/include/v8-embedder-heap.h b/deps/include/v8-embedder-heap.h new file mode 100755 index 0000000..f994cdf --- /dev/null +++ b/deps/include/v8-embedder-heap.h @@ -0,0 +1,223 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_EMBEDDER_HEAP_H_ +#define INCLUDE_V8_EMBEDDER_HEAP_H_ + +#include +#include + +#include +#include + +#include "cppgc/common.h" +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-traced-handle.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Data; +class Isolate; +class Value; + +namespace internal { +class LocalEmbedderHeapTracer; +} // namespace internal + +/** + * Handler for embedder roots on non-unified heap garbage collections. + */ +class V8_EXPORT EmbedderRootsHandler { + public: + virtual ~EmbedderRootsHandler() = default; + + /** + * Returns true if the |TracedReference| handle should be considered as root + * for the currently running non-tracing garbage collection and false + * otherwise. The default implementation will keep all |TracedReference| + * references as roots. + * + * If this returns false, then V8 may decide that the object referred to by + * such a handle is reclaimed. In that case, V8 calls |ResetRoot()| for the + * |TracedReference|. + * + * Note that the `handle` is different from the handle that the embedder holds + * for retaining the object. The embedder may use |WrapperClassId()| to + * distinguish cases where it wants handles to be treated as roots from not + * being treated as roots. + */ + virtual bool IsRoot(const v8::TracedReference& handle) = 0; + + /** + * Used in combination with |IsRoot|. Called by V8 when an + * object that is backed by a handle is reclaimed by a non-tracing garbage + * collection. It is up to the embedder to reset the original handle. + * + * Note that the |handle| is different from the handle that the embedder holds + * for retaining the object. It is up to the embedder to find the original + * handle via the object or class id. + */ + virtual void ResetRoot(const v8::TracedReference& handle) = 0; +}; + +/** + * Interface for tracing through the embedder heap. During a V8 garbage + * collection, V8 collects hidden fields of all potential wrappers, and at the + * end of its marking phase iterates the collection and asks the embedder to + * trace through its heap and use reporter to report each JavaScript object + * reachable from any of the given wrappers. + */ +class V8_EXPORT +// GCC doesn't like combining __attribute__(()) with [[deprecated]]. +#ifdef __clang__ +V8_DEPRECATED("Use CppHeap when working with v8::TracedReference.") +#endif // __clang__ + EmbedderHeapTracer { + public: + using EmbedderStackState = cppgc::EmbedderStackState; + + enum TraceFlags : uint64_t { + kNoFlags = 0, + kReduceMemory = 1 << 0, + kForced = 1 << 2, + }; + + /** + * Interface for iterating through |TracedReference| handles. + */ + class V8_EXPORT TracedGlobalHandleVisitor { + public: + virtual ~TracedGlobalHandleVisitor() = default; + virtual void VisitTracedReference(const TracedReference& handle) {} + }; + + /** + * Summary of a garbage collection cycle. See |TraceEpilogue| on how the + * summary is reported. + */ + struct TraceSummary { + /** + * Time spent managing the retained memory in milliseconds. This can e.g. + * include the time tracing through objects in the embedder. + */ + double time = 0.0; + + /** + * Memory retained by the embedder through the |EmbedderHeapTracer| + * mechanism in bytes. + */ + size_t allocated_size = 0; + }; + + virtual ~EmbedderHeapTracer() = default; + + /** + * Iterates all |TracedReference| handles created for the |v8::Isolate| the + * tracer is attached to. + */ + void IterateTracedGlobalHandles(TracedGlobalHandleVisitor* visitor); + + /** + * Called by the embedder to set the start of the stack which is e.g. used by + * V8 to determine whether handles are used from stack or heap. + */ + void SetStackStart(void* stack_start); + + /** + * Called by v8 to register internal fields of found wrappers. + * + * The embedder is expected to store them somewhere and trace reachable + * wrappers from them when called through |AdvanceTracing|. + */ + virtual void RegisterV8References( + const std::vector>& embedder_fields) = 0; + + void RegisterEmbedderReference(const BasicTracedReference& ref); + + /** + * Called at the beginning of a GC cycle. + */ + virtual void TracePrologue(TraceFlags flags) {} + + /** + * Called to advance tracing in the embedder. + * + * The embedder is expected to trace its heap starting from wrappers reported + * by RegisterV8References method, and report back all reachable wrappers. + * Furthermore, the embedder is expected to stop tracing by the given + * deadline. A deadline of infinity means that tracing should be finished. + * + * Returns |true| if tracing is done, and false otherwise. + */ + virtual bool AdvanceTracing(double deadline_in_ms) = 0; + + /* + * Returns true if there no more tracing work to be done (see AdvanceTracing) + * and false otherwise. + */ + virtual bool IsTracingDone() = 0; + + /** + * Called at the end of a GC cycle. + * + * Note that allocation is *not* allowed within |TraceEpilogue|. Can be + * overriden to fill a |TraceSummary| that is used by V8 to schedule future + * garbage collections. + */ + virtual void TraceEpilogue(TraceSummary* trace_summary) {} + + /** + * Called upon entering the final marking pause. No more incremental marking + * steps will follow this call. + */ + virtual void EnterFinalPause(EmbedderStackState stack_state) = 0; + + /* + * Called by the embedder to request immediate finalization of the currently + * running tracing phase that has been started with TracePrologue and not + * yet finished with TraceEpilogue. + * + * Will be a noop when currently not in tracing. + * + * This is an experimental feature. + */ + void FinalizeTracing(); + + /** + * See documentation on EmbedderRootsHandler. + */ + virtual bool IsRootForNonTracingGC( + const v8::TracedReference& handle); + + /** + * See documentation on EmbedderRootsHandler. + */ + virtual void ResetHandleInNonTracingGC( + const v8::TracedReference& handle); + + /* + * Called by the embedder to signal newly allocated or freed memory. Not bound + * to tracing phases. Embedders should trade off when increments are reported + * as V8 may consult global heuristics on whether to trigger garbage + * collection on this change. + */ + void IncreaseAllocatedSize(size_t bytes); + void DecreaseAllocatedSize(size_t bytes); + + /* + * Returns the v8::Isolate this tracer is attached too and |nullptr| if it + * is not attached to any v8::Isolate. + */ + v8::Isolate* isolate() const { return v8_isolate_; } + + protected: + v8::Isolate* v8_isolate_ = nullptr; + + friend class internal::LocalEmbedderHeapTracer; +}; + +} // namespace v8 + +#endif // INCLUDE_V8_EMBEDDER_HEAP_H_ diff --git a/deps/include/v8-embedder-state-scope.h b/deps/include/v8-embedder-state-scope.h new file mode 100755 index 0000000..d8a3b08 --- /dev/null +++ b/deps/include/v8-embedder-state-scope.h @@ -0,0 +1,51 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_EMBEDDER_STATE_SCOPE_H_ +#define INCLUDE_V8_EMBEDDER_STATE_SCOPE_H_ + +#include + +#include "v8-context.h" // NOLINT(build/include_directory) +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) + +namespace v8 { + +namespace internal { +class EmbedderState; +} // namespace internal + +// A StateTag represents a possible state of the embedder. +enum class EmbedderStateTag : uint8_t { + // reserved + EMPTY = 0, + OTHER = 1, + // embedder can define any state after +}; + +// A stack-allocated class that manages an embedder state on the isolate. +// After an EmbedderState scope has been created, a new embedder state will be +// pushed on the isolate stack. +class V8_EXPORT EmbedderStateScope { + public: + EmbedderStateScope(Isolate* isolate, Local context, + EmbedderStateTag tag); + + ~EmbedderStateScope(); + + private: + // Declaring operator new and delete as deleted is not spec compliant. + // Therefore declare them private instead to disable dynamic alloc + void* operator new(size_t size); + void* operator new[](size_t size); + void operator delete(void*, size_t); + void operator delete[](void*, size_t); + + std::unique_ptr embedder_state_; +}; + +} // namespace v8 + +#endif // INCLUDE_V8_EMBEDDER_STATE_SCOPE_H_ diff --git a/deps/include/v8-exception.h b/deps/include/v8-exception.h new file mode 100755 index 0000000..bc058e3 --- /dev/null +++ b/deps/include/v8-exception.h @@ -0,0 +1,217 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_EXCEPTION_H_ +#define INCLUDE_V8_EXCEPTION_H_ + +#include + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; +class Isolate; +class Message; +class StackTrace; +class String; +class Value; + +namespace internal { +class Isolate; +class ThreadLocalTop; +} // namespace internal + +/** + * Create new error objects by calling the corresponding error object + * constructor with the message. + */ +class V8_EXPORT Exception { + public: + static Local RangeError(Local message); + static Local ReferenceError(Local message); + static Local SyntaxError(Local message); + static Local TypeError(Local message); + static Local WasmCompileError(Local message); + static Local WasmLinkError(Local message); + static Local WasmRuntimeError(Local message); + static Local Error(Local message); + + /** + * Creates an error message for the given exception. + * Will try to reconstruct the original stack trace from the exception value, + * or capture the current stack trace if not available. + */ + static Local CreateMessage(Isolate* isolate, Local exception); + + /** + * Returns the original stack trace that was captured at the creation time + * of a given exception, or an empty handle if not available. + */ + static Local GetStackTrace(Local exception); +}; + +/** + * An external exception handler. + */ +class V8_EXPORT TryCatch { + public: + /** + * Creates a new try/catch block and registers it with v8. Note that + * all TryCatch blocks should be stack allocated because the memory + * location itself is compared against JavaScript try/catch blocks. + */ + explicit TryCatch(Isolate* isolate); + + /** + * Unregisters and deletes this try/catch block. + */ + ~TryCatch(); + + /** + * Returns true if an exception has been caught by this try/catch block. + */ + bool HasCaught() const; + + /** + * For certain types of exceptions, it makes no sense to continue execution. + * + * If CanContinue returns false, the correct action is to perform any C++ + * cleanup needed and then return. If CanContinue returns false and + * HasTerminated returns true, it is possible to call + * CancelTerminateExecution in order to continue calling into the engine. + */ + bool CanContinue() const; + + /** + * Returns true if an exception has been caught due to script execution + * being terminated. + * + * There is no JavaScript representation of an execution termination + * exception. Such exceptions are thrown when the TerminateExecution + * methods are called to terminate a long-running script. + * + * If such an exception has been thrown, HasTerminated will return true, + * indicating that it is possible to call CancelTerminateExecution in order + * to continue calling into the engine. + */ + bool HasTerminated() const; + + /** + * Throws the exception caught by this TryCatch in a way that avoids + * it being caught again by this same TryCatch. As with ThrowException + * it is illegal to execute any JavaScript operations after calling + * ReThrow; the caller must return immediately to where the exception + * is caught. + */ + Local ReThrow(); + + /** + * Returns the exception caught by this try/catch block. If no exception has + * been caught an empty handle is returned. + */ + Local Exception() const; + + /** + * Returns the .stack property of an object. If no .stack + * property is present an empty handle is returned. + */ + V8_WARN_UNUSED_RESULT static MaybeLocal StackTrace( + Local context, Local exception); + + /** + * Returns the .stack property of the thrown object. If no .stack property is + * present or if this try/catch block has not caught an exception, an empty + * handle is returned. + */ + V8_WARN_UNUSED_RESULT MaybeLocal StackTrace( + Local context) const; + + /** + * Returns the message associated with this exception. If there is + * no message associated an empty handle is returned. + */ + Local Message() const; + + /** + * Clears any exceptions that may have been caught by this try/catch block. + * After this method has been called, HasCaught() will return false. Cancels + * the scheduled exception if it is caught and ReThrow() is not called before. + * + * It is not necessary to clear a try/catch block before using it again; if + * another exception is thrown the previously caught exception will just be + * overwritten. However, it is often a good idea since it makes it easier + * to determine which operation threw a given exception. + */ + void Reset(); + + /** + * Set verbosity of the external exception handler. + * + * By default, exceptions that are caught by an external exception + * handler are not reported. Call SetVerbose with true on an + * external exception handler to have exceptions caught by the + * handler reported as if they were not caught. + */ + void SetVerbose(bool value); + + /** + * Returns true if verbosity is enabled. + */ + bool IsVerbose() const; + + /** + * Set whether or not this TryCatch should capture a Message object + * which holds source information about where the exception + * occurred. True by default. + */ + void SetCaptureMessage(bool value); + + TryCatch(const TryCatch&) = delete; + void operator=(const TryCatch&) = delete; + + private: + // Declaring operator new and delete as deleted is not spec compliant. + // Therefore declare them private instead to disable dynamic alloc + void* operator new(size_t size); + void* operator new[](size_t size); + void operator delete(void*, size_t); + void operator delete[](void*, size_t); + + /** + * There are cases when the raw address of C++ TryCatch object cannot be + * used for comparisons with addresses into the JS stack. The cases are: + * 1) ARM, ARM64 and MIPS simulators which have separate JS stack. + * 2) Address sanitizer allocates local C++ object in the heap when + * UseAfterReturn mode is enabled. + * This method returns address that can be used for comparisons with + * addresses into the JS stack. When neither simulator nor ASAN's + * UseAfterReturn is enabled, then the address returned will be the address + * of the C++ try catch handler itself. + */ + internal::Address JSStackComparableAddressPrivate() { + return js_stack_comparable_address_; + } + + void ResetInternal(); + + internal::Isolate* i_isolate_; + TryCatch* next_; + void* exception_; + void* message_obj_; + internal::Address js_stack_comparable_address_; + bool is_verbose_ : 1; + bool can_continue_ : 1; + bool capture_message_ : 1; + bool rethrow_ : 1; + bool has_terminated_ : 1; + + friend class internal::Isolate; + friend class internal::ThreadLocalTop; +}; + +} // namespace v8 + +#endif // INCLUDE_V8_EXCEPTION_H_ diff --git a/deps/include/v8-extension.h b/deps/include/v8-extension.h new file mode 100755 index 0000000..0705e2a --- /dev/null +++ b/deps/include/v8-extension.h @@ -0,0 +1,62 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_EXTENSION_H_ +#define INCLUDE_V8_EXTENSION_H_ + +#include + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-primitive.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class FunctionTemplate; + +// --- Extensions --- + +/** + * Ignore + */ +class V8_EXPORT Extension { + public: + // Note that the strings passed into this constructor must live as long + // as the Extension itself. + Extension(const char* name, const char* source = nullptr, int dep_count = 0, + const char** deps = nullptr, int source_length = -1); + virtual ~Extension() { delete source_; } + virtual Local GetNativeFunctionTemplate( + Isolate* isolate, Local name) { + return Local(); + } + + const char* name() const { return name_; } + size_t source_length() const { return source_length_; } + const String::ExternalOneByteStringResource* source() const { + return source_; + } + int dependency_count() const { return dep_count_; } + const char** dependencies() const { return deps_; } + void set_auto_enable(bool value) { auto_enable_ = value; } + bool auto_enable() { return auto_enable_; } + + // Disallow copying and assigning. + Extension(const Extension&) = delete; + void operator=(const Extension&) = delete; + + private: + const char* name_; + size_t source_length_; // expected to initialize before source_ + String::ExternalOneByteStringResource* source_; + int dep_count_; + const char** deps_; + bool auto_enable_; +}; + +void V8_EXPORT RegisterExtension(std::unique_ptr); + +} // namespace v8 + +#endif // INCLUDE_V8_EXTENSION_H_ diff --git a/deps/include/v8-external.h b/deps/include/v8-external.h new file mode 100755 index 0000000..2e24503 --- /dev/null +++ b/deps/include/v8-external.h @@ -0,0 +1,37 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_EXTERNAL_H_ +#define INCLUDE_V8_EXTERNAL_H_ + +#include "v8-value.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Isolate; + +/** + * A JavaScript value that wraps a C++ void*. This type of value is mainly used + * to associate C++ data structures with JavaScript objects. + */ +class V8_EXPORT External : public Value { + public: + static Local New(Isolate* isolate, void* value); + V8_INLINE static External* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + void* Value() const; + + private: + static void CheckCast(v8::Value* obj); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_EXTERNAL_H_ diff --git a/deps/include/v8-fast-api-calls.h b/deps/include/v8-fast-api-calls.h new file mode 100755 index 0000000..1826f13 --- /dev/null +++ b/deps/include/v8-fast-api-calls.h @@ -0,0 +1,934 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * This file provides additional API on top of the default one for making + * API calls, which come from embedder C++ functions. The functions are being + * called directly from optimized code, doing all the necessary typechecks + * in the compiler itself, instead of on the embedder side. Hence the "fast" + * in the name. Example usage might look like: + * + * \code + * void FastMethod(int param, bool another_param); + * + * v8::FunctionTemplate::New(isolate, SlowCallback, data, + * signature, length, constructor_behavior + * side_effect_type, + * &v8::CFunction::Make(FastMethod)); + * \endcode + * + * By design, fast calls are limited by the following requirements, which + * the embedder should enforce themselves: + * - they should not allocate on the JS heap; + * - they should not trigger JS execution. + * To enforce them, the embedder could use the existing + * v8::Isolate::DisallowJavascriptExecutionScope and a utility similar to + * Blink's NoAllocationScope: + * https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/platform/heap/thread_state_scopes.h;l=16 + * + * Due to these limitations, it's not directly possible to report errors by + * throwing a JS exception or to otherwise do an allocation. There is an + * alternative way of creating fast calls that supports falling back to the + * slow call and then performing the necessary allocation. When one creates + * the fast method by using CFunction::MakeWithFallbackSupport instead of + * CFunction::Make, the fast callback gets as last parameter an output variable, + * through which it can request falling back to the slow call. So one might + * declare their method like: + * + * \code + * void FastMethodWithFallback(int param, FastApiCallbackOptions& options); + * \endcode + * + * If the callback wants to signal an error condition or to perform an + * allocation, it must set options.fallback to true and do an early return from + * the fast method. Then V8 checks the value of options.fallback and if it's + * true, falls back to executing the SlowCallback, which is capable of reporting + * the error (either by throwing a JS exception or logging to the console) or + * doing the allocation. It's the embedder's responsibility to ensure that the + * fast callback is idempotent up to the point where error and fallback + * conditions are checked, because otherwise executing the slow callback might + * produce visible side-effects twice. + * + * An example for custom embedder type support might employ a way to wrap/ + * unwrap various C++ types in JSObject instances, e.g: + * + * \code + * + * // Helper method with a check for field count. + * template + * inline T* GetInternalField(v8::Local wrapper) { + * assert(offset < wrapper->InternalFieldCount()); + * return reinterpret_cast( + * wrapper->GetAlignedPointerFromInternalField(offset)); + * } + * + * class CustomEmbedderType { + * public: + * // Returns the raw C object from a wrapper JS object. + * static CustomEmbedderType* Unwrap(v8::Local wrapper) { + * return GetInternalField(wrapper); + * } + * static void FastMethod(v8::Local receiver_obj, int param) { + * CustomEmbedderType* receiver = static_cast( + * receiver_obj->GetAlignedPointerFromInternalField( + * kV8EmbedderWrapperObjectIndex)); + * + * // Type checks are already done by the optimized code. + * // Then call some performance-critical method like: + * // receiver->Method(param); + * } + * + * static void SlowMethod( + * const v8::FunctionCallbackInfo& info) { + * v8::Local instance = + * v8::Local::Cast(info.Holder()); + * CustomEmbedderType* receiver = Unwrap(instance); + * // TODO: Do type checks and extract {param}. + * receiver->Method(param); + * } + * }; + * + * // TODO(mslekova): Clean-up these constants + * // The constants kV8EmbedderWrapperTypeIndex and + * // kV8EmbedderWrapperObjectIndex describe the offsets for the type info + * // struct and the native object, when expressed as internal field indices + * // within a JSObject. The existance of this helper function assumes that + * // all embedder objects have their JSObject-side type info at the same + * // offset, but this is not a limitation of the API itself. For a detailed + * // use case, see the third example. + * static constexpr int kV8EmbedderWrapperTypeIndex = 0; + * static constexpr int kV8EmbedderWrapperObjectIndex = 1; + * + * // The following setup function can be templatized based on + * // the {embedder_object} argument. + * void SetupCustomEmbedderObject(v8::Isolate* isolate, + * v8::Local context, + * CustomEmbedderType* embedder_object) { + * isolate->set_embedder_wrapper_type_index( + * kV8EmbedderWrapperTypeIndex); + * isolate->set_embedder_wrapper_object_index( + * kV8EmbedderWrapperObjectIndex); + * + * v8::CFunction c_func = + * MakeV8CFunction(CustomEmbedderType::FastMethod); + * + * Local method_template = + * v8::FunctionTemplate::New( + * isolate, CustomEmbedderType::SlowMethod, v8::Local(), + * v8::Local(), 1, v8::ConstructorBehavior::kAllow, + * v8::SideEffectType::kHasSideEffect, &c_func); + * + * v8::Local object_template = + * v8::ObjectTemplate::New(isolate); + * object_template->SetInternalFieldCount( + * kV8EmbedderWrapperObjectIndex + 1); + * object_template->Set(isolate, "method", method_template); + * + * // Instantiate the wrapper JS object. + * v8::Local object = + * object_template->NewInstance(context).ToLocalChecked(); + * object->SetAlignedPointerInInternalField( + * kV8EmbedderWrapperObjectIndex, + * reinterpret_cast(embedder_object)); + * + * // TODO: Expose {object} where it's necessary. + * } + * \endcode + * + * For instance if {object} is exposed via a global "obj" variable, + * one could write in JS: + * function hot_func() { + * obj.method(42); + * } + * and once {hot_func} gets optimized, CustomEmbedderType::FastMethod + * will be called instead of the slow version, with the following arguments: + * receiver := the {embedder_object} from above + * param := 42 + * + * Currently supported return types: + * - void + * - bool + * - int32_t + * - uint32_t + * - float32_t + * - float64_t + * Currently supported argument types: + * - pointer to an embedder type + * - JavaScript array of primitive types + * - bool + * - int32_t + * - uint32_t + * - int64_t + * - uint64_t + * - float32_t + * - float64_t + * + * The 64-bit integer types currently have the IDL (unsigned) long long + * semantics: https://heycam.github.io/webidl/#abstract-opdef-converttoint + * In the future we'll extend the API to also provide conversions from/to + * BigInt to preserve full precision. + * The floating point types currently have the IDL (unrestricted) semantics, + * which is the only one used by WebGL. We plan to add support also for + * restricted floats/doubles, similarly to the BigInt conversion policies. + * We also differ from the specific NaN bit pattern that WebIDL prescribes + * (https://heycam.github.io/webidl/#es-unrestricted-float) in that Blink + * passes NaN values as-is, i.e. doesn't normalize them. + * + * To be supported types: + * - TypedArrays and ArrayBuffers + * - arrays of embedder types + * + * + * The API offers a limited support for function overloads: + * + * \code + * void FastMethod_2Args(int param, bool another_param); + * void FastMethod_3Args(int param, bool another_param, int third_param); + * + * v8::CFunction fast_method_2args_c_func = + * MakeV8CFunction(FastMethod_2Args); + * v8::CFunction fast_method_3args_c_func = + * MakeV8CFunction(FastMethod_3Args); + * const v8::CFunction fast_method_overloads[] = {fast_method_2args_c_func, + * fast_method_3args_c_func}; + * Local method_template = + * v8::FunctionTemplate::NewWithCFunctionOverloads( + * isolate, SlowCallback, data, signature, length, + * constructor_behavior, side_effect_type, + * {fast_method_overloads, 2}); + * \endcode + * + * In this example a single FunctionTemplate is associated to multiple C++ + * functions. The overload resolution is currently only based on the number of + * arguments passed in a call. For example, if this method_template is + * registered with a wrapper JS object as described above, a call with two + * arguments: + * obj.method(42, true); + * will result in a fast call to FastMethod_2Args, while a call with three or + * more arguments: + * obj.method(42, true, 11); + * will result in a fast call to FastMethod_3Args. Instead a call with less than + * two arguments, like: + * obj.method(42); + * would not result in a fast call but would fall back to executing the + * associated SlowCallback. + */ + +#ifndef INCLUDE_V8_FAST_API_CALLS_H_ +#define INCLUDE_V8_FAST_API_CALLS_H_ + +#include +#include + +#include +#include + +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-typed-array.h" // NOLINT(build/include_directory) +#include "v8-value.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Isolate; + +class CTypeInfo { + public: + enum class Type : uint8_t { + kVoid, + kBool, + kUint8, + kInt32, + kUint32, + kInt64, + kUint64, + kFloat32, + kFloat64, + kV8Value, + kApiObject, // This will be deprecated once all users have + // migrated from v8::ApiObject to v8::Local. + kAny, // This is added to enable untyped representation of fast + // call arguments for test purposes. It can represent any of + // the other types stored in the same memory as a union (see + // the AnyCType struct declared below). This allows for + // uniform passing of arguments w.r.t. their location + // (in a register or on the stack), independent of their + // actual type. It's currently used by the arm64 simulator + // and can be added to the other simulators as well when fast + // calls having both GP and FP params need to be supported. + }; + + // kCallbackOptionsType is not part of the Type enum + // because it is only used internally. Use value 255 that is larger + // than any valid Type enum. + static constexpr Type kCallbackOptionsType = Type(255); + + enum class SequenceType : uint8_t { + kScalar, + kIsSequence, // sequence + kIsTypedArray, // TypedArray of T or any ArrayBufferView if T + // is void + kIsArrayBuffer // ArrayBuffer + }; + + enum class Flags : uint8_t { + kNone = 0, + kAllowSharedBit = 1 << 0, // Must be an ArrayBuffer or TypedArray + kEnforceRangeBit = 1 << 1, // T must be integral + kClampBit = 1 << 2, // T must be integral + kIsRestrictedBit = 1 << 3, // T must be float or double + }; + + explicit constexpr CTypeInfo( + Type type, SequenceType sequence_type = SequenceType::kScalar, + Flags flags = Flags::kNone) + : type_(type), sequence_type_(sequence_type), flags_(flags) {} + + typedef uint32_t Identifier; + explicit constexpr CTypeInfo(Identifier identifier) + : CTypeInfo(static_cast(identifier >> 16), + static_cast((identifier >> 8) & 255), + static_cast(identifier & 255)) {} + constexpr Identifier GetId() const { + return static_cast(type_) << 16 | + static_cast(sequence_type_) << 8 | + static_cast(flags_); + } + + constexpr Type GetType() const { return type_; } + constexpr SequenceType GetSequenceType() const { return sequence_type_; } + constexpr Flags GetFlags() const { return flags_; } + + static constexpr bool IsIntegralType(Type type) { + return type == Type::kUint8 || type == Type::kInt32 || + type == Type::kUint32 || type == Type::kInt64 || + type == Type::kUint64; + } + + static constexpr bool IsFloatingPointType(Type type) { + return type == Type::kFloat32 || type == Type::kFloat64; + } + + static constexpr bool IsPrimitive(Type type) { + return IsIntegralType(type) || IsFloatingPointType(type) || + type == Type::kBool; + } + + private: + Type type_; + SequenceType sequence_type_; + Flags flags_; +}; + +struct FastApiTypedArrayBase { + public: + // Returns the length in number of elements. + size_t V8_EXPORT length() const { return length_; } + // Checks whether the given index is within the bounds of the collection. + void V8_EXPORT ValidateIndex(size_t index) const; + + protected: + size_t length_ = 0; +}; + +template +struct FastApiTypedArray : public FastApiTypedArrayBase { + public: + V8_INLINE T get(size_t index) const { +#ifdef DEBUG + ValidateIndex(index); +#endif // DEBUG + T tmp; + memcpy(&tmp, reinterpret_cast(data_) + index, sizeof(T)); + return tmp; + } + + bool getStorageIfAligned(T** elements) const { + if (reinterpret_cast(data_) % alignof(T) != 0) { + return false; + } + *elements = reinterpret_cast(data_); + return true; + } + + private: + // This pointer should include the typed array offset applied. + // It's not guaranteed that it's aligned to sizeof(T), it's only + // guaranteed that it's 4-byte aligned, so for 8-byte types we need to + // provide a special implementation for reading from it, which hides + // the possibly unaligned read in the `get` method. + void* data_; +}; + +// Any TypedArray. It uses kTypedArrayBit with base type void +// Overloaded args of ArrayBufferView and TypedArray are not supported +// (for now) because the generic “any” ArrayBufferView doesn’t have its +// own instance type. It could be supported if we specify that +// TypedArray always has precedence over the generic ArrayBufferView, +// but this complicates overload resolution. +struct FastApiArrayBufferView { + void* data; + size_t byte_length; +}; + +struct FastApiArrayBuffer { + void* data; + size_t byte_length; +}; + +class V8_EXPORT CFunctionInfo { + public: + // Construct a struct to hold a CFunction's type information. + // |return_info| describes the function's return type. + // |arg_info| is an array of |arg_count| CTypeInfos describing the + // arguments. Only the last argument may be of the special type + // CTypeInfo::kCallbackOptionsType. + CFunctionInfo(const CTypeInfo& return_info, unsigned int arg_count, + const CTypeInfo* arg_info); + + const CTypeInfo& ReturnInfo() const { return return_info_; } + + // The argument count, not including the v8::FastApiCallbackOptions + // if present. + unsigned int ArgumentCount() const { + return HasOptions() ? arg_count_ - 1 : arg_count_; + } + + // |index| must be less than ArgumentCount(). + // Note: if the last argument passed on construction of CFunctionInfo + // has type CTypeInfo::kCallbackOptionsType, it is not included in + // ArgumentCount(). + const CTypeInfo& ArgumentInfo(unsigned int index) const; + + bool HasOptions() const { + // The options arg is always the last one. + return arg_count_ > 0 && arg_info_[arg_count_ - 1].GetType() == + CTypeInfo::kCallbackOptionsType; + } + + private: + const CTypeInfo return_info_; + const unsigned int arg_count_; + const CTypeInfo* arg_info_; +}; + +struct FastApiCallbackOptions; + +// Provided for testing. +struct AnyCType { + AnyCType() : int64_value(0) {} + + union { + bool bool_value; + int32_t int32_value; + uint32_t uint32_value; + int64_t int64_value; + uint64_t uint64_value; + float float_value; + double double_value; + Local object_value; + Local sequence_value; + const FastApiTypedArray* uint8_ta_value; + const FastApiTypedArray* int32_ta_value; + const FastApiTypedArray* uint32_ta_value; + const FastApiTypedArray* int64_ta_value; + const FastApiTypedArray* uint64_ta_value; + const FastApiTypedArray* float_ta_value; + const FastApiTypedArray* double_ta_value; + FastApiCallbackOptions* options_value; + }; +}; + +static_assert( + sizeof(AnyCType) == 8, + "The AnyCType struct should have size == 64 bits, as this is assumed " + "by EffectControlLinearizer."); + +class V8_EXPORT CFunction { + public: + constexpr CFunction() : address_(nullptr), type_info_(nullptr) {} + + const CTypeInfo& ReturnInfo() const { return type_info_->ReturnInfo(); } + + const CTypeInfo& ArgumentInfo(unsigned int index) const { + return type_info_->ArgumentInfo(index); + } + + unsigned int ArgumentCount() const { return type_info_->ArgumentCount(); } + + const void* GetAddress() const { return address_; } + const CFunctionInfo* GetTypeInfo() const { return type_info_; } + + enum class OverloadResolution { kImpossible, kAtRuntime, kAtCompileTime }; + + // Returns whether an overload between this and the given CFunction can + // be resolved at runtime by the RTTI available for the arguments or at + // compile time for functions with different number of arguments. + OverloadResolution GetOverloadResolution(const CFunction* other) { + // Runtime overload resolution can only deal with functions with the + // same number of arguments. Functions with different arity are handled + // by compile time overload resolution though. + if (ArgumentCount() != other->ArgumentCount()) { + return OverloadResolution::kAtCompileTime; + } + + // The functions can only differ by a single argument position. + int diff_index = -1; + for (unsigned int i = 0; i < ArgumentCount(); ++i) { + if (ArgumentInfo(i).GetSequenceType() != + other->ArgumentInfo(i).GetSequenceType()) { + if (diff_index >= 0) { + return OverloadResolution::kImpossible; + } + diff_index = i; + + // We only support overload resolution between sequence types. + if (ArgumentInfo(i).GetSequenceType() == + CTypeInfo::SequenceType::kScalar || + other->ArgumentInfo(i).GetSequenceType() == + CTypeInfo::SequenceType::kScalar) { + return OverloadResolution::kImpossible; + } + } + } + + return OverloadResolution::kAtRuntime; + } + + template + static CFunction Make(F* func) { + return ArgUnwrap::Make(func); + } + + // Provided for testing purposes. + template + static CFunction Make(R (*func)(Args...), + R_Patch (*patching_func)(Args_Patch...)) { + CFunction c_func = ArgUnwrap::Make(func); + static_assert( + sizeof...(Args_Patch) == sizeof...(Args), + "The patching function must have the same number of arguments."); + c_func.address_ = reinterpret_cast(patching_func); + return c_func; + } + + CFunction(const void* address, const CFunctionInfo* type_info); + + private: + const void* address_; + const CFunctionInfo* type_info_; + + template + class ArgUnwrap { + static_assert(sizeof(F) != sizeof(F), + "CFunction must be created from a function pointer."); + }; + + template + class ArgUnwrap { + public: + static CFunction Make(R (*func)(Args...)); + }; +}; + +/** + * A struct which may be passed to a fast call callback, like so: + * \code + * void FastMethodWithOptions(int param, FastApiCallbackOptions& options); + * \endcode + */ +struct FastApiCallbackOptions { + /** + * Creates a new instance of FastApiCallbackOptions for testing purpose. The + * returned instance may be filled with mock data. + */ + static FastApiCallbackOptions CreateForTesting(Isolate* isolate) { + return {false, {0}, nullptr}; + } + + /** + * If the callback wants to signal an error condition or to perform an + * allocation, it must set options.fallback to true and do an early return + * from the fast method. Then V8 checks the value of options.fallback and if + * it's true, falls back to executing the SlowCallback, which is capable of + * reporting the error (either by throwing a JS exception or logging to the + * console) or doing the allocation. It's the embedder's responsibility to + * ensure that the fast callback is idempotent up to the point where error and + * fallback conditions are checked, because otherwise executing the slow + * callback might produce visible side-effects twice. + */ + bool fallback; + + /** + * The `data` passed to the FunctionTemplate constructor, or `undefined`. + * `data_ptr` allows for default constructing FastApiCallbackOptions. + */ + union { + uintptr_t data_ptr; + v8::Local data; + }; + + /** + * When called from WebAssembly, a view of the calling module's memory. + */ + FastApiTypedArray* const wasm_memory; +}; + +namespace internal { + +// Helper to count the number of occurances of `T` in `List` +template +struct count : std::integral_constant {}; +template +struct count + : std::integral_constant::value> {}; +template +struct count : count {}; + +template +class CFunctionInfoImpl : public CFunctionInfo { + static constexpr int kOptionsArgCount = + count(); + static constexpr int kReceiverCount = 1; + + static_assert(kOptionsArgCount == 0 || kOptionsArgCount == 1, + "Only one options parameter is supported."); + + static_assert(sizeof...(ArgBuilders) >= kOptionsArgCount + kReceiverCount, + "The receiver or the options argument is missing."); + + public: + constexpr CFunctionInfoImpl() + : CFunctionInfo(RetBuilder::Build(), sizeof...(ArgBuilders), + arg_info_storage_), + arg_info_storage_{ArgBuilders::Build()...} { + constexpr CTypeInfo::Type kReturnType = RetBuilder::Build().GetType(); + static_assert(kReturnType == CTypeInfo::Type::kVoid || + kReturnType == CTypeInfo::Type::kBool || + kReturnType == CTypeInfo::Type::kInt32 || + kReturnType == CTypeInfo::Type::kUint32 || + kReturnType == CTypeInfo::Type::kFloat32 || + kReturnType == CTypeInfo::Type::kFloat64 || + kReturnType == CTypeInfo::Type::kAny, + "64-bit int and api object values are not currently " + "supported return types."); + } + + private: + const CTypeInfo arg_info_storage_[sizeof...(ArgBuilders)]; +}; + +template +struct TypeInfoHelper { + static_assert(sizeof(T) != sizeof(T), "This type is not supported"); +}; + +#define SPECIALIZE_GET_TYPE_INFO_HELPER_FOR(T, Enum) \ + template <> \ + struct TypeInfoHelper { \ + static constexpr CTypeInfo::Flags Flags() { \ + return CTypeInfo::Flags::kNone; \ + } \ + \ + static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::Enum; } \ + static constexpr CTypeInfo::SequenceType SequenceType() { \ + return CTypeInfo::SequenceType::kScalar; \ + } \ + }; + +template +struct CTypeInfoTraits {}; + +#define DEFINE_TYPE_INFO_TRAITS(CType, Enum) \ + template <> \ + struct CTypeInfoTraits { \ + using ctype = CType; \ + }; + +#define PRIMITIVE_C_TYPES(V) \ + V(bool, kBool) \ + V(int32_t, kInt32) \ + V(uint32_t, kUint32) \ + V(int64_t, kInt64) \ + V(uint64_t, kUint64) \ + V(float, kFloat32) \ + V(double, kFloat64) \ + V(uint8_t, kUint8) + +// Same as above, but includes deprecated types for compatibility. +#define ALL_C_TYPES(V) \ + PRIMITIVE_C_TYPES(V) \ + V(void, kVoid) \ + V(v8::Local, kV8Value) \ + V(v8::Local, kV8Value) \ + V(AnyCType, kAny) + +// ApiObject was a temporary solution to wrap the pointer to the v8::Value. +// Please use v8::Local in new code for the arguments and +// v8::Local for the receiver, as ApiObject will be deprecated. + +ALL_C_TYPES(SPECIALIZE_GET_TYPE_INFO_HELPER_FOR) +PRIMITIVE_C_TYPES(DEFINE_TYPE_INFO_TRAITS) + +#undef PRIMITIVE_C_TYPES +#undef ALL_C_TYPES + +#define SPECIALIZE_GET_TYPE_INFO_HELPER_FOR_TA(T, Enum) \ + template <> \ + struct TypeInfoHelper&> { \ + static constexpr CTypeInfo::Flags Flags() { \ + return CTypeInfo::Flags::kNone; \ + } \ + \ + static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::Enum; } \ + static constexpr CTypeInfo::SequenceType SequenceType() { \ + return CTypeInfo::SequenceType::kIsTypedArray; \ + } \ + }; + +#define TYPED_ARRAY_C_TYPES(V) \ + V(int32_t, kInt32) \ + V(uint32_t, kUint32) \ + V(int64_t, kInt64) \ + V(uint64_t, kUint64) \ + V(float, kFloat32) \ + V(double, kFloat64) \ + V(uint8_t, kUint8) + +TYPED_ARRAY_C_TYPES(SPECIALIZE_GET_TYPE_INFO_HELPER_FOR_TA) + +#undef TYPED_ARRAY_C_TYPES + +template <> +struct TypeInfoHelper> { + static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; } + + static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::kVoid; } + static constexpr CTypeInfo::SequenceType SequenceType() { + return CTypeInfo::SequenceType::kIsSequence; + } +}; + +template <> +struct TypeInfoHelper> { + static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; } + + static constexpr CTypeInfo::Type Type() { return CTypeInfo::Type::kUint32; } + static constexpr CTypeInfo::SequenceType SequenceType() { + return CTypeInfo::SequenceType::kIsTypedArray; + } +}; + +template <> +struct TypeInfoHelper { + static constexpr CTypeInfo::Flags Flags() { return CTypeInfo::Flags::kNone; } + + static constexpr CTypeInfo::Type Type() { + return CTypeInfo::kCallbackOptionsType; + } + static constexpr CTypeInfo::SequenceType SequenceType() { + return CTypeInfo::SequenceType::kScalar; + } +}; + +#define STATIC_ASSERT_IMPLIES(COND, ASSERTION, MSG) \ + static_assert(((COND) == 0) || (ASSERTION), MSG) + +} // namespace internal + +template +class V8_EXPORT CTypeInfoBuilder { + public: + using BaseType = T; + + static constexpr CTypeInfo Build() { + constexpr CTypeInfo::Flags kFlags = + MergeFlags(internal::TypeInfoHelper::Flags(), Flags...); + constexpr CTypeInfo::Type kType = internal::TypeInfoHelper::Type(); + constexpr CTypeInfo::SequenceType kSequenceType = + internal::TypeInfoHelper::SequenceType(); + + STATIC_ASSERT_IMPLIES( + uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kAllowSharedBit), + (kSequenceType == CTypeInfo::SequenceType::kIsTypedArray || + kSequenceType == CTypeInfo::SequenceType::kIsArrayBuffer), + "kAllowSharedBit is only allowed for TypedArrays and ArrayBuffers."); + STATIC_ASSERT_IMPLIES( + uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kEnforceRangeBit), + CTypeInfo::IsIntegralType(kType), + "kEnforceRangeBit is only allowed for integral types."); + STATIC_ASSERT_IMPLIES( + uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kClampBit), + CTypeInfo::IsIntegralType(kType), + "kClampBit is only allowed for integral types."); + STATIC_ASSERT_IMPLIES( + uint8_t(kFlags) & uint8_t(CTypeInfo::Flags::kIsRestrictedBit), + CTypeInfo::IsFloatingPointType(kType), + "kIsRestrictedBit is only allowed for floating point types."); + STATIC_ASSERT_IMPLIES(kSequenceType == CTypeInfo::SequenceType::kIsSequence, + kType == CTypeInfo::Type::kVoid, + "Sequences are only supported from void type."); + STATIC_ASSERT_IMPLIES( + kSequenceType == CTypeInfo::SequenceType::kIsTypedArray, + CTypeInfo::IsPrimitive(kType) || kType == CTypeInfo::Type::kVoid, + "TypedArrays are only supported from primitive types or void."); + + // Return the same type with the merged flags. + return CTypeInfo(internal::TypeInfoHelper::Type(), + internal::TypeInfoHelper::SequenceType(), kFlags); + } + + private: + template + static constexpr CTypeInfo::Flags MergeFlags(CTypeInfo::Flags flags, + Rest... rest) { + return CTypeInfo::Flags(uint8_t(flags) | uint8_t(MergeFlags(rest...))); + } + static constexpr CTypeInfo::Flags MergeFlags() { return CTypeInfo::Flags(0); } +}; + +namespace internal { +template +class CFunctionBuilderWithFunction { + public: + explicit constexpr CFunctionBuilderWithFunction(const void* fn) : fn_(fn) {} + + template + constexpr auto Ret() { + return CFunctionBuilderWithFunction< + CTypeInfoBuilder, + ArgBuilders...>(fn_); + } + + template + constexpr auto Arg() { + // Return a copy of the builder with the Nth arg builder merged with + // template parameter pack Flags. + return ArgImpl( + std::make_index_sequence()); + } + + // Provided for testing purposes. + template + auto Patch(Ret (*patching_func)(Args...)) { + static_assert( + sizeof...(Args) == sizeof...(ArgBuilders), + "The patching function must have the same number of arguments."); + fn_ = reinterpret_cast(patching_func); + return *this; + } + + auto Build() { + static CFunctionInfoImpl instance; + return CFunction(fn_, &instance); + } + + private: + template + struct GetArgBuilder; + + // Returns the same ArgBuilder as the one at index N, including its flags. + // Flags in the template parameter pack are ignored. + template + struct GetArgBuilder { + using type = + typename std::tuple_element>::type; + }; + + // Returns an ArgBuilder with the same base type as the one at index N, + // but merges the flags with the flags in the template parameter pack. + template + struct GetArgBuilder { + using type = CTypeInfoBuilder< + typename std::tuple_element>::type::BaseType, + std::tuple_element>::type::Build() + .GetFlags(), + Flags...>; + }; + + // Return a copy of the CFunctionBuilder, but merges the Flags on + // ArgBuilder index N with the new Flags passed in the template parameter + // pack. + template + constexpr auto ArgImpl(std::index_sequence) { + return CFunctionBuilderWithFunction< + RetBuilder, typename GetArgBuilder::type...>(fn_); + } + + const void* fn_; +}; + +class CFunctionBuilder { + public: + constexpr CFunctionBuilder() {} + + template + constexpr auto Fn(R (*fn)(Args...)) { + return CFunctionBuilderWithFunction, + CTypeInfoBuilder...>( + reinterpret_cast(fn)); + } +}; + +} // namespace internal + +// static +template +CFunction CFunction::ArgUnwrap::Make(R (*func)(Args...)) { + return internal::CFunctionBuilder().Fn(func).Build(); +} + +using CFunctionBuilder = internal::CFunctionBuilder; + +static constexpr CTypeInfo kTypeInfoInt32 = CTypeInfo(CTypeInfo::Type::kInt32); +static constexpr CTypeInfo kTypeInfoFloat64 = + CTypeInfo(CTypeInfo::Type::kFloat64); + +/** + * Copies the contents of this JavaScript array to a C++ buffer with + * a given max_length. A CTypeInfo is passed as an argument, + * instructing different rules for conversion (e.g. restricted float/double). + * The element type T of the destination array must match the C type + * corresponding to the CTypeInfo (specified by CTypeInfoTraits). + * If the array length is larger than max_length or the array is of + * unsupported type, the operation will fail, returning false. Generally, an + * array which contains objects, undefined, null or anything not convertible + * to the requested destination type, is considered unsupported. The operation + * returns true on success. `type_info` will be used for conversions. + */ +template +bool V8_EXPORT V8_WARN_UNUSED_RESULT TryToCopyAndConvertArrayToCppBuffer( + Local src, T* dst, uint32_t max_length); + +template <> +bool V8_EXPORT V8_WARN_UNUSED_RESULT +TryToCopyAndConvertArrayToCppBuffer::Build().GetId(), + int32_t>(Local src, int32_t* dst, + uint32_t max_length); + +template <> +bool V8_EXPORT V8_WARN_UNUSED_RESULT +TryToCopyAndConvertArrayToCppBuffer::Build().GetId(), + uint32_t>(Local src, uint32_t* dst, + uint32_t max_length); + +template <> +bool V8_EXPORT V8_WARN_UNUSED_RESULT +TryToCopyAndConvertArrayToCppBuffer::Build().GetId(), + float>(Local src, float* dst, + uint32_t max_length); + +template <> +bool V8_EXPORT V8_WARN_UNUSED_RESULT +TryToCopyAndConvertArrayToCppBuffer::Build().GetId(), + double>(Local src, double* dst, + uint32_t max_length); + +} // namespace v8 + +#endif // INCLUDE_V8_FAST_API_CALLS_H_ diff --git a/deps/include/v8-forward.h b/deps/include/v8-forward.h new file mode 100755 index 0000000..db3a201 --- /dev/null +++ b/deps/include/v8-forward.h @@ -0,0 +1,81 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_FORWARD_H_ +#define INCLUDE_V8_FORWARD_H_ + +// This header is intended to be used by headers that pass around V8 types, +// either by pointer or using Local. The full definitions can be included +// either via v8.h or the more fine-grained headers. + +#include "v8-local-handle.h" // NOLINT(build/include_directory) + +namespace v8 { + +class AccessorSignature; +class Array; +class ArrayBuffer; +class ArrayBufferView; +class BigInt; +class BigInt64Array; +class BigIntObject; +class BigUint64Array; +class Boolean; +class BooleanObject; +class Context; +class DataView; +class Data; +class Date; +class Extension; +class External; +class FixedArray; +class Float32Array; +class Float64Array; +class Function; +template +class FunctionCallbackInfo; +class FunctionTemplate; +class Int16Array; +class Int32; +class Int32Array; +class Int8Array; +class Integer; +class Isolate; +class Map; +class Module; +class Name; +class Number; +class NumberObject; +class Object; +class ObjectTemplate; +class Platform; +class Primitive; +class Private; +class Promise; +class Proxy; +class RegExp; +class Script; +class Set; +class SharedArrayBuffer; +class Signature; +class String; +class StringObject; +class Symbol; +class SymbolObject; +class Template; +class TryCatch; +class TypedArray; +class Uint16Array; +class Uint32; +class Uint32Array; +class Uint8Array; +class Uint8ClampedArray; +class UnboundModuleScript; +class Value; +class WasmMemoryObject; +class WasmModuleObject; + +} // namespace v8 + +#endif // INCLUDE_V8_FORWARD_H_ diff --git a/deps/include/v8-function-callback.h b/deps/include/v8-function-callback.h new file mode 100755 index 0000000..2adff99 --- /dev/null +++ b/deps/include/v8-function-callback.h @@ -0,0 +1,475 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_FUNCTION_CALLBACK_H_ +#define INCLUDE_V8_FUNCTION_CALLBACK_H_ + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-primitive.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +template +class BasicTracedReference; +template +class Global; +class Object; +class Value; + +namespace internal { +class FunctionCallbackArguments; +class PropertyCallbackArguments; +} // namespace internal + +namespace debug { +class ConsoleCallArguments; +} // namespace debug + +template +class ReturnValue { + public: + template + V8_INLINE ReturnValue(const ReturnValue& that) : value_(that.value_) { + static_assert(std::is_base_of::value, "type check"); + } + // Local setters + template + V8_INLINE void Set(const Global& handle); + template + V8_INLINE void Set(const BasicTracedReference& handle); + template + V8_INLINE void Set(const Local handle); + // Fast primitive setters + V8_INLINE void Set(bool value); + V8_INLINE void Set(double i); + V8_INLINE void Set(int32_t i); + V8_INLINE void Set(uint32_t i); + // Fast JS primitive setters + V8_INLINE void SetNull(); + V8_INLINE void SetUndefined(); + V8_INLINE void SetEmptyString(); + // Convenience getter for Isolate + V8_INLINE Isolate* GetIsolate() const; + + // Pointer setter: Uncompilable to prevent inadvertent misuse. + template + V8_INLINE void Set(S* whatever); + + // Getter. Creates a new Local<> so it comes with a certain performance + // hit. If the ReturnValue was not yet set, this will return the undefined + // value. + V8_INLINE Local Get() const; + + private: + template + friend class ReturnValue; + template + friend class FunctionCallbackInfo; + template + friend class PropertyCallbackInfo; + template + friend class PersistentValueMapBase; + V8_INLINE void SetInternal(internal::Address value) { *value_ = value; } + V8_INLINE internal::Address GetDefaultValue(); + V8_INLINE explicit ReturnValue(internal::Address* slot); + internal::Address* value_; +}; + +/** + * The argument information given to function call callbacks. This + * class provides access to information about the context of the call, + * including the receiver, the number and values of arguments, and + * the holder of the function. + */ +template +class FunctionCallbackInfo { + public: + /** The number of available arguments. */ + V8_INLINE int Length() const; + /** + * Accessor for the available arguments. Returns `undefined` if the index + * is out of bounds. + */ + V8_INLINE Local operator[](int i) const; + /** Returns the receiver. This corresponds to the "this" value. */ + V8_INLINE Local This() const; + /** + * If the callback was created without a Signature, this is the same + * value as This(). If there is a signature, and the signature didn't match + * This() but one of its hidden prototypes, this will be the respective + * hidden prototype. + * + * Note that this is not the prototype of This() on which the accessor + * referencing this callback was found (which in V8 internally is often + * referred to as holder [sic]). + */ + V8_INLINE Local Holder() const; + /** For construct calls, this returns the "new.target" value. */ + V8_INLINE Local NewTarget() const; + /** Indicates whether this is a regular call or a construct call. */ + V8_INLINE bool IsConstructCall() const; + /** The data argument specified when creating the callback. */ + V8_INLINE Local Data() const; + /** The current Isolate. */ + V8_INLINE Isolate* GetIsolate() const; + /** The ReturnValue for the call. */ + V8_INLINE ReturnValue GetReturnValue() const; + // This shouldn't be public, but the arm compiler needs it. + static const int kArgsLength = 6; + + protected: + friend class internal::FunctionCallbackArguments; + friend class internal::CustomArguments; + friend class debug::ConsoleCallArguments; + static const int kHolderIndex = 0; + static const int kIsolateIndex = 1; + static const int kReturnValueDefaultValueIndex = 2; + static const int kReturnValueIndex = 3; + static const int kDataIndex = 4; + static const int kNewTargetIndex = 5; + + V8_INLINE FunctionCallbackInfo(internal::Address* implicit_args, + internal::Address* values, int length); + internal::Address* implicit_args_; + internal::Address* values_; + int length_; +}; + +/** + * The information passed to a property callback about the context + * of the property access. + */ +template +class PropertyCallbackInfo { + public: + /** + * \return The isolate of the property access. + */ + V8_INLINE Isolate* GetIsolate() const; + + /** + * \return The data set in the configuration, i.e., in + * `NamedPropertyHandlerConfiguration` or + * `IndexedPropertyHandlerConfiguration.` + */ + V8_INLINE Local Data() const; + + /** + * \return The receiver. In many cases, this is the object on which the + * property access was intercepted. When using + * `Reflect.get`, `Function.prototype.call`, or similar functions, it is the + * object passed in as receiver or thisArg. + * + * \code + * void GetterCallback(Local name, + * const v8::PropertyCallbackInfo& info) { + * auto context = info.GetIsolate()->GetCurrentContext(); + * + * v8::Local a_this = + * info.This() + * ->GetRealNamedProperty(context, v8_str("a")) + * .ToLocalChecked(); + * v8::Local a_holder = + * info.Holder() + * ->GetRealNamedProperty(context, v8_str("a")) + * .ToLocalChecked(); + * + * CHECK(v8_str("r")->Equals(context, a_this).FromJust()); + * CHECK(v8_str("obj")->Equals(context, a_holder).FromJust()); + * + * info.GetReturnValue().Set(name); + * } + * + * v8::Local templ = + * v8::FunctionTemplate::New(isolate); + * templ->InstanceTemplate()->SetHandler( + * v8::NamedPropertyHandlerConfiguration(GetterCallback)); + * LocalContext env; + * env->Global() + * ->Set(env.local(), v8_str("obj"), templ->GetFunction(env.local()) + * .ToLocalChecked() + * ->NewInstance(env.local()) + * .ToLocalChecked()) + * .FromJust(); + * + * CompileRun("obj.a = 'obj'; var r = {a: 'r'}; Reflect.get(obj, 'x', r)"); + * \endcode + */ + V8_INLINE Local This() const; + + /** + * \return The object in the prototype chain of the receiver that has the + * interceptor. Suppose you have `x` and its prototype is `y`, and `y` + * has an interceptor. Then `info.This()` is `x` and `info.Holder()` is `y`. + * The Holder() could be a hidden object (the global object, rather + * than the global proxy). + * + * \note For security reasons, do not pass the object back into the runtime. + */ + V8_INLINE Local Holder() const; + + /** + * \return The return value of the callback. + * Can be changed by calling Set(). + * \code + * info.GetReturnValue().Set(...) + * \endcode + * + */ + V8_INLINE ReturnValue GetReturnValue() const; + + /** + * \return True if the intercepted function should throw if an error occurs. + * Usually, `true` corresponds to `'use strict'`. + * + * \note Always `false` when intercepting `Reflect.set()` + * independent of the language mode. + */ + V8_INLINE bool ShouldThrowOnError() const; + + // This shouldn't be public, but the arm compiler needs it. + static const int kArgsLength = 7; + + protected: + friend class MacroAssembler; + friend class internal::PropertyCallbackArguments; + friend class internal::CustomArguments; + static const int kShouldThrowOnErrorIndex = 0; + static const int kHolderIndex = 1; + static const int kIsolateIndex = 2; + static const int kReturnValueDefaultValueIndex = 3; + static const int kReturnValueIndex = 4; + static const int kDataIndex = 5; + static const int kThisIndex = 6; + + V8_INLINE PropertyCallbackInfo(internal::Address* args) : args_(args) {} + internal::Address* args_; +}; + +using FunctionCallback = void (*)(const FunctionCallbackInfo& info); + +// --- Implementation --- + +template +ReturnValue::ReturnValue(internal::Address* slot) : value_(slot) {} + +template +template +void ReturnValue::Set(const Global& handle) { + static_assert(std::is_base_of::value, "type check"); + if (V8_UNLIKELY(handle.IsEmpty())) { + *value_ = GetDefaultValue(); + } else { + *value_ = *reinterpret_cast(*handle); + } +} + +template +template +void ReturnValue::Set(const BasicTracedReference& handle) { + static_assert(std::is_base_of::value, "type check"); + if (V8_UNLIKELY(handle.IsEmpty())) { + *value_ = GetDefaultValue(); + } else { + *value_ = *reinterpret_cast(handle.val_); + } +} + +template +template +void ReturnValue::Set(const Local handle) { + static_assert(std::is_void::value || std::is_base_of::value, + "type check"); + if (V8_UNLIKELY(handle.IsEmpty())) { + *value_ = GetDefaultValue(); + } else { + *value_ = *reinterpret_cast(*handle); + } +} + +template +void ReturnValue::Set(double i) { + static_assert(std::is_base_of::value, "type check"); + Set(Number::New(GetIsolate(), i)); +} + +template +void ReturnValue::Set(int32_t i) { + static_assert(std::is_base_of::value, "type check"); + using I = internal::Internals; + if (V8_LIKELY(I::IsValidSmi(i))) { + *value_ = I::IntToSmi(i); + return; + } + Set(Integer::New(GetIsolate(), i)); +} + +template +void ReturnValue::Set(uint32_t i) { + static_assert(std::is_base_of::value, "type check"); + // Can't simply use INT32_MAX here for whatever reason. + bool fits_into_int32_t = (i & (1U << 31)) == 0; + if (V8_LIKELY(fits_into_int32_t)) { + Set(static_cast(i)); + return; + } + Set(Integer::NewFromUnsigned(GetIsolate(), i)); +} + +template +void ReturnValue::Set(bool value) { + static_assert(std::is_base_of::value, "type check"); + using I = internal::Internals; + int root_index; + if (value) { + root_index = I::kTrueValueRootIndex; + } else { + root_index = I::kFalseValueRootIndex; + } + *value_ = *I::GetRoot(GetIsolate(), root_index); +} + +template +void ReturnValue::SetNull() { + static_assert(std::is_base_of::value, "type check"); + using I = internal::Internals; + *value_ = *I::GetRoot(GetIsolate(), I::kNullValueRootIndex); +} + +template +void ReturnValue::SetUndefined() { + static_assert(std::is_base_of::value, "type check"); + using I = internal::Internals; + *value_ = *I::GetRoot(GetIsolate(), I::kUndefinedValueRootIndex); +} + +template +void ReturnValue::SetEmptyString() { + static_assert(std::is_base_of::value, "type check"); + using I = internal::Internals; + *value_ = *I::GetRoot(GetIsolate(), I::kEmptyStringRootIndex); +} + +template +Isolate* ReturnValue::GetIsolate() const { + // Isolate is always the pointer below the default value on the stack. + return *reinterpret_cast(&value_[-2]); +} + +template +Local ReturnValue::Get() const { + using I = internal::Internals; + if (*value_ == *I::GetRoot(GetIsolate(), I::kTheHoleValueRootIndex)) + return Local(*Undefined(GetIsolate())); + return Local::New(GetIsolate(), reinterpret_cast(value_)); +} + +template +template +void ReturnValue::Set(S* whatever) { + static_assert(sizeof(S) < 0, "incompilable to prevent inadvertent misuse"); +} + +template +internal::Address ReturnValue::GetDefaultValue() { + // Default value is always the pointer below value_ on the stack. + return value_[-1]; +} + +template +FunctionCallbackInfo::FunctionCallbackInfo(internal::Address* implicit_args, + internal::Address* values, + int length) + : implicit_args_(implicit_args), values_(values), length_(length) {} + +template +Local FunctionCallbackInfo::operator[](int i) const { + // values_ points to the first argument (not the receiver). + if (i < 0 || length_ <= i) return Local(*Undefined(GetIsolate())); + return Local(reinterpret_cast(values_ + i)); +} + +template +Local FunctionCallbackInfo::This() const { + // values_ points to the first argument (not the receiver). + return Local(reinterpret_cast(values_ - 1)); +} + +template +Local FunctionCallbackInfo::Holder() const { + return Local( + reinterpret_cast(&implicit_args_[kHolderIndex])); +} + +template +Local FunctionCallbackInfo::NewTarget() const { + return Local( + reinterpret_cast(&implicit_args_[kNewTargetIndex])); +} + +template +Local FunctionCallbackInfo::Data() const { + return Local(reinterpret_cast(&implicit_args_[kDataIndex])); +} + +template +Isolate* FunctionCallbackInfo::GetIsolate() const { + return *reinterpret_cast(&implicit_args_[kIsolateIndex]); +} + +template +ReturnValue FunctionCallbackInfo::GetReturnValue() const { + return ReturnValue(&implicit_args_[kReturnValueIndex]); +} + +template +bool FunctionCallbackInfo::IsConstructCall() const { + return !NewTarget()->IsUndefined(); +} + +template +int FunctionCallbackInfo::Length() const { + return length_; +} + +template +Isolate* PropertyCallbackInfo::GetIsolate() const { + return *reinterpret_cast(&args_[kIsolateIndex]); +} + +template +Local PropertyCallbackInfo::Data() const { + return Local(reinterpret_cast(&args_[kDataIndex])); +} + +template +Local PropertyCallbackInfo::This() const { + return Local(reinterpret_cast(&args_[kThisIndex])); +} + +template +Local PropertyCallbackInfo::Holder() const { + return Local(reinterpret_cast(&args_[kHolderIndex])); +} + +template +ReturnValue PropertyCallbackInfo::GetReturnValue() const { + return ReturnValue(&args_[kReturnValueIndex]); +} + +template +bool PropertyCallbackInfo::ShouldThrowOnError() const { + using I = internal::Internals; + if (args_[kShouldThrowOnErrorIndex] != + I::IntToSmi(I::kInferShouldThrowMode)) { + return args_[kShouldThrowOnErrorIndex] != I::IntToSmi(I::kDontThrow); + } + return v8::internal::ShouldThrowOnError( + reinterpret_cast(GetIsolate())); +} + +} // namespace v8 + +#endif // INCLUDE_V8_FUNCTION_CALLBACK_H_ diff --git a/deps/include/v8-function.h b/deps/include/v8-function.h new file mode 100755 index 0000000..2dc7e72 --- /dev/null +++ b/deps/include/v8-function.h @@ -0,0 +1,133 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_FUNCTION_H_ +#define INCLUDE_V8_FUNCTION_H_ + +#include +#include + +#include "v8-function-callback.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-message.h" // NOLINT(build/include_directory) +#include "v8-object.h" // NOLINT(build/include_directory) +#include "v8-template.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; +class UnboundScript; + +/** + * A JavaScript function object (ECMA-262, 15.3). + */ +class V8_EXPORT Function : public Object { + public: + /** + * Create a function in the current execution context + * for a given FunctionCallback. + */ + static MaybeLocal New( + Local context, FunctionCallback callback, + Local data = Local(), int length = 0, + ConstructorBehavior behavior = ConstructorBehavior::kAllow, + SideEffectType side_effect_type = SideEffectType::kHasSideEffect); + + V8_WARN_UNUSED_RESULT MaybeLocal NewInstance( + Local context, int argc, Local argv[]) const; + + V8_WARN_UNUSED_RESULT MaybeLocal NewInstance( + Local context) const { + return NewInstance(context, 0, nullptr); + } + + /** + * When side effect checks are enabled, passing kHasNoSideEffect allows the + * constructor to be invoked without throwing. Calls made within the + * constructor are still checked. + */ + V8_WARN_UNUSED_RESULT MaybeLocal NewInstanceWithSideEffectType( + Local context, int argc, Local argv[], + SideEffectType side_effect_type = SideEffectType::kHasSideEffect) const; + + V8_WARN_UNUSED_RESULT MaybeLocal Call(Local context, + Local recv, int argc, + Local argv[]); + + void SetName(Local name); + Local GetName() const; + + MaybeLocal GetUnboundScript() const; + + /** + * Name inferred from variable or property assignment of this function. + * Used to facilitate debugging and profiling of JavaScript code written + * in an OO style, where many functions are anonymous but are assigned + * to object properties. + */ + Local GetInferredName() const; + + /** + * displayName if it is set, otherwise name if it is configured, otherwise + * function name, otherwise inferred name. + */ + Local GetDebugName() const; + + /** + * Returns zero based line number of function body and + * kLineOffsetNotFound if no information available. + */ + int GetScriptLineNumber() const; + /** + * Returns zero based column number of function body and + * kLineOffsetNotFound if no information available. + */ + int GetScriptColumnNumber() const; + + /** + * Returns scriptId. + */ + int ScriptId() const; + + /** + * Returns the original function if this function is bound, else returns + * v8::Undefined. + */ + Local GetBoundFunction() const; + + /** + * Calls builtin Function.prototype.toString on this function. + * This is different from Value::ToString() that may call a user-defined + * toString() function, and different than Object::ObjectProtoToString() which + * always serializes "[object Function]". + */ + V8_WARN_UNUSED_RESULT MaybeLocal FunctionProtoToString( + Local context); + + /** + * Returns true if the function does nothing. + * The function returns false on error. + * Note that this function is experimental. Embedders should not rely on + * this existing. We may remove this function in the future. + */ + V8_WARN_UNUSED_RESULT bool Experimental_IsNopFunction() const; + + ScriptOrigin GetScriptOrigin() const; + V8_INLINE static Function* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + static const int kLineOffsetNotFound; + + private: + Function(); + static void CheckCast(Value* obj); +}; +} // namespace v8 + +#endif // INCLUDE_V8_FUNCTION_H_ diff --git a/deps/include/v8-initialization.h b/deps/include/v8-initialization.h new file mode 100755 index 0000000..d3e35d6 --- /dev/null +++ b/deps/include/v8-initialization.h @@ -0,0 +1,289 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_INITIALIZATION_H_ +#define INCLUDE_V8_INITIALIZATION_H_ + +#include +#include + +#include "v8-callbacks.h" // NOLINT(build/include_directory) +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8-isolate.h" // NOLINT(build/include_directory) +#include "v8-platform.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +// We reserve the V8_* prefix for macros defined in V8 public API and +// assume there are no name conflicts with the embedder's code. + +/** + * The v8 JavaScript engine. + */ +namespace v8 { + +class PageAllocator; +class Platform; +template +class PersistentValueMapBase; + +/** + * EntropySource is used as a callback function when v8 needs a source + * of entropy. + */ +using EntropySource = bool (*)(unsigned char* buffer, size_t length); + +/** + * ReturnAddressLocationResolver is used as a callback function when v8 is + * resolving the location of a return address on the stack. Profilers that + * change the return address on the stack can use this to resolve the stack + * location to wherever the profiler stashed the original return address. + * + * \param return_addr_location A location on stack where a machine + * return address resides. + * \returns Either return_addr_location, or else a pointer to the profiler's + * copy of the original return address. + * + * \note The resolver function must not cause garbage collection. + */ +using ReturnAddressLocationResolver = + uintptr_t (*)(uintptr_t return_addr_location); + +using DcheckErrorCallback = void (*)(const char* file, int line, + const char* message); + +/** + * Container class for static utility functions. + */ +class V8_EXPORT V8 { + public: + /** + * Hand startup data to V8, in case the embedder has chosen to build + * V8 with external startup data. + * + * Note: + * - By default the startup data is linked into the V8 library, in which + * case this function is not meaningful. + * - If this needs to be called, it needs to be called before V8 + * tries to make use of its built-ins. + * - To avoid unnecessary copies of data, V8 will point directly into the + * given data blob, so pretty please keep it around until V8 exit. + * - Compression of the startup blob might be useful, but needs to + * handled entirely on the embedders' side. + * - The call will abort if the data is invalid. + */ + static void SetSnapshotDataBlob(StartupData* startup_blob); + + /** Set the callback to invoke in case of Dcheck failures. */ + static void SetDcheckErrorHandler(DcheckErrorCallback that); + + /** + * Sets V8 flags from a string. + */ + static void SetFlagsFromString(const char* str); + static void SetFlagsFromString(const char* str, size_t length); + + /** + * Sets V8 flags from the command line. + */ + static void SetFlagsFromCommandLine(int* argc, char** argv, + bool remove_flags); + + /** Get the version string. */ + static const char* GetVersion(); + + /** + * Initializes V8. This function needs to be called before the first Isolate + * is created. It always returns true. + */ + V8_INLINE static bool Initialize() { + const int kBuildConfiguration = + (internal::PointerCompressionIsEnabled() ? kPointerCompression : 0) | + (internal::SmiValuesAre31Bits() ? k31BitSmis : 0) | + (internal::SandboxIsEnabled() ? kSandbox : 0); + return Initialize(kBuildConfiguration); + } + + /** + * Allows the host application to provide a callback which can be used + * as a source of entropy for random number generators. + */ + static void SetEntropySource(EntropySource source); + + /** + * Allows the host application to provide a callback that allows v8 to + * cooperate with a profiler that rewrites return addresses on stack. + */ + static void SetReturnAddressLocationResolver( + ReturnAddressLocationResolver return_address_resolver); + + /** + * Releases any resources used by v8 and stops any utility threads + * that may be running. Note that disposing v8 is permanent, it + * cannot be reinitialized. + * + * It should generally not be necessary to dispose v8 before exiting + * a process, this should happen automatically. It is only necessary + * to use if the process needs the resources taken up by v8. + */ + static bool Dispose(); + + /** + * Initialize the ICU library bundled with V8. The embedder should only + * invoke this method when using the bundled ICU. Returns true on success. + * + * If V8 was compiled with the ICU data in an external file, the location + * of the data file has to be provided. + */ + static bool InitializeICU(const char* icu_data_file = nullptr); + + /** + * Initialize the ICU library bundled with V8. The embedder should only + * invoke this method when using the bundled ICU. If V8 was compiled with + * the ICU data in an external file and when the default location of that + * file should be used, a path to the executable must be provided. + * Returns true on success. + * + * The default is a file called icudtl.dat side-by-side with the executable. + * + * Optionally, the location of the data file can be provided to override the + * default. + */ + static bool InitializeICUDefaultLocation(const char* exec_path, + const char* icu_data_file = nullptr); + + /** + * Initialize the external startup data. The embedder only needs to + * invoke this method when external startup data was enabled in a build. + * + * If V8 was compiled with the startup data in an external file, then + * V8 needs to be given those external files during startup. There are + * three ways to do this: + * - InitializeExternalStartupData(const char*) + * This will look in the given directory for the file "snapshot_blob.bin". + * - InitializeExternalStartupDataFromFile(const char*) + * As above, but will directly use the given file name. + * - Call SetSnapshotDataBlob. + * This will read the blobs from the given data structure and will + * not perform any file IO. + */ + static void InitializeExternalStartupData(const char* directory_path); + static void InitializeExternalStartupDataFromFile(const char* snapshot_blob); + + /** + * Sets the v8::Platform to use. This should be invoked before V8 is + * initialized. + */ + static void InitializePlatform(Platform* platform); + + /** + * Clears all references to the v8::Platform. This should be invoked after + * V8 was disposed. + */ + static void DisposePlatform(); + +#if defined(V8_ENABLE_SANDBOX) + /** + * Returns true if the sandbox is configured securely. + * + * If V8 cannot create a regular sandbox during initialization, for example + * because not enough virtual address space can be reserved, it will instead + * create a fallback sandbox that still allows it to function normally but + * does not have the same security properties as a regular sandbox. This API + * can be used to determine if such a fallback sandbox is being used, in + * which case it will return false. + */ + static bool IsSandboxConfiguredSecurely(); + + /** + * Provides access to the virtual address subspace backing the sandbox. + * + * This can be used to allocate pages inside the sandbox, for example to + * obtain virtual memory for ArrayBuffer backing stores, which must be + * located inside the sandbox. + * + * It should be assumed that an attacker can corrupt data inside the sandbox, + * and so in particular the contents of pages allocagted in this virtual + * address space, arbitrarily and concurrently. Due to this, it is + * recommended to to only place pure data buffers in them. + */ + static VirtualAddressSpace* GetSandboxAddressSpace(); + + /** + * Returns the size of the sandbox in bytes. + * + * This represents the size of the address space that V8 can directly address + * and in which it allocates its objects. + */ + static size_t GetSandboxSizeInBytes(); + + /** + * Returns the size of the address space reservation backing the sandbox. + * + * This may be larger than the sandbox (i.e. |GetSandboxSizeInBytes()|) due + * to surrounding guard regions, or may be smaller than the sandbox in case a + * fallback sandbox is being used, which will use a smaller virtual address + * space reservation. In the latter case this will also be different from + * |GetSandboxAddressSpace()->size()| as that will cover a larger part of the + * address space than what has actually been reserved. + */ + static size_t GetSandboxReservationSizeInBytes(); +#endif // V8_ENABLE_SANDBOX + + /** + * Activate trap-based bounds checking for WebAssembly. + * + * \param use_v8_signal_handler Whether V8 should install its own signal + * handler or rely on the embedder's. + */ + static bool EnableWebAssemblyTrapHandler(bool use_v8_signal_handler); + +#if defined(V8_OS_WIN) + /** + * On Win64, by default V8 does not emit unwinding data for jitted code, + * which means the OS cannot walk the stack frames and the system Structured + * Exception Handling (SEH) cannot unwind through V8-generated code: + * https://code.google.com/p/v8/issues/detail?id=3598. + * + * This function allows embedders to register a custom exception handler for + * exceptions in V8-generated code. + */ + static void SetUnhandledExceptionCallback( + UnhandledExceptionCallback callback); +#endif + + /** + * Allows the host application to provide a callback that will be called when + * v8 has encountered a fatal failure to allocate memory and is about to + * terminate. + */ + static void SetFatalMemoryErrorCallback(OOMErrorCallback callback); + + /** + * Get statistics about the shared memory usage. + */ + static void GetSharedMemoryStatistics(SharedMemoryStatistics* statistics); + + private: + V8(); + + enum BuildConfigurationFeatures { + kPointerCompression = 1 << 0, + k31BitSmis = 1 << 1, + kSandbox = 1 << 2, + }; + + /** + * Checks that the embedder build configuration is compatible with + * the V8 binary and if so initializes V8. + */ + static bool Initialize(int build_config); + + friend class Context; + template + friend class PersistentValueMapBase; +}; + +} // namespace v8 + +#endif // INCLUDE_V8_INITIALIZATION_H_ diff --git a/deps/include/v8-inspector-protocol.h b/deps/include/v8-inspector-protocol.h new file mode 100755 index 0000000..a5ffb7d --- /dev/null +++ b/deps/include/v8-inspector-protocol.h @@ -0,0 +1,13 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_V8_INSPECTOR_PROTOCOL_H_ +#define V8_V8_INSPECTOR_PROTOCOL_H_ + +#include "inspector/Debugger.h" // NOLINT(build/include_directory) +#include "inspector/Runtime.h" // NOLINT(build/include_directory) +#include "inspector/Schema.h" // NOLINT(build/include_directory) +#include "v8-inspector.h" // NOLINT(build/include_directory) + +#endif // V8_V8_INSPECTOR_PROTOCOL_H_ diff --git a/deps/include/v8-inspector.h b/deps/include/v8-inspector.h new file mode 100755 index 0000000..aa5a044 --- /dev/null +++ b/deps/include/v8-inspector.h @@ -0,0 +1,382 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_V8_INSPECTOR_H_ +#define V8_V8_INSPECTOR_H_ + +#include + +#include +#include + +#include "v8-isolate.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) + +namespace v8 { +class Context; +class Name; +class Object; +class StackTrace; +class Value; +} // namespace v8 + +namespace v8_inspector { + +namespace internal { +class V8DebuggerId; +} // namespace internal + +namespace protocol { +namespace Debugger { +namespace API { +class SearchMatch; +} +} +namespace Runtime { +namespace API { +class RemoteObject; +class StackTrace; +class StackTraceId; +} +} +namespace Schema { +namespace API { +class Domain; +} +} +} // namespace protocol + +class V8_EXPORT StringView { + public: + StringView() : m_is8Bit(true), m_length(0), m_characters8(nullptr) {} + + StringView(const uint8_t* characters, size_t length) + : m_is8Bit(true), m_length(length), m_characters8(characters) {} + + StringView(const uint16_t* characters, size_t length) + : m_is8Bit(false), m_length(length), m_characters16(characters) {} + + bool is8Bit() const { return m_is8Bit; } + size_t length() const { return m_length; } + + // TODO(dgozman): add DCHECK(m_is8Bit) to accessors once platform can be used + // here. + const uint8_t* characters8() const { return m_characters8; } + const uint16_t* characters16() const { return m_characters16; } + + private: + bool m_is8Bit; + size_t m_length; + union { + const uint8_t* m_characters8; + const uint16_t* m_characters16; + }; +}; + +class V8_EXPORT StringBuffer { + public: + virtual ~StringBuffer() = default; + virtual StringView string() const = 0; + // This method copies contents. + static std::unique_ptr create(StringView); +}; + +class V8_EXPORT V8ContextInfo { + public: + V8ContextInfo(v8::Local context, int contextGroupId, + StringView humanReadableName) + : context(context), + contextGroupId(contextGroupId), + humanReadableName(humanReadableName), + hasMemoryOnConsole(false) {} + + v8::Local context; + // Each v8::Context is a part of a group. The group id must be non-zero. + int contextGroupId; + StringView humanReadableName; + StringView origin; + StringView auxData; + bool hasMemoryOnConsole; + + static int executionContextId(v8::Local context); + + // Disallow copying and allocating this one. + enum NotNullTagEnum { NotNullLiteral }; + void* operator new(size_t) = delete; + void* operator new(size_t, NotNullTagEnum, void*) = delete; + void* operator new(size_t, void*) = delete; + V8ContextInfo(const V8ContextInfo&) = delete; + V8ContextInfo& operator=(const V8ContextInfo&) = delete; +}; + +// This debugger id tries to be unique by generating two random +// numbers, which should most likely avoid collisions. +// Debugger id has a 1:1 mapping to context group. It is used to +// attribute stack traces to a particular debugging, when doing any +// cross-debugger operations (e.g. async step in). +// See also Runtime.UniqueDebuggerId in the protocol. +class V8_EXPORT V8DebuggerId { + public: + V8DebuggerId() = default; + V8DebuggerId(const V8DebuggerId&) = default; + V8DebuggerId& operator=(const V8DebuggerId&) = default; + + std::unique_ptr toString() const; + bool isValid() const; + std::pair pair() const; + + private: + friend class internal::V8DebuggerId; + explicit V8DebuggerId(std::pair); + + int64_t m_first = 0; + int64_t m_second = 0; +}; + +class V8_EXPORT V8StackTrace { + public: + virtual StringView firstNonEmptySourceURL() const = 0; + virtual bool isEmpty() const = 0; + virtual StringView topSourceURL() const = 0; + virtual int topLineNumber() const = 0; + virtual int topColumnNumber() const = 0; + virtual int topScriptId() const = 0; + virtual StringView topFunctionName() const = 0; + + virtual ~V8StackTrace() = default; + virtual std::unique_ptr + buildInspectorObject(int maxAsyncDepth) const = 0; + virtual std::unique_ptr toString() const = 0; + + // Safe to pass between threads, drops async chain. + virtual std::unique_ptr clone() = 0; +}; + +class V8_EXPORT V8InspectorSession { + public: + virtual ~V8InspectorSession() = default; + + // Cross-context inspectable values (DOM nodes in different worlds, etc.). + class V8_EXPORT Inspectable { + public: + virtual v8::Local get(v8::Local) = 0; + virtual ~Inspectable() = default; + }; + class V8_EXPORT CommandLineAPIScope { + public: + virtual ~CommandLineAPIScope() = default; + }; + virtual void addInspectedObject(std::unique_ptr) = 0; + + // Dispatching protocol messages. + static bool canDispatchMethod(StringView method); + virtual void dispatchProtocolMessage(StringView message) = 0; + virtual std::vector state() = 0; + virtual std::vector> + supportedDomains() = 0; + + virtual std::unique_ptr + initializeCommandLineAPIScope(int executionContextId) = 0; + + // Debugger actions. + virtual void schedulePauseOnNextStatement(StringView breakReason, + StringView breakDetails) = 0; + virtual void cancelPauseOnNextStatement() = 0; + virtual void breakProgram(StringView breakReason, + StringView breakDetails) = 0; + virtual void setSkipAllPauses(bool) = 0; + virtual void resume(bool setTerminateOnResume = false) = 0; + virtual void stepOver() = 0; + virtual std::vector> + searchInTextByLines(StringView text, StringView query, bool caseSensitive, + bool isRegex) = 0; + + // Remote objects. + virtual std::unique_ptr wrapObject( + v8::Local, v8::Local, StringView groupName, + bool generatePreview) = 0; + + virtual bool unwrapObject(std::unique_ptr* error, + StringView objectId, v8::Local*, + v8::Local*, + std::unique_ptr* objectGroup) = 0; + virtual void releaseObjectGroup(StringView) = 0; + virtual void triggerPreciseCoverageDeltaUpdate(StringView occasion) = 0; +}; + +class V8_EXPORT WebDriverValue { + public: + explicit WebDriverValue(std::unique_ptr type, + v8::MaybeLocal value = {}) + : type(std::move(type)), value(value) {} + std::unique_ptr type; + v8::MaybeLocal value; +}; + +class V8_EXPORT V8InspectorClient { + public: + virtual ~V8InspectorClient() = default; + + virtual void runMessageLoopOnPause(int contextGroupId) {} + virtual void runMessageLoopOnInstrumentationPause(int contextGroupId) { + runMessageLoopOnPause(contextGroupId); + } + virtual void quitMessageLoopOnPause() {} + virtual void runIfWaitingForDebugger(int contextGroupId) {} + + virtual void muteMetrics(int contextGroupId) {} + virtual void unmuteMetrics(int contextGroupId) {} + + virtual void beginUserGesture() {} + virtual void endUserGesture() {} + + virtual std::unique_ptr serializeToWebDriverValue( + v8::Local v8_value, int max_depth) { + return nullptr; + } + virtual std::unique_ptr valueSubtype(v8::Local) { + return nullptr; + } + virtual std::unique_ptr descriptionForValueSubtype( + v8::Local, v8::Local) { + return nullptr; + } + virtual bool isInspectableHeapObject(v8::Local) { return true; } + + virtual v8::Local ensureDefaultContextInGroup( + int contextGroupId) { + return v8::Local(); + } + virtual void beginEnsureAllContextsInGroup(int contextGroupId) {} + virtual void endEnsureAllContextsInGroup(int contextGroupId) {} + + virtual void installAdditionalCommandLineAPI(v8::Local, + v8::Local) {} + virtual void consoleAPIMessage(int contextGroupId, + v8::Isolate::MessageErrorLevel level, + const StringView& message, + const StringView& url, unsigned lineNumber, + unsigned columnNumber, V8StackTrace*) {} + virtual v8::MaybeLocal memoryInfo(v8::Isolate*, + v8::Local) { + return v8::MaybeLocal(); + } + + virtual void consoleTime(const StringView& title) {} + virtual void consoleTimeEnd(const StringView& title) {} + virtual void consoleTimeStamp(const StringView& title) {} + virtual void consoleClear(int contextGroupId) {} + virtual double currentTimeMS() { return 0; } + typedef void (*TimerCallback)(void*); + virtual void startRepeatingTimer(double, TimerCallback, void* data) {} + virtual void cancelTimer(void* data) {} + + // TODO(dgozman): this was added to support service worker shadow page. We + // should not connect at all. + virtual bool canExecuteScripts(int contextGroupId) { return true; } + + virtual void maxAsyncCallStackDepthChanged(int depth) {} + + virtual std::unique_ptr resourceNameToUrl( + const StringView& resourceName) { + return nullptr; + } + + // The caller would defer to generating a random 64 bit integer if + // this method returns 0. + virtual int64_t generateUniqueId() { return 0; } + + virtual void dispatchError(v8::Local, v8::Local, + v8::Local) {} +}; + +// These stack trace ids are intended to be passed between debuggers and be +// resolved later. This allows to track cross-debugger calls and step between +// them if a single client connects to multiple debuggers. +struct V8_EXPORT V8StackTraceId { + uintptr_t id; + std::pair debugger_id; + bool should_pause = false; + + V8StackTraceId(); + V8StackTraceId(const V8StackTraceId&) = default; + V8StackTraceId(uintptr_t id, const std::pair debugger_id); + V8StackTraceId(uintptr_t id, const std::pair debugger_id, + bool should_pause); + explicit V8StackTraceId(StringView); + V8StackTraceId& operator=(const V8StackTraceId&) = default; + V8StackTraceId& operator=(V8StackTraceId&&) noexcept = default; + ~V8StackTraceId() = default; + + bool IsInvalid() const; + std::unique_ptr ToString(); +}; + +class V8_EXPORT V8Inspector { + public: + static std::unique_ptr create(v8::Isolate*, V8InspectorClient*); + virtual ~V8Inspector() = default; + + // Contexts instrumentation. + virtual void contextCreated(const V8ContextInfo&) = 0; + virtual void contextDestroyed(v8::Local) = 0; + virtual void resetContextGroup(int contextGroupId) = 0; + virtual v8::MaybeLocal contextById(int contextId) = 0; + virtual V8DebuggerId uniqueDebuggerId(int contextId) = 0; + + // Various instrumentation. + virtual void idleStarted() = 0; + virtual void idleFinished() = 0; + + // Async stack traces instrumentation. + virtual void asyncTaskScheduled(StringView taskName, void* task, + bool recurring) = 0; + virtual void asyncTaskCanceled(void* task) = 0; + virtual void asyncTaskStarted(void* task) = 0; + virtual void asyncTaskFinished(void* task) = 0; + virtual void allAsyncTasksCanceled() = 0; + + virtual V8StackTraceId storeCurrentStackTrace(StringView description) = 0; + virtual void externalAsyncTaskStarted(const V8StackTraceId& parent) = 0; + virtual void externalAsyncTaskFinished(const V8StackTraceId& parent) = 0; + + // Exceptions instrumentation. + virtual unsigned exceptionThrown(v8::Local, StringView message, + v8::Local exception, + StringView detailedMessage, StringView url, + unsigned lineNumber, unsigned columnNumber, + std::unique_ptr, + int scriptId) = 0; + virtual void exceptionRevoked(v8::Local, unsigned exceptionId, + StringView message) = 0; + virtual bool associateExceptionData(v8::Local, + v8::Local exception, + v8::Local key, + v8::Local value) = 0; + + // Connection. + class V8_EXPORT Channel { + public: + virtual ~Channel() = default; + virtual void sendResponse(int callId, + std::unique_ptr message) = 0; + virtual void sendNotification(std::unique_ptr message) = 0; + virtual void flushProtocolNotifications() = 0; + }; + enum ClientTrustLevel { kUntrusted, kFullyTrusted }; + virtual std::unique_ptr connect( + int contextGroupId, Channel*, StringView state, + ClientTrustLevel client_trust_level) { + return nullptr; + } + + // API methods. + virtual std::unique_ptr createStackTrace( + v8::Local) = 0; + virtual std::unique_ptr captureStackTrace(bool fullStack) = 0; +}; + +} // namespace v8_inspector + +#endif // V8_V8_INSPECTOR_H_ diff --git a/deps/include/v8-internal.h b/deps/include/v8-internal.h new file mode 100755 index 0000000..704e89e --- /dev/null +++ b/deps/include/v8-internal.h @@ -0,0 +1,867 @@ +// Copyright 2018 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_INTERNAL_H_ +#define INCLUDE_V8_INTERNAL_H_ + +#include +#include +#include + +#include +#include + +#include "v8-version.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Array; +class Context; +class Data; +class Isolate; +template +class Local; + +namespace internal { + +class Isolate; + +typedef uintptr_t Address; +static const Address kNullAddress = 0; + +constexpr int KB = 1024; +constexpr int MB = KB * 1024; +constexpr int GB = MB * 1024; +#ifdef V8_TARGET_ARCH_X64 +constexpr size_t TB = size_t{GB} * 1024; +#endif + +/** + * Configuration of tagging scheme. + */ +const int kApiSystemPointerSize = sizeof(void*); +const int kApiDoubleSize = sizeof(double); +const int kApiInt32Size = sizeof(int32_t); +const int kApiInt64Size = sizeof(int64_t); +const int kApiSizetSize = sizeof(size_t); + +// Tag information for HeapObject. +const int kHeapObjectTag = 1; +const int kWeakHeapObjectTag = 3; +const int kHeapObjectTagSize = 2; +const intptr_t kHeapObjectTagMask = (1 << kHeapObjectTagSize) - 1; +const intptr_t kHeapObjectReferenceTagMask = 1 << (kHeapObjectTagSize - 1); + +// Tag information for fowarding pointers stored in object headers. +// 0b00 at the lowest 2 bits in the header indicates that the map word is a +// forwarding pointer. +const int kForwardingTag = 0; +const int kForwardingTagSize = 2; +const intptr_t kForwardingTagMask = (1 << kForwardingTagSize) - 1; + +// Tag information for Smi. +const int kSmiTag = 0; +const int kSmiTagSize = 1; +const intptr_t kSmiTagMask = (1 << kSmiTagSize) - 1; + +template +struct SmiTagging; + +constexpr intptr_t kIntptrAllBitsSet = intptr_t{-1}; +constexpr uintptr_t kUintptrAllBitsSet = + static_cast(kIntptrAllBitsSet); + +// Smi constants for systems where tagged pointer is a 32-bit value. +template <> +struct SmiTagging<4> { + enum { kSmiShiftSize = 0, kSmiValueSize = 31 }; + + static constexpr intptr_t kSmiMinValue = + static_cast(kUintptrAllBitsSet << (kSmiValueSize - 1)); + static constexpr intptr_t kSmiMaxValue = -(kSmiMinValue + 1); + + V8_INLINE static int SmiToInt(const internal::Address value) { + int shift_bits = kSmiTagSize + kSmiShiftSize; + // Truncate and shift down (requires >> to be sign extending). + return static_cast(static_cast(value)) >> shift_bits; + } + V8_INLINE static constexpr bool IsValidSmi(intptr_t value) { + // Is value in range [kSmiMinValue, kSmiMaxValue]. + // Use unsigned operations in order to avoid undefined behaviour in case of + // signed integer overflow. + return (static_cast(value) - + static_cast(kSmiMinValue)) <= + (static_cast(kSmiMaxValue) - + static_cast(kSmiMinValue)); + } +}; + +// Smi constants for systems where tagged pointer is a 64-bit value. +template <> +struct SmiTagging<8> { + enum { kSmiShiftSize = 31, kSmiValueSize = 32 }; + + static constexpr intptr_t kSmiMinValue = + static_cast(kUintptrAllBitsSet << (kSmiValueSize - 1)); + static constexpr intptr_t kSmiMaxValue = -(kSmiMinValue + 1); + + V8_INLINE static int SmiToInt(const internal::Address value) { + int shift_bits = kSmiTagSize + kSmiShiftSize; + // Shift down and throw away top 32 bits. + return static_cast(static_cast(value) >> shift_bits); + } + V8_INLINE static constexpr bool IsValidSmi(intptr_t value) { + // To be representable as a long smi, the value must be a 32-bit integer. + return (value == static_cast(value)); + } +}; + +#ifdef V8_COMPRESS_POINTERS +// See v8:7703 or src/common/ptr-compr-inl.h for details about pointer +// compression. +constexpr size_t kPtrComprCageReservationSize = size_t{1} << 32; +constexpr size_t kPtrComprCageBaseAlignment = size_t{1} << 32; + +static_assert( + kApiSystemPointerSize == kApiInt64Size, + "Pointer compression can be enabled only for 64-bit architectures"); +const int kApiTaggedSize = kApiInt32Size; +#else +const int kApiTaggedSize = kApiSystemPointerSize; +#endif + +constexpr bool PointerCompressionIsEnabled() { + return kApiTaggedSize != kApiSystemPointerSize; +} + +#ifdef V8_31BIT_SMIS_ON_64BIT_ARCH +using PlatformSmiTagging = SmiTagging; +#else +using PlatformSmiTagging = SmiTagging; +#endif + +// TODO(ishell): Consinder adding kSmiShiftBits = kSmiShiftSize + kSmiTagSize +// since it's used much more often than the inividual constants. +const int kSmiShiftSize = PlatformSmiTagging::kSmiShiftSize; +const int kSmiValueSize = PlatformSmiTagging::kSmiValueSize; +const int kSmiMinValue = static_cast(PlatformSmiTagging::kSmiMinValue); +const int kSmiMaxValue = static_cast(PlatformSmiTagging::kSmiMaxValue); +constexpr bool SmiValuesAre31Bits() { return kSmiValueSize == 31; } +constexpr bool SmiValuesAre32Bits() { return kSmiValueSize == 32; } + +V8_INLINE static constexpr internal::Address IntToSmi(int value) { + return (static_cast
(value) << (kSmiTagSize + kSmiShiftSize)) | + kSmiTag; +} + +/* + * Sandbox related types, constants, and functions. + */ +constexpr bool SandboxIsEnabled() { +#ifdef V8_ENABLE_SANDBOX + return true; +#else + return false; +#endif +} + +// SandboxedPointers are guaranteed to point into the sandbox. This is achieved +// for example by storing them as offset rather than as raw pointers. +using SandboxedPointer_t = Address; + +#ifdef V8_ENABLE_SANDBOX + +// Size of the sandbox, excluding the guard regions surrounding it. +#ifdef V8_TARGET_OS_ANDROID +// On Android, most 64-bit devices seem to be configured with only 39 bits of +// virtual address space for userspace. As such, limit the sandbox to 128GB (a +// quarter of the total available address space). +constexpr size_t kSandboxSizeLog2 = 37; // 128 GB +#else +// Everywhere else use a 1TB sandbox. +constexpr size_t kSandboxSizeLog2 = 40; // 1 TB +#endif // V8_TARGET_OS_ANDROID +constexpr size_t kSandboxSize = 1ULL << kSandboxSizeLog2; + +// Required alignment of the sandbox. For simplicity, we require the +// size of the guard regions to be a multiple of this, so that this specifies +// the alignment of the sandbox including and excluding surrounding guard +// regions. The alignment requirement is due to the pointer compression cage +// being located at the start of the sandbox. +constexpr size_t kSandboxAlignment = kPtrComprCageBaseAlignment; + +// Sandboxed pointers are stored inside the heap as offset from the sandbox +// base shifted to the left. This way, it is guaranteed that the offset is +// smaller than the sandbox size after shifting it to the right again. This +// constant specifies the shift amount. +constexpr uint64_t kSandboxedPointerShift = 64 - kSandboxSizeLog2; + +// Size of the guard regions surrounding the sandbox. This assumes a worst-case +// scenario of a 32-bit unsigned index used to access an array of 64-bit +// values. +constexpr size_t kSandboxGuardRegionSize = 32ULL * GB; + +static_assert((kSandboxGuardRegionSize % kSandboxAlignment) == 0, + "The size of the guard regions around the sandbox must be a " + "multiple of its required alignment."); + +// On OSes where reserving virtual memory is too expensive to reserve the +// entire address space backing the sandbox, notably Windows pre 8.1, we create +// a partially reserved sandbox that doesn't actually reserve most of the +// memory, and so doesn't have the desired security properties as unrelated +// memory allocations could end up inside of it, but which still ensures that +// objects that should be located inside the sandbox are allocated within +// kSandboxSize bytes from the start of the sandbox. The minimum size of the +// region that is actually reserved for such a sandbox is specified by this +// constant and should be big enough to contain the pointer compression cage as +// well as the ArrayBuffer partition. +constexpr size_t kSandboxMinimumReservationSize = 8ULL * GB; + +static_assert(kSandboxMinimumReservationSize > kPtrComprCageReservationSize, + "The minimum reservation size for a sandbox must be larger than " + "the pointer compression cage contained within it."); + +// The maximum buffer size allowed inside the sandbox. This is mostly dependent +// on the size of the guard regions around the sandbox: an attacker must not be +// able to construct a buffer that appears larger than the guard regions and +// thereby "reach out of" the sandbox. +constexpr size_t kMaxSafeBufferSizeForSandbox = 32ULL * GB - 1; +static_assert(kMaxSafeBufferSizeForSandbox <= kSandboxGuardRegionSize, + "The maximum allowed buffer size must not be larger than the " + "sandbox's guard regions"); + +constexpr size_t kBoundedSizeShift = 29; +static_assert(1ULL << (64 - kBoundedSizeShift) == + kMaxSafeBufferSizeForSandbox + 1, + "The maximum size of a BoundedSize must be synchronized with the " + "kMaxSafeBufferSizeForSandbox"); + +#endif // V8_ENABLE_SANDBOX + +#ifdef V8_COMPRESS_POINTERS + +// The size of the virtual memory reservation for an external pointer table. +// This determines the maximum number of entries in a table. Using a maximum +// size allows omitting bounds checks on table accesses if the indices are +// guaranteed (e.g. through shifting) to be below the maximum index. This +// value must be a power of two. +static const size_t kExternalPointerTableReservationSize = 512 * MB; + +// The maximum number of entries in an external pointer table. +static const size_t kMaxExternalPointers = + kExternalPointerTableReservationSize / kApiSystemPointerSize; + +// The external pointer table indices stored in HeapObjects as external +// pointers are shifted to the left by this amount to guarantee that they are +// smaller than the maximum table size. +static const uint32_t kExternalPointerIndexShift = 6; +static_assert((1 << (32 - kExternalPointerIndexShift)) == kMaxExternalPointers, + "kExternalPointerTableReservationSize and " + "kExternalPointerIndexShift don't match"); + +#else // !V8_COMPRESS_POINTERS + +// Needed for the V8.SandboxedExternalPointersCount histogram. +static const size_t kMaxExternalPointers = 0; + +#endif // V8_COMPRESS_POINTERS + +// A ExternalPointerHandle represents a (opaque) reference to an external +// pointer that can be stored inside the sandbox. A ExternalPointerHandle has +// meaning only in combination with an (active) Isolate as it references an +// external pointer stored in the currently active Isolate's +// ExternalPointerTable. Internally, an ExternalPointerHandles is simply an +// index into an ExternalPointerTable that is shifted to the left to guarantee +// that it is smaller than the size of the table. +using ExternalPointerHandle = uint32_t; + +// ExternalPointers point to objects located outside the sandbox. When +// sandboxed external pointers are enabled, these are stored on heap as +// ExternalPointerHandles, otherwise they are simply raw pointers. +#ifdef V8_ENABLE_SANDBOX +using ExternalPointer_t = ExternalPointerHandle; +#else +using ExternalPointer_t = Address; +#endif + +// When the sandbox is enabled, external pointers are stored in an external +// pointer table and are referenced from HeapObjects through an index (a +// "handle"). When stored in the table, the pointers are tagged with per-type +// tags to prevent type confusion attacks between different external objects. +// Besides type information bits, these tags also contain the GC marking bit +// which indicates whether the pointer table entry is currently alive. When a +// pointer is written into the table, the tag is ORed into the top bits. When +// that pointer is later loaded from the table, it is ANDed with the inverse of +// the expected tag. If the expected and actual type differ, this will leave +// some of the top bits of the pointer set, rendering the pointer inaccessible. +// The AND operation also removes the GC marking bit from the pointer. +// +// The tags are constructed such that UNTAG(TAG(0, T1), T2) != 0 for any two +// (distinct) tags T1 and T2. In practice, this is achieved by generating tags +// that all have the same number of zeroes and ones but different bit patterns. +// With N type tag bits, this allows for (N choose N/2) possible type tags. +// Besides the type tag bits, the tags also have the GC marking bit set so that +// the marking bit is automatically set when a pointer is written into the +// external pointer table (in which case it is clearly alive) and is cleared +// when the pointer is loaded. The exception to this is the free entry tag, +// which doesn't have the mark bit set, as the entry is not alive. This +// construction allows performing the type check and removing GC marking bits +// from the pointer in one efficient operation (bitwise AND). The number of +// available bits is limited in the following way: on x64, bits [47, 64) are +// generally available for tagging (userspace has 47 address bits available). +// On Arm64, userspace typically has a 40 or 48 bit address space. However, due +// to top-byte ignore (TBI) and memory tagging (MTE), the top byte is unusable +// for type checks as type-check failures would go unnoticed or collide with +// MTE bits. Some bits of the top byte can, however, still be used for the GC +// marking bit. The bits available for the type tags are therefore limited to +// [48, 56), i.e. (8 choose 4) = 70 different types. +// The following options exist to increase the number of possible types: +// - Using multiple ExternalPointerTables since tags can safely be reused +// across different tables +// - Using "extended" type checks, where additional type information is stored +// either in an adjacent pointer table entry or at the pointed-to location +// - Using a different tagging scheme, for example based on XOR which would +// allow for 2**8 different tags but require a separate operation to remove +// the marking bit +// +// The external pointer sandboxing mechanism ensures that every access to an +// external pointer field will result in a valid pointer of the expected type +// even in the presence of an attacker able to corrupt memory inside the +// sandbox. However, if any data related to the external object is stored +// inside the sandbox it may still be corrupted and so must be validated before +// use or moved into the external object. Further, an attacker will always be +// able to substitute different external pointers of the same type for each +// other. Therefore, code using external pointers must be written in a +// "substitution-safe" way, i.e. it must always be possible to substitute +// external pointers of the same type without causing memory corruption outside +// of the sandbox. Generally this is achieved by referencing any group of +// related external objects through a single external pointer. +// +// Currently we use bit 62 for the marking bit which should always be unused as +// it's part of the non-canonical address range. When Arm's top-byte ignore +// (TBI) is enabled, this bit will be part of the ignored byte, and we assume +// that the Embedder is not using this byte (really only this one bit) for any +// other purpose. This bit also does not collide with the memory tagging +// extension (MTE) which would use bits [56, 60). +constexpr uint64_t kExternalPointerMarkBit = 1ULL << 62; +constexpr uint64_t kExternalPointerTagMask = 0x40ff000000000000; +constexpr uint64_t kExternalPointerTagShift = 48; + +// All possible 8-bit type tags. +// These are sorted so that tags can be grouped together and it can efficiently +// be checked if a tag belongs to a given group. See for example the +// IsSharedExternalPointerType routine. +constexpr uint64_t kAllExternalPointerTypeTags[] = { + 0b00001111, 0b00010111, 0b00011011, 0b00011101, 0b00011110, 0b00100111, + 0b00101011, 0b00101101, 0b00101110, 0b00110011, 0b00110101, 0b00110110, + 0b00111001, 0b00111010, 0b00111100, 0b01000111, 0b01001011, 0b01001101, + 0b01001110, 0b01010011, 0b01010101, 0b01010110, 0b01011001, 0b01011010, + 0b01011100, 0b01100011, 0b01100101, 0b01100110, 0b01101001, 0b01101010, + 0b01101100, 0b01110001, 0b01110010, 0b01110100, 0b01111000, 0b10000111, + 0b10001011, 0b10001101, 0b10001110, 0b10010011, 0b10010101, 0b10010110, + 0b10011001, 0b10011010, 0b10011100, 0b10100011, 0b10100101, 0b10100110, + 0b10101001, 0b10101010, 0b10101100, 0b10110001, 0b10110010, 0b10110100, + 0b10111000, 0b11000011, 0b11000101, 0b11000110, 0b11001001, 0b11001010, + 0b11001100, 0b11010001, 0b11010010, 0b11010100, 0b11011000, 0b11100001, + 0b11100010, 0b11100100, 0b11101000, 0b11110000}; + +// clang-format off +// New entries should be added with state "sandboxed". +// When adding new tags, please ensure that the code using these tags is +// "substitution-safe", i.e. still operate safely if external pointers of the +// same type are swapped by an attacker. See comment above for more details. +#define TAG(i) (kAllExternalPointerTypeTags[i]) + +// Shared external pointers are owned by the shared Isolate and stored in the +// shared external pointer table associated with that Isolate, where they can +// be accessed from multiple threads at the same time. The objects referenced +// in this way must therefore always be thread-safe. +#define SHARED_EXTERNAL_POINTER_TAGS(V) \ + V(kFirstSharedTag, sandboxed, TAG(0)) \ + V(kWaiterQueueNodeTag, sandboxed, TAG(0)) \ + V(kExternalStringResourceTag, sandboxed, TAG(1)) \ + V(kExternalStringResourceDataTag, sandboxed, TAG(2)) \ + V(kLastSharedTag, sandboxed, TAG(2)) + +// External pointers using these tags are kept in a per-Isolate external +// pointer table and can only be accessed when this Isolate is active. +#define PER_ISOLATE_EXTERNAL_POINTER_TAGS(V) \ + V(kForeignForeignAddressTag, sandboxed, TAG(10)) \ + V(kNativeContextMicrotaskQueueTag, sandboxed, TAG(11)) \ + V(kEmbedderDataSlotPayloadTag, sandboxed, TAG(12)) \ + V(kExternalObjectValueTag, sandboxed, TAG(13)) \ + V(kCallHandlerInfoCallbackTag, sandboxed, TAG(14)) \ + V(kAccessorInfoGetterTag, sandboxed, TAG(15)) \ + V(kAccessorInfoSetterTag, sandboxed, TAG(16)) \ + V(kWasmInternalFunctionCallTargetTag, sandboxed, TAG(17)) \ + V(kWasmTypeInfoNativeTypeTag, sandboxed, TAG(18)) \ + V(kWasmExportedFunctionDataSignatureTag, sandboxed, TAG(19)) \ + V(kWasmContinuationJmpbufTag, sandboxed, TAG(20)) + +// All external pointer tags. +#define ALL_EXTERNAL_POINTER_TAGS(V) \ + SHARED_EXTERNAL_POINTER_TAGS(V) \ + PER_ISOLATE_EXTERNAL_POINTER_TAGS(V) + +// When the sandbox is enabled, external pointers marked as "sandboxed" above +// use the external pointer table (i.e. are sandboxed). This allows a gradual +// rollout of external pointer sandboxing. If the sandbox is off, no external +// pointers are sandboxed. +// +// Sandboxed external pointer tags are available when compressing pointers even +// when the sandbox is off. Some tags (e.g. kWaiterQueueNodeTag) are used +// manually with the external pointer table even when the sandbox is off to ease +// alignment requirements. +#define sandboxed(X) (X << kExternalPointerTagShift) | kExternalPointerMarkBit +#define unsandboxed(X) kUnsandboxedExternalPointerTag +#if defined(V8_COMPRESS_POINTERS) +#define EXTERNAL_POINTER_TAG_ENUM(Name, State, Bits) Name = State(Bits), +#else +#define EXTERNAL_POINTER_TAG_ENUM(Name, State, Bits) Name = unsandboxed(Bits), +#endif + +#define MAKE_TAG(HasMarkBit, TypeTag) \ + ((static_cast(TypeTag) << kExternalPointerTagShift) | \ + (HasMarkBit ? kExternalPointerMarkBit : 0)) +enum ExternalPointerTag : uint64_t { + // Empty tag value. Mostly used as placeholder. + kExternalPointerNullTag = MAKE_TAG(0, 0b00000000), + // Tag to use for unsandboxed external pointers, which are still stored as + // raw pointers on the heap. + kUnsandboxedExternalPointerTag = MAKE_TAG(0, 0b00000000), + // External pointer tag that will match any external pointer. Use with care! + kAnyExternalPointerTag = MAKE_TAG(1, 0b11111111), + // The free entry tag has all type bits set so every type check with a + // different type fails. It also doesn't have the mark bit set as free + // entries are (by definition) not alive. + kExternalPointerFreeEntryTag = MAKE_TAG(0, 0b11111111), + // Evacuation entries are used during external pointer table compaction. + kExternalPointerEvacuationEntryTag = MAKE_TAG(1, 0b11100111), + + ALL_EXTERNAL_POINTER_TAGS(EXTERNAL_POINTER_TAG_ENUM) +}; + +#undef MAKE_TAG +#undef unsandboxed +#undef sandboxed +#undef TAG +#undef EXTERNAL_POINTER_TAG_ENUM + +// clang-format on + +// True if the external pointer is sandboxed and so must be referenced through +// an external pointer table. +V8_INLINE static constexpr bool IsSandboxedExternalPointerType( + ExternalPointerTag tag) { + return tag != kUnsandboxedExternalPointerTag; +} + +// True if the external pointer must be accessed from the shared isolate's +// external pointer table. +V8_INLINE static constexpr bool IsSharedExternalPointerType( + ExternalPointerTag tag) { + return tag >= kFirstSharedTag && tag <= kLastSharedTag; +} + +// Sanity checks. +#define CHECK_SHARED_EXTERNAL_POINTER_TAGS(Tag, ...) \ + static_assert(!IsSandboxedExternalPointerType(Tag) || \ + IsSharedExternalPointerType(Tag)); +#define CHECK_NON_SHARED_EXTERNAL_POINTER_TAGS(Tag, ...) \ + static_assert(!IsSandboxedExternalPointerType(Tag) || \ + !IsSharedExternalPointerType(Tag)); + +SHARED_EXTERNAL_POINTER_TAGS(CHECK_SHARED_EXTERNAL_POINTER_TAGS) +PER_ISOLATE_EXTERNAL_POINTER_TAGS(CHECK_NON_SHARED_EXTERNAL_POINTER_TAGS) + +#undef CHECK_NON_SHARED_EXTERNAL_POINTER_TAGS +#undef CHECK_SHARED_EXTERNAL_POINTER_TAGS + +#undef SHARED_EXTERNAL_POINTER_TAGS +#undef EXTERNAL_POINTER_TAGS + +// {obj} must be the raw tagged pointer representation of a HeapObject +// that's guaranteed to never be in ReadOnlySpace. +V8_EXPORT internal::Isolate* IsolateFromNeverReadOnlySpaceObject(Address obj); + +// Returns if we need to throw when an error occurs. This infers the language +// mode based on the current context and the closure. This returns true if the +// language mode is strict. +V8_EXPORT bool ShouldThrowOnError(v8::internal::Isolate* isolate); +/** + * This class exports constants and functionality from within v8 that + * is necessary to implement inline functions in the v8 api. Don't + * depend on functions and constants defined here. + */ +class Internals { +#ifdef V8_MAP_PACKING + V8_INLINE static constexpr internal::Address UnpackMapWord( + internal::Address mapword) { + // TODO(wenyuzhao): Clear header metadata. + return mapword ^ kMapWordXorMask; + } +#endif + + public: + // These values match non-compiler-dependent values defined within + // the implementation of v8. + static const int kHeapObjectMapOffset = 0; + static const int kMapInstanceTypeOffset = 1 * kApiTaggedSize + kApiInt32Size; + static const int kStringResourceOffset = + 1 * kApiTaggedSize + 2 * kApiInt32Size; + + static const int kOddballKindOffset = 4 * kApiTaggedSize + kApiDoubleSize; + static const int kJSObjectHeaderSize = 3 * kApiTaggedSize; + static const int kFixedArrayHeaderSize = 2 * kApiTaggedSize; + static const int kEmbedderDataArrayHeaderSize = 2 * kApiTaggedSize; + static const int kEmbedderDataSlotSize = kApiSystemPointerSize; +#ifdef V8_ENABLE_SANDBOX + static const int kEmbedderDataSlotExternalPointerOffset = kApiTaggedSize; +#else + static const int kEmbedderDataSlotExternalPointerOffset = 0; +#endif + static const int kNativeContextEmbedderDataOffset = 6 * kApiTaggedSize; + static const int kStringRepresentationAndEncodingMask = 0x0f; + static const int kStringEncodingMask = 0x8; + static const int kExternalTwoByteRepresentationTag = 0x02; + static const int kExternalOneByteRepresentationTag = 0x0a; + + static const uint32_t kNumIsolateDataSlots = 4; + static const int kStackGuardSize = 7 * kApiSystemPointerSize; + static const int kBuiltinTier0EntryTableSize = 7 * kApiSystemPointerSize; + static const int kBuiltinTier0TableSize = 7 * kApiSystemPointerSize; + + // ExternalPointerTable layout guarantees. + static const int kExternalPointerTableBufferOffset = 0; + static const int kExternalPointerTableSize = 4 * kApiSystemPointerSize; + + // IsolateData layout guarantees. + static const int kIsolateCageBaseOffset = 0; + static const int kIsolateStackGuardOffset = + kIsolateCageBaseOffset + kApiSystemPointerSize; + static const int kVariousBooleanFlagsOffset = + kIsolateStackGuardOffset + kStackGuardSize; + static const int kBuiltinTier0EntryTableOffset = + kVariousBooleanFlagsOffset + kApiSystemPointerSize; + static const int kBuiltinTier0TableOffset = + kBuiltinTier0EntryTableOffset + kBuiltinTier0EntryTableSize; + static const int kIsolateEmbedderDataOffset = + kBuiltinTier0TableOffset + kBuiltinTier0TableSize; + static const int kIsolateFastCCallCallerFpOffset = + kIsolateEmbedderDataOffset + kNumIsolateDataSlots * kApiSystemPointerSize; + static const int kIsolateFastCCallCallerPcOffset = + kIsolateFastCCallCallerFpOffset + kApiSystemPointerSize; + static const int kIsolateFastApiCallTargetOffset = + kIsolateFastCCallCallerPcOffset + kApiSystemPointerSize; + static const int kIsolateLongTaskStatsCounterOffset = + kIsolateFastApiCallTargetOffset + kApiSystemPointerSize; +#ifdef V8_COMPRESS_POINTERS + static const int kIsolateExternalPointerTableOffset = + kIsolateLongTaskStatsCounterOffset + kApiSizetSize; + static const int kIsolateSharedExternalPointerTableAddressOffset = + kIsolateExternalPointerTableOffset + kExternalPointerTableSize; + static const int kIsolateRootsOffset = + kIsolateSharedExternalPointerTableAddressOffset + kApiSystemPointerSize; +#else + static const int kIsolateRootsOffset = + kIsolateLongTaskStatsCounterOffset + kApiSizetSize; +#endif + + static const int kUndefinedValueRootIndex = 4; + static const int kTheHoleValueRootIndex = 5; + static const int kNullValueRootIndex = 6; + static const int kTrueValueRootIndex = 7; + static const int kFalseValueRootIndex = 8; + static const int kEmptyStringRootIndex = 9; + + static const int kNodeClassIdOffset = 1 * kApiSystemPointerSize; + static const int kNodeFlagsOffset = 1 * kApiSystemPointerSize + 3; + static const int kNodeStateMask = 0x3; + static const int kNodeStateIsWeakValue = 2; + + static const int kTracedNodeClassIdOffset = kApiSystemPointerSize; + + static const int kFirstNonstringType = 0x80; + static const int kOddballType = 0x83; + static const int kForeignType = 0xcc; + static const int kJSSpecialApiObjectType = 0x410; + static const int kJSObjectType = 0x421; + static const int kFirstJSApiObjectType = 0x422; + static const int kLastJSApiObjectType = 0x80A; + + static const int kUndefinedOddballKind = 5; + static const int kNullOddballKind = 3; + + // Constants used by PropertyCallbackInfo to check if we should throw when an + // error occurs. + static const int kThrowOnError = 0; + static const int kDontThrow = 1; + static const int kInferShouldThrowMode = 2; + + // Soft limit for AdjustAmountofExternalAllocatedMemory. Trigger an + // incremental GC once the external memory reaches this limit. + static constexpr int kExternalAllocationSoftLimit = 64 * 1024 * 1024; + +#ifdef V8_MAP_PACKING + static const uintptr_t kMapWordMetadataMask = 0xffffULL << 48; + // The lowest two bits of mapwords are always `0b10` + static const uintptr_t kMapWordSignature = 0b10; + // XORing a (non-compressed) map with this mask ensures that the two + // low-order bits are 0b10. The 0 at the end makes this look like a Smi, + // although real Smis have all lower 32 bits unset. We only rely on these + // values passing as Smis in very few places. + static const int kMapWordXorMask = 0b11; +#endif + + V8_EXPORT static void CheckInitializedImpl(v8::Isolate* isolate); + V8_INLINE static void CheckInitialized(v8::Isolate* isolate) { +#ifdef V8_ENABLE_CHECKS + CheckInitializedImpl(isolate); +#endif + } + + V8_INLINE static bool HasHeapObjectTag(const internal::Address value) { + return (value & kHeapObjectTagMask) == static_cast
(kHeapObjectTag); + } + + V8_INLINE static int SmiValue(const internal::Address value) { + return PlatformSmiTagging::SmiToInt(value); + } + + V8_INLINE static constexpr internal::Address IntToSmi(int value) { + return internal::IntToSmi(value); + } + + V8_INLINE static constexpr bool IsValidSmi(intptr_t value) { + return PlatformSmiTagging::IsValidSmi(value); + } + + V8_INLINE static int GetInstanceType(const internal::Address obj) { + typedef internal::Address A; + A map = ReadTaggedPointerField(obj, kHeapObjectMapOffset); +#ifdef V8_MAP_PACKING + map = UnpackMapWord(map); +#endif + return ReadRawField(map, kMapInstanceTypeOffset); + } + + V8_INLINE static int GetOddballKind(const internal::Address obj) { + return SmiValue(ReadTaggedSignedField(obj, kOddballKindOffset)); + } + + V8_INLINE static bool IsExternalTwoByteString(int instance_type) { + int representation = (instance_type & kStringRepresentationAndEncodingMask); + return representation == kExternalTwoByteRepresentationTag; + } + + V8_INLINE static constexpr bool CanHaveInternalField(int instance_type) { + static_assert(kJSObjectType + 1 == kFirstJSApiObjectType); + static_assert(kJSObjectType < kLastJSApiObjectType); + static_assert(kFirstJSApiObjectType < kLastJSApiObjectType); + // Check for IsJSObject() || IsJSSpecialApiObject() || IsJSApiObject() + return instance_type == kJSSpecialApiObjectType || + // inlined version of base::IsInRange + (static_cast(static_cast(instance_type) - + static_cast(kJSObjectType)) <= + static_cast(kLastJSApiObjectType - kJSObjectType)); + } + + V8_INLINE static uint8_t GetNodeFlag(internal::Address* obj, int shift) { + uint8_t* addr = reinterpret_cast(obj) + kNodeFlagsOffset; + return *addr & static_cast(1U << shift); + } + + V8_INLINE static void UpdateNodeFlag(internal::Address* obj, bool value, + int shift) { + uint8_t* addr = reinterpret_cast(obj) + kNodeFlagsOffset; + uint8_t mask = static_cast(1U << shift); + *addr = static_cast((*addr & ~mask) | (value << shift)); + } + + V8_INLINE static uint8_t GetNodeState(internal::Address* obj) { + uint8_t* addr = reinterpret_cast(obj) + kNodeFlagsOffset; + return *addr & kNodeStateMask; + } + + V8_INLINE static void UpdateNodeState(internal::Address* obj, uint8_t value) { + uint8_t* addr = reinterpret_cast(obj) + kNodeFlagsOffset; + *addr = static_cast((*addr & ~kNodeStateMask) | value); + } + + V8_INLINE static void SetEmbedderData(v8::Isolate* isolate, uint32_t slot, + void* data) { + internal::Address addr = reinterpret_cast(isolate) + + kIsolateEmbedderDataOffset + + slot * kApiSystemPointerSize; + *reinterpret_cast(addr) = data; + } + + V8_INLINE static void* GetEmbedderData(const v8::Isolate* isolate, + uint32_t slot) { + internal::Address addr = reinterpret_cast(isolate) + + kIsolateEmbedderDataOffset + + slot * kApiSystemPointerSize; + return *reinterpret_cast(addr); + } + + V8_INLINE static void IncrementLongTasksStatsCounter(v8::Isolate* isolate) { + internal::Address addr = reinterpret_cast(isolate) + + kIsolateLongTaskStatsCounterOffset; + ++(*reinterpret_cast(addr)); + } + + V8_INLINE static internal::Address* GetRoot(v8::Isolate* isolate, int index) { + internal::Address addr = reinterpret_cast(isolate) + + kIsolateRootsOffset + + index * kApiSystemPointerSize; + return reinterpret_cast(addr); + } + +#ifdef V8_ENABLE_SANDBOX + V8_INLINE static internal::Address* GetExternalPointerTableBase( + v8::Isolate* isolate) { + internal::Address addr = reinterpret_cast(isolate) + + kIsolateExternalPointerTableOffset + + kExternalPointerTableBufferOffset; + return *reinterpret_cast(addr); + } + + V8_INLINE static internal::Address* GetSharedExternalPointerTableBase( + v8::Isolate* isolate) { + internal::Address addr = reinterpret_cast(isolate) + + kIsolateSharedExternalPointerTableAddressOffset; + addr = *reinterpret_cast(addr); + addr += kExternalPointerTableBufferOffset; + return *reinterpret_cast(addr); + } +#endif + + template + V8_INLINE static T ReadRawField(internal::Address heap_object_ptr, + int offset) { + internal::Address addr = heap_object_ptr + offset - kHeapObjectTag; +#ifdef V8_COMPRESS_POINTERS + if (sizeof(T) > kApiTaggedSize) { + // TODO(ishell, v8:8875): When pointer compression is enabled 8-byte size + // fields (external pointers, doubles and BigInt data) are only + // kTaggedSize aligned so we have to use unaligned pointer friendly way of + // accessing them in order to avoid undefined behavior in C++ code. + T r; + memcpy(&r, reinterpret_cast(addr), sizeof(T)); + return r; + } +#endif + return *reinterpret_cast(addr); + } + + V8_INLINE static internal::Address ReadTaggedPointerField( + internal::Address heap_object_ptr, int offset) { +#ifdef V8_COMPRESS_POINTERS + uint32_t value = ReadRawField(heap_object_ptr, offset); + internal::Address base = + GetPtrComprCageBaseFromOnHeapAddress(heap_object_ptr); + return base + static_cast(static_cast(value)); +#else + return ReadRawField(heap_object_ptr, offset); +#endif + } + + V8_INLINE static internal::Address ReadTaggedSignedField( + internal::Address heap_object_ptr, int offset) { +#ifdef V8_COMPRESS_POINTERS + uint32_t value = ReadRawField(heap_object_ptr, offset); + return static_cast(static_cast(value)); +#else + return ReadRawField(heap_object_ptr, offset); +#endif + } + + V8_INLINE static v8::Isolate* GetIsolateForSandbox(internal::Address obj) { +#ifdef V8_ENABLE_SANDBOX + return reinterpret_cast( + internal::IsolateFromNeverReadOnlySpaceObject(obj)); +#else + // Not used in non-sandbox mode. + return nullptr; +#endif + } + + template + V8_INLINE static internal::Address ReadExternalPointerField( + v8::Isolate* isolate, internal::Address heap_object_ptr, int offset) { +#ifdef V8_ENABLE_SANDBOX + if (IsSandboxedExternalPointerType(tag)) { + // See src/sandbox/external-pointer-table-inl.h. Logic duplicated here so + // it can be inlined and doesn't require an additional call. + internal::Address* table = + IsSharedExternalPointerType(tag) + ? GetSharedExternalPointerTableBase(isolate) + : GetExternalPointerTableBase(isolate); + internal::ExternalPointerHandle handle = + ReadRawField(heap_object_ptr, offset); + uint32_t index = handle >> kExternalPointerIndexShift; + std::atomic* ptr = + reinterpret_cast*>(&table[index]); + internal::Address entry = + std::atomic_load_explicit(ptr, std::memory_order_relaxed); + return entry & ~tag; + } +#endif + return ReadRawField
(heap_object_ptr, offset); + } + +#ifdef V8_COMPRESS_POINTERS + V8_INLINE static internal::Address GetPtrComprCageBaseFromOnHeapAddress( + internal::Address addr) { + return addr & -static_cast(kPtrComprCageBaseAlignment); + } + + V8_INLINE static internal::Address DecompressTaggedAnyField( + internal::Address heap_object_ptr, uint32_t value) { + internal::Address base = + GetPtrComprCageBaseFromOnHeapAddress(heap_object_ptr); + return base + static_cast(static_cast(value)); + } + +#endif // V8_COMPRESS_POINTERS +}; + +// Only perform cast check for types derived from v8::Data since +// other types do not implement the Cast method. +template +struct CastCheck { + template + static void Perform(T* data); +}; + +template <> +template +void CastCheck::Perform(T* data) { + T::Cast(data); +} + +template <> +template +void CastCheck::Perform(T* data) {} + +template +V8_INLINE void PerformCastCheck(T* data) { + CastCheck::value && + !std::is_same>::value>::Perform(data); +} + +// A base class for backing stores, which is needed due to vagaries of +// how static casts work with std::shared_ptr. +class BackingStoreBase {}; + +// The maximum value in enum GarbageCollectionReason, defined in heap.h. +// This is needed for histograms sampling garbage collection reasons. +constexpr int kGarbageCollectionReasonMaxValue = 27; + +} // namespace internal + +} // namespace v8 + +#endif // INCLUDE_V8_INTERNAL_H_ diff --git a/deps/include/v8-isolate.h b/deps/include/v8-isolate.h new file mode 100755 index 0000000..e9f5319 --- /dev/null +++ b/deps/include/v8-isolate.h @@ -0,0 +1,1694 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_ISOLATE_H_ +#define INCLUDE_V8_ISOLATE_H_ + +#include +#include + +#include +#include + +#include "cppgc/common.h" +#include "v8-array-buffer.h" // NOLINT(build/include_directory) +#include "v8-callbacks.h" // NOLINT(build/include_directory) +#include "v8-data.h" // NOLINT(build/include_directory) +#include "v8-debug.h" // NOLINT(build/include_directory) +#include "v8-embedder-heap.h" // NOLINT(build/include_directory) +#include "v8-function-callback.h" // NOLINT(build/include_directory) +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-microtask.h" // NOLINT(build/include_directory) +#include "v8-persistent-handle.h" // NOLINT(build/include_directory) +#include "v8-primitive.h" // NOLINT(build/include_directory) +#include "v8-statistics.h" // NOLINT(build/include_directory) +#include "v8-unwinder.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class CppHeap; +class HeapProfiler; +class MicrotaskQueue; +class StartupData; +class ScriptOrModule; +class SharedArrayBuffer; + +namespace internal { +class MicrotaskQueue; +class ThreadLocalTop; +} // namespace internal + +namespace metrics { +class Recorder; +} // namespace metrics + +/** + * A set of constraints that specifies the limits of the runtime's memory use. + * You must set the heap size before initializing the VM - the size cannot be + * adjusted after the VM is initialized. + * + * If you are using threads then you should hold the V8::Locker lock while + * setting the stack limit and you must set a non-default stack limit separately + * for each thread. + * + * The arguments for set_max_semi_space_size, set_max_old_space_size, + * set_max_executable_size, set_code_range_size specify limits in MB. + * + * The argument for set_max_semi_space_size_in_kb is in KB. + */ +class V8_EXPORT ResourceConstraints { + public: + /** + * Configures the constraints with reasonable default values based on the + * provided heap size limit. The heap size includes both the young and + * the old generation. + * + * \param initial_heap_size_in_bytes The initial heap size or zero. + * By default V8 starts with a small heap and dynamically grows it to + * match the set of live objects. This may lead to ineffective + * garbage collections at startup if the live set is large. + * Setting the initial heap size avoids such garbage collections. + * Note that this does not affect young generation garbage collections. + * + * \param maximum_heap_size_in_bytes The hard limit for the heap size. + * When the heap size approaches this limit, V8 will perform series of + * garbage collections and invoke the NearHeapLimitCallback. If the garbage + * collections do not help and the callback does not increase the limit, + * then V8 will crash with V8::FatalProcessOutOfMemory. + */ + void ConfigureDefaultsFromHeapSize(size_t initial_heap_size_in_bytes, + size_t maximum_heap_size_in_bytes); + + /** + * Configures the constraints with reasonable default values based on the + * capabilities of the current device the VM is running on. + * + * \param physical_memory The total amount of physical memory on the current + * device, in bytes. + * \param virtual_memory_limit The amount of virtual memory on the current + * device, in bytes, or zero, if there is no limit. + */ + void ConfigureDefaults(uint64_t physical_memory, + uint64_t virtual_memory_limit); + + /** + * The address beyond which the VM's stack may not grow. + */ + uint32_t* stack_limit() const { return stack_limit_; } + void set_stack_limit(uint32_t* value) { stack_limit_ = value; } + + /** + * The amount of virtual memory reserved for generated code. This is relevant + * for 64-bit architectures that rely on code range for calls in code. + * + * When V8_COMPRESS_POINTERS_IN_SHARED_CAGE is defined, there is a shared + * process-wide code range that is lazily initialized. This value is used to + * configure that shared code range when the first Isolate is + * created. Subsequent Isolates ignore this value. + */ + size_t code_range_size_in_bytes() const { return code_range_size_; } + void set_code_range_size_in_bytes(size_t limit) { code_range_size_ = limit; } + + /** + * The maximum size of the old generation. + * When the old generation approaches this limit, V8 will perform series of + * garbage collections and invoke the NearHeapLimitCallback. + * If the garbage collections do not help and the callback does not + * increase the limit, then V8 will crash with V8::FatalProcessOutOfMemory. + */ + size_t max_old_generation_size_in_bytes() const { + return max_old_generation_size_; + } + void set_max_old_generation_size_in_bytes(size_t limit) { + max_old_generation_size_ = limit; + } + + /** + * The maximum size of the young generation, which consists of two semi-spaces + * and a large object space. This affects frequency of Scavenge garbage + * collections and should be typically much smaller that the old generation. + */ + size_t max_young_generation_size_in_bytes() const { + return max_young_generation_size_; + } + void set_max_young_generation_size_in_bytes(size_t limit) { + max_young_generation_size_ = limit; + } + + size_t initial_old_generation_size_in_bytes() const { + return initial_old_generation_size_; + } + void set_initial_old_generation_size_in_bytes(size_t initial_size) { + initial_old_generation_size_ = initial_size; + } + + size_t initial_young_generation_size_in_bytes() const { + return initial_young_generation_size_; + } + void set_initial_young_generation_size_in_bytes(size_t initial_size) { + initial_young_generation_size_ = initial_size; + } + + private: + static constexpr size_t kMB = 1048576u; + size_t code_range_size_ = 0; + size_t max_old_generation_size_ = 0; + size_t max_young_generation_size_ = 0; + size_t initial_old_generation_size_ = 0; + size_t initial_young_generation_size_ = 0; + uint32_t* stack_limit_ = nullptr; +}; + +/** + * Option flags passed to the SetRAILMode function. + * See documentation https://developers.google.com/web/tools/chrome-devtools/ + * profile/evaluate-performance/rail + */ +enum RAILMode : unsigned { + // Response performance mode: In this mode very low virtual machine latency + // is provided. V8 will try to avoid JavaScript execution interruptions. + // Throughput may be throttled. + PERFORMANCE_RESPONSE, + // Animation performance mode: In this mode low virtual machine latency is + // provided. V8 will try to avoid as many JavaScript execution interruptions + // as possible. Throughput may be throttled. This is the default mode. + PERFORMANCE_ANIMATION, + // Idle performance mode: The embedder is idle. V8 can complete deferred work + // in this mode. + PERFORMANCE_IDLE, + // Load performance mode: In this mode high throughput is provided. V8 may + // turn off latency optimizations. + PERFORMANCE_LOAD +}; + +/** + * Memory pressure level for the MemoryPressureNotification. + * kNone hints V8 that there is no memory pressure. + * kModerate hints V8 to speed up incremental garbage collection at the cost of + * of higher latency due to garbage collection pauses. + * kCritical hints V8 to free memory as soon as possible. Garbage collection + * pauses at this level will be large. + */ +enum class MemoryPressureLevel { kNone, kModerate, kCritical }; + +/** + * Indicator for the stack state. + */ +using StackState = cppgc::EmbedderStackState; + +/** + * Isolate represents an isolated instance of the V8 engine. V8 isolates have + * completely separate states. Objects from one isolate must not be used in + * other isolates. The embedder can create multiple isolates and use them in + * parallel in multiple threads. An isolate can be entered by at most one + * thread at any given time. The Locker/Unlocker API must be used to + * synchronize. + */ +class V8_EXPORT Isolate { + public: + /** + * Initial configuration parameters for a new Isolate. + */ + struct V8_EXPORT CreateParams { + CreateParams(); + ~CreateParams(); + + ALLOW_COPY_AND_MOVE_WITH_DEPRECATED_FIELDS(CreateParams) + + /** + * Allows the host application to provide the address of a function that is + * notified each time code is added, moved or removed. + */ + JitCodeEventHandler code_event_handler = nullptr; + + /** + * ResourceConstraints to use for the new Isolate. + */ + ResourceConstraints constraints; + + /** + * Explicitly specify a startup snapshot blob. The embedder owns the blob. + * The embedder *must* ensure that the snapshot is from a trusted source. + */ + StartupData* snapshot_blob = nullptr; + + /** + * Enables the host application to provide a mechanism for recording + * statistics counters. + */ + CounterLookupCallback counter_lookup_callback = nullptr; + + /** + * Enables the host application to provide a mechanism for recording + * histograms. The CreateHistogram function returns a + * histogram which will later be passed to the AddHistogramSample + * function. + */ + CreateHistogramCallback create_histogram_callback = nullptr; + AddHistogramSampleCallback add_histogram_sample_callback = nullptr; + + /** + * The ArrayBuffer::Allocator to use for allocating and freeing the backing + * store of ArrayBuffers. + * + * If the shared_ptr version is used, the Isolate instance and every + * |BackingStore| allocated using this allocator hold a std::shared_ptr + * to the allocator, in order to facilitate lifetime + * management for the allocator instance. + */ + ArrayBuffer::Allocator* array_buffer_allocator = nullptr; + std::shared_ptr array_buffer_allocator_shared; + + /** + * Specifies an optional nullptr-terminated array of raw addresses in the + * embedder that V8 can match against during serialization and use for + * deserialization. This array and its content must stay valid for the + * entire lifetime of the isolate. + */ + const intptr_t* external_references = nullptr; + + /** + * Whether calling Atomics.wait (a function that may block) is allowed in + * this isolate. This can also be configured via SetAllowAtomicsWait. + */ + bool allow_atomics_wait = true; + + /** + * Termination is postponed when there is no active SafeForTerminationScope. + */ + bool only_terminate_in_safe_scope = false; + + /** + * The following parameters describe the offsets for addressing type info + * for wrapped API objects and are used by the fast C API + * (for details see v8-fast-api-calls.h). + */ + int embedder_wrapper_type_index = -1; + int embedder_wrapper_object_index = -1; + + /** + * Callbacks to invoke in case of fatal or OOM errors. + */ + FatalErrorCallback fatal_error_callback = nullptr; + OOMErrorCallback oom_error_callback = nullptr; + }; + + /** + * Stack-allocated class which sets the isolate for all operations + * executed within a local scope. + */ + class V8_EXPORT V8_NODISCARD Scope { + public: + explicit Scope(Isolate* isolate) : v8_isolate_(isolate) { + v8_isolate_->Enter(); + } + + ~Scope() { v8_isolate_->Exit(); } + + // Prevent copying of Scope objects. + Scope(const Scope&) = delete; + Scope& operator=(const Scope&) = delete; + + private: + Isolate* const v8_isolate_; + }; + + /** + * Assert that no Javascript code is invoked. + */ + class V8_EXPORT V8_NODISCARD DisallowJavascriptExecutionScope { + public: + enum OnFailure { CRASH_ON_FAILURE, THROW_ON_FAILURE, DUMP_ON_FAILURE }; + + DisallowJavascriptExecutionScope(Isolate* isolate, OnFailure on_failure); + ~DisallowJavascriptExecutionScope(); + + // Prevent copying of Scope objects. + DisallowJavascriptExecutionScope(const DisallowJavascriptExecutionScope&) = + delete; + DisallowJavascriptExecutionScope& operator=( + const DisallowJavascriptExecutionScope&) = delete; + + private: + OnFailure on_failure_; + v8::Isolate* v8_isolate_; + + bool was_execution_allowed_assert_; + bool was_execution_allowed_throws_; + bool was_execution_allowed_dump_; + }; + + /** + * Introduce exception to DisallowJavascriptExecutionScope. + */ + class V8_EXPORT V8_NODISCARD AllowJavascriptExecutionScope { + public: + explicit AllowJavascriptExecutionScope(Isolate* isolate); + ~AllowJavascriptExecutionScope(); + + // Prevent copying of Scope objects. + AllowJavascriptExecutionScope(const AllowJavascriptExecutionScope&) = + delete; + AllowJavascriptExecutionScope& operator=( + const AllowJavascriptExecutionScope&) = delete; + + private: + Isolate* v8_isolate_; + bool was_execution_allowed_assert_; + bool was_execution_allowed_throws_; + bool was_execution_allowed_dump_; + }; + + /** + * Do not run microtasks while this scope is active, even if microtasks are + * automatically executed otherwise. + */ + class V8_EXPORT V8_NODISCARD SuppressMicrotaskExecutionScope { + public: + explicit SuppressMicrotaskExecutionScope( + Isolate* isolate, MicrotaskQueue* microtask_queue = nullptr); + ~SuppressMicrotaskExecutionScope(); + + // Prevent copying of Scope objects. + SuppressMicrotaskExecutionScope(const SuppressMicrotaskExecutionScope&) = + delete; + SuppressMicrotaskExecutionScope& operator=( + const SuppressMicrotaskExecutionScope&) = delete; + + private: + internal::Isolate* const i_isolate_; + internal::MicrotaskQueue* const microtask_queue_; + internal::Address previous_stack_height_; + + friend class internal::ThreadLocalTop; + }; + + /** + * This scope allows terminations inside direct V8 API calls and forbid them + * inside any recursive API calls without explicit SafeForTerminationScope. + */ + class V8_EXPORT V8_NODISCARD SafeForTerminationScope { + public: + explicit SafeForTerminationScope(v8::Isolate* v8_isolate); + ~SafeForTerminationScope(); + + // Prevent copying of Scope objects. + SafeForTerminationScope(const SafeForTerminationScope&) = delete; + SafeForTerminationScope& operator=(const SafeForTerminationScope&) = delete; + + private: + internal::Isolate* i_isolate_; + bool prev_value_; + }; + + /** + * Types of garbage collections that can be requested via + * RequestGarbageCollectionForTesting. + */ + enum GarbageCollectionType { + kFullGarbageCollection, + kMinorGarbageCollection + }; + + /** + * Features reported via the SetUseCounterCallback callback. Do not change + * assigned numbers of existing items; add new features to the end of this + * list. + */ + enum UseCounterFeature { + kUseAsm = 0, + kBreakIterator = 1, + kLegacyConst = 2, + kMarkDequeOverflow = 3, + kStoreBufferOverflow = 4, + kSlotsBufferOverflow = 5, + kObjectObserve = 6, + kForcedGC = 7, + kSloppyMode = 8, + kStrictMode = 9, + kStrongMode = 10, + kRegExpPrototypeStickyGetter = 11, + kRegExpPrototypeToString = 12, + kRegExpPrototypeUnicodeGetter = 13, + kIntlV8Parse = 14, + kIntlPattern = 15, + kIntlResolved = 16, + kPromiseChain = 17, + kPromiseAccept = 18, + kPromiseDefer = 19, + kHtmlCommentInExternalScript = 20, + kHtmlComment = 21, + kSloppyModeBlockScopedFunctionRedefinition = 22, + kForInInitializer = 23, + kArrayProtectorDirtied = 24, + kArraySpeciesModified = 25, + kArrayPrototypeConstructorModified = 26, + kArrayInstanceProtoModified = 27, + kArrayInstanceConstructorModified = 28, + kLegacyFunctionDeclaration = 29, + kRegExpPrototypeSourceGetter = 30, // Unused. + kRegExpPrototypeOldFlagGetter = 31, // Unused. + kDecimalWithLeadingZeroInStrictMode = 32, + kLegacyDateParser = 33, + kDefineGetterOrSetterWouldThrow = 34, + kFunctionConstructorReturnedUndefined = 35, + kAssigmentExpressionLHSIsCallInSloppy = 36, + kAssigmentExpressionLHSIsCallInStrict = 37, + kPromiseConstructorReturnedUndefined = 38, + kConstructorNonUndefinedPrimitiveReturn = 39, + kLabeledExpressionStatement = 40, + kLineOrParagraphSeparatorAsLineTerminator = 41, + kIndexAccessor = 42, + kErrorCaptureStackTrace = 43, + kErrorPrepareStackTrace = 44, + kErrorStackTraceLimit = 45, + kWebAssemblyInstantiation = 46, + kDeoptimizerDisableSpeculation = 47, + kArrayPrototypeSortJSArrayModifiedPrototype = 48, + kFunctionTokenOffsetTooLongForToString = 49, + kWasmSharedMemory = 50, + kWasmThreadOpcodes = 51, + kAtomicsNotify = 52, // Unused. + kAtomicsWake = 53, // Unused. + kCollator = 54, + kNumberFormat = 55, + kDateTimeFormat = 56, + kPluralRules = 57, + kRelativeTimeFormat = 58, + kLocale = 59, + kListFormat = 60, + kSegmenter = 61, + kStringLocaleCompare = 62, + kStringToLocaleUpperCase = 63, + kStringToLocaleLowerCase = 64, + kNumberToLocaleString = 65, + kDateToLocaleString = 66, + kDateToLocaleDateString = 67, + kDateToLocaleTimeString = 68, + kAttemptOverrideReadOnlyOnPrototypeSloppy = 69, + kAttemptOverrideReadOnlyOnPrototypeStrict = 70, + kOptimizedFunctionWithOneShotBytecode = 71, // Unused. + kRegExpMatchIsTrueishOnNonJSRegExp = 72, + kRegExpMatchIsFalseishOnJSRegExp = 73, + kDateGetTimezoneOffset = 74, // Unused. + kStringNormalize = 75, + kCallSiteAPIGetFunctionSloppyCall = 76, + kCallSiteAPIGetThisSloppyCall = 77, + kRegExpMatchAllWithNonGlobalRegExp = 78, + kRegExpExecCalledOnSlowRegExp = 79, + kRegExpReplaceCalledOnSlowRegExp = 80, + kDisplayNames = 81, + kSharedArrayBufferConstructed = 82, + kArrayPrototypeHasElements = 83, + kObjectPrototypeHasElements = 84, + kNumberFormatStyleUnit = 85, + kDateTimeFormatRange = 86, + kDateTimeFormatDateTimeStyle = 87, + kBreakIteratorTypeWord = 88, + kBreakIteratorTypeLine = 89, + kInvalidatedArrayBufferDetachingProtector = 90, + kInvalidatedArrayConstructorProtector = 91, + kInvalidatedArrayIteratorLookupChainProtector = 92, + kInvalidatedArraySpeciesLookupChainProtector = 93, + kInvalidatedIsConcatSpreadableLookupChainProtector = 94, + kInvalidatedMapIteratorLookupChainProtector = 95, + kInvalidatedNoElementsProtector = 96, + kInvalidatedPromiseHookProtector = 97, + kInvalidatedPromiseResolveLookupChainProtector = 98, + kInvalidatedPromiseSpeciesLookupChainProtector = 99, + kInvalidatedPromiseThenLookupChainProtector = 100, + kInvalidatedRegExpSpeciesLookupChainProtector = 101, + kInvalidatedSetIteratorLookupChainProtector = 102, + kInvalidatedStringIteratorLookupChainProtector = 103, + kInvalidatedStringLengthOverflowLookupChainProtector = 104, + kInvalidatedTypedArraySpeciesLookupChainProtector = 105, + kWasmSimdOpcodes = 106, + kVarRedeclaredCatchBinding = 107, + kWasmRefTypes = 108, + kWasmBulkMemory = 109, // Unused. + kWasmMultiValue = 110, + kWasmExceptionHandling = 111, + kInvalidatedMegaDOMProtector = 112, + kFunctionPrototypeArguments = 113, + kFunctionPrototypeCaller = 114, + kTurboFanOsrCompileStarted = 115, + kAsyncStackTaggingCreateTaskCall = 116, + kDurationFormat = 117, + + // If you add new values here, you'll also need to update Chromium's: + // web_feature.mojom, use_counter_callback.cc, and enums.xml. V8 changes to + // this list need to be landed first, then changes on the Chromium side. + kUseCounterFeatureCount // This enum value must be last. + }; + + enum MessageErrorLevel { + kMessageLog = (1 << 0), + kMessageDebug = (1 << 1), + kMessageInfo = (1 << 2), + kMessageError = (1 << 3), + kMessageWarning = (1 << 4), + kMessageAll = kMessageLog | kMessageDebug | kMessageInfo | kMessageError | + kMessageWarning, + }; + + using UseCounterCallback = void (*)(Isolate* isolate, + UseCounterFeature feature); + + /** + * Allocates a new isolate but does not initialize it. Does not change the + * currently entered isolate. + * + * Only Isolate::GetData() and Isolate::SetData(), which access the + * embedder-controlled parts of the isolate, are allowed to be called on the + * uninitialized isolate. To initialize the isolate, call + * Isolate::Initialize(). + * + * When an isolate is no longer used its resources should be freed + * by calling Dispose(). Using the delete operator is not allowed. + * + * V8::Initialize() must have run prior to this. + */ + static Isolate* Allocate(); + + /** + * Initialize an Isolate previously allocated by Isolate::Allocate(). + */ + static void Initialize(Isolate* isolate, const CreateParams& params); + + /** + * Creates a new isolate. Does not change the currently entered + * isolate. + * + * When an isolate is no longer used its resources should be freed + * by calling Dispose(). Using the delete operator is not allowed. + * + * V8::Initialize() must have run prior to this. + */ + static Isolate* New(const CreateParams& params); + + /** + * Returns the entered isolate for the current thread or NULL in + * case there is no current isolate. + * + * This method must not be invoked before V8::Initialize() was invoked. + */ + static Isolate* GetCurrent(); + + /** + * Returns the entered isolate for the current thread or NULL in + * case there is no current isolate. + * + * No checks are performed by this method. + */ + static Isolate* TryGetCurrent(); + + /** + * Return true if this isolate is currently active. + **/ + bool IsCurrent() const; + + /** + * Clears the set of objects held strongly by the heap. This set of + * objects are originally built when a WeakRef is created or + * successfully dereferenced. + * + * This is invoked automatically after microtasks are run. See + * MicrotasksPolicy for when microtasks are run. + * + * This needs to be manually invoked only if the embedder is manually running + * microtasks via a custom MicrotaskQueue class's PerformCheckpoint. In that + * case, it is the embedder's responsibility to make this call at a time which + * does not interrupt synchronous ECMAScript code execution. + */ + void ClearKeptObjects(); + + /** + * Custom callback used by embedders to help V8 determine if it should abort + * when it throws and no internal handler is predicted to catch the + * exception. If --abort-on-uncaught-exception is used on the command line, + * then V8 will abort if either: + * - no custom callback is set. + * - the custom callback set returns true. + * Otherwise, the custom callback will not be called and V8 will not abort. + */ + using AbortOnUncaughtExceptionCallback = bool (*)(Isolate*); + void SetAbortOnUncaughtExceptionCallback( + AbortOnUncaughtExceptionCallback callback); + + /** + * This specifies the callback called by the upcoming dynamic + * import() language feature to load modules. + */ + void SetHostImportModuleDynamicallyCallback( + HostImportModuleDynamicallyCallback callback); + + /** + * This specifies the callback called by the upcoming import.meta + * language feature to retrieve host-defined meta data for a module. + */ + void SetHostInitializeImportMetaObjectCallback( + HostInitializeImportMetaObjectCallback callback); + + /** + * This specifies the callback called by the upcoming ShadowRealm + * construction language feature to retrieve host created globals. + */ + void SetHostCreateShadowRealmContextCallback( + HostCreateShadowRealmContextCallback callback); + + /** + * This specifies the callback called when the stack property of Error + * is accessed. + */ + void SetPrepareStackTraceCallback(PrepareStackTraceCallback callback); + + /** + * Optional notification that the system is running low on memory. + * V8 uses these notifications to guide heuristics. + * It is allowed to call this function from another thread while + * the isolate is executing long running JavaScript code. + */ + void MemoryPressureNotification(MemoryPressureLevel level); + + /** + * Drop non-essential caches. Should only be called from testing code. + * The method can potentially block for a long time and does not necessarily + * trigger GC. + */ + void ClearCachesForTesting(); + + /** + * Methods below this point require holding a lock (using Locker) in + * a multi-threaded environment. + */ + + /** + * Sets this isolate as the entered one for the current thread. + * Saves the previously entered one (if any), so that it can be + * restored when exiting. Re-entering an isolate is allowed. + */ + void Enter(); + + /** + * Exits this isolate by restoring the previously entered one in the + * current thread. The isolate may still stay the same, if it was + * entered more than once. + * + * Requires: this == Isolate::GetCurrent(). + */ + void Exit(); + + /** + * Disposes the isolate. The isolate must not be entered by any + * thread to be disposable. + */ + void Dispose(); + + /** + * Dumps activated low-level V8 internal stats. This can be used instead + * of performing a full isolate disposal. + */ + void DumpAndResetStats(); + + /** + * Discards all V8 thread-specific data for the Isolate. Should be used + * if a thread is terminating and it has used an Isolate that will outlive + * the thread -- all thread-specific data for an Isolate is discarded when + * an Isolate is disposed so this call is pointless if an Isolate is about + * to be Disposed. + */ + void DiscardThreadSpecificMetadata(); + + /** + * Associate embedder-specific data with the isolate. |slot| has to be + * between 0 and GetNumberOfDataSlots() - 1. + */ + V8_INLINE void SetData(uint32_t slot, void* data); + + /** + * Retrieve embedder-specific data from the isolate. + * Returns NULL if SetData has never been called for the given |slot|. + */ + V8_INLINE void* GetData(uint32_t slot); + + /** + * Returns the maximum number of available embedder data slots. Valid slots + * are in the range of 0 - GetNumberOfDataSlots() - 1. + */ + V8_INLINE static uint32_t GetNumberOfDataSlots(); + + /** + * Return data that was previously attached to the isolate snapshot via + * SnapshotCreator, and removes the reference to it. + * Repeated call with the same index returns an empty MaybeLocal. + */ + template + V8_INLINE MaybeLocal GetDataFromSnapshotOnce(size_t index); + + /** + * Get statistics about the heap memory usage. + */ + void GetHeapStatistics(HeapStatistics* heap_statistics); + + /** + * Returns the number of spaces in the heap. + */ + size_t NumberOfHeapSpaces(); + + /** + * Get the memory usage of a space in the heap. + * + * \param space_statistics The HeapSpaceStatistics object to fill in + * statistics. + * \param index The index of the space to get statistics from, which ranges + * from 0 to NumberOfHeapSpaces() - 1. + * \returns true on success. + */ + bool GetHeapSpaceStatistics(HeapSpaceStatistics* space_statistics, + size_t index); + + /** + * Returns the number of types of objects tracked in the heap at GC. + */ + size_t NumberOfTrackedHeapObjectTypes(); + + /** + * Get statistics about objects in the heap. + * + * \param object_statistics The HeapObjectStatistics object to fill in + * statistics of objects of given type, which were live in the previous GC. + * \param type_index The index of the type of object to fill details about, + * which ranges from 0 to NumberOfTrackedHeapObjectTypes() - 1. + * \returns true on success. + */ + bool GetHeapObjectStatisticsAtLastGC(HeapObjectStatistics* object_statistics, + size_t type_index); + + /** + * Get statistics about code and its metadata in the heap. + * + * \param object_statistics The HeapCodeStatistics object to fill in + * statistics of code, bytecode and their metadata. + * \returns true on success. + */ + bool GetHeapCodeAndMetadataStatistics(HeapCodeStatistics* object_statistics); + + /** + * This API is experimental and may change significantly. + * + * Enqueues a memory measurement request and invokes the delegate with the + * results. + * + * \param delegate the delegate that defines which contexts to measure and + * reports the results. + * + * \param execution promptness executing the memory measurement. + * The kEager value is expected to be used only in tests. + */ + bool MeasureMemory( + std::unique_ptr delegate, + MeasureMemoryExecution execution = MeasureMemoryExecution::kDefault); + + /** + * Get a call stack sample from the isolate. + * \param state Execution state. + * \param frames Caller allocated buffer to store stack frames. + * \param frames_limit Maximum number of frames to capture. The buffer must + * be large enough to hold the number of frames. + * \param sample_info The sample info is filled up by the function + * provides number of actual captured stack frames and + * the current VM state. + * \note GetStackSample should only be called when the JS thread is paused or + * interrupted. Otherwise the behavior is undefined. + */ + void GetStackSample(const RegisterState& state, void** frames, + size_t frames_limit, SampleInfo* sample_info); + + /** + * Adjusts the amount of registered external memory. Used to give V8 an + * indication of the amount of externally allocated memory that is kept alive + * by JavaScript objects. V8 uses this to decide when to perform global + * garbage collections. Registering externally allocated memory will trigger + * global garbage collections more often than it would otherwise in an attempt + * to garbage collect the JavaScript objects that keep the externally + * allocated memory alive. + * + * \param change_in_bytes the change in externally allocated memory that is + * kept alive by JavaScript objects. + * \returns the adjusted value. + */ + int64_t AdjustAmountOfExternalAllocatedMemory(int64_t change_in_bytes); + + /** + * Returns heap profiler for this isolate. Will return NULL until the isolate + * is initialized. + */ + HeapProfiler* GetHeapProfiler(); + + /** + * Tells the VM whether the embedder is idle or not. + */ + void SetIdle(bool is_idle); + + /** Returns the ArrayBuffer::Allocator used in this isolate. */ + ArrayBuffer::Allocator* GetArrayBufferAllocator(); + + /** Returns true if this isolate has a current context. */ + bool InContext(); + + /** + * Returns the context of the currently running JavaScript, or the context + * on the top of the stack if no JavaScript is running. + */ + Local GetCurrentContext(); + + /** + * Returns either the last context entered through V8's C++ API, or the + * context of the currently running microtask while processing microtasks. + * If a context is entered while executing a microtask, that context is + * returned. + */ + Local GetEnteredOrMicrotaskContext(); + + /** + * Returns the Context that corresponds to the Incumbent realm in HTML spec. + * https://html.spec.whatwg.org/multipage/webappapis.html#incumbent + */ + Local GetIncumbentContext(); + + /** + * Schedules a v8::Exception::Error with the given message. + * See ThrowException for more details. Templatized to provide compile-time + * errors in case of too long strings (see v8::String::NewFromUtf8Literal). + */ + template + Local ThrowError(const char (&message)[N]) { + return ThrowError(String::NewFromUtf8Literal(this, message)); + } + Local ThrowError(Local message); + + /** + * Schedules an exception to be thrown when returning to JavaScript. When an + * exception has been scheduled it is illegal to invoke any JavaScript + * operation; the caller must return immediately and only after the exception + * has been handled does it become legal to invoke JavaScript operations. + */ + Local ThrowException(Local exception); + + using GCCallback = void (*)(Isolate* isolate, GCType type, + GCCallbackFlags flags); + using GCCallbackWithData = void (*)(Isolate* isolate, GCType type, + GCCallbackFlags flags, void* data); + + /** + * Enables the host application to receive a notification before a + * garbage collection. Allocations are allowed in the callback function, + * but the callback is not re-entrant: if the allocation inside it will + * trigger the garbage collection, the callback won't be called again. + * It is possible to specify the GCType filter for your callback. But it is + * not possible to register the same callback function two times with + * different GCType filters. + */ + void AddGCPrologueCallback(GCCallbackWithData callback, void* data = nullptr, + GCType gc_type_filter = kGCTypeAll); + void AddGCPrologueCallback(GCCallback callback, + GCType gc_type_filter = kGCTypeAll); + + /** + * This function removes callback which was installed by + * AddGCPrologueCallback function. + */ + void RemoveGCPrologueCallback(GCCallbackWithData, void* data = nullptr); + void RemoveGCPrologueCallback(GCCallback callback); + + START_ALLOW_USE_DEPRECATED() + /** + * Sets the embedder heap tracer for the isolate. + * SetEmbedderHeapTracer cannot be used simultaneously with AttachCppHeap. + */ + void SetEmbedderHeapTracer(EmbedderHeapTracer* tracer); + + /* + * Gets the currently active heap tracer for the isolate that was set with + * SetEmbedderHeapTracer. + */ + EmbedderHeapTracer* GetEmbedderHeapTracer(); + END_ALLOW_USE_DEPRECATED() + + /** + * Sets an embedder roots handle that V8 should consider when performing + * non-unified heap garbage collections. + * + * Using only EmbedderHeapTracer automatically sets up a default handler. + * The intended use case is for setting a custom handler after invoking + * `AttachCppHeap()`. + * + * V8 does not take ownership of the handler. + */ + void SetEmbedderRootsHandler(EmbedderRootsHandler* handler); + + /** + * Attaches a managed C++ heap as an extension to the JavaScript heap. The + * embedder maintains ownership of the CppHeap. At most one C++ heap can be + * attached to V8. + * + * AttachCppHeap cannot be used simultaneously with SetEmbedderHeapTracer. + * + * Multi-threaded use requires the use of v8::Locker/v8::Unlocker, see + * CppHeap. + */ + void AttachCppHeap(CppHeap*); + + /** + * Detaches a managed C++ heap if one was attached using `AttachCppHeap()`. + */ + void DetachCppHeap(); + + /** + * \returns the C++ heap managed by V8. Only available if such a heap has been + * attached using `AttachCppHeap()`. + */ + CppHeap* GetCppHeap() const; + + /** + * Use for |AtomicsWaitCallback| to indicate the type of event it receives. + */ + enum class AtomicsWaitEvent { + /** Indicates that this call is happening before waiting. */ + kStartWait, + /** `Atomics.wait()` finished because of an `Atomics.wake()` call. */ + kWokenUp, + /** `Atomics.wait()` finished because it timed out. */ + kTimedOut, + /** `Atomics.wait()` was interrupted through |TerminateExecution()|. */ + kTerminatedExecution, + /** `Atomics.wait()` was stopped through |AtomicsWaitWakeHandle|. */ + kAPIStopped, + /** `Atomics.wait()` did not wait, as the initial condition was not met. */ + kNotEqual + }; + + /** + * Passed to |AtomicsWaitCallback| as a means of stopping an ongoing + * `Atomics.wait` call. + */ + class V8_EXPORT AtomicsWaitWakeHandle { + public: + /** + * Stop this `Atomics.wait()` call and call the |AtomicsWaitCallback| + * with |kAPIStopped|. + * + * This function may be called from another thread. The caller has to ensure + * through proper synchronization that it is not called after + * the finishing |AtomicsWaitCallback|. + * + * Note that the ECMAScript specification does not plan for the possibility + * of wakeups that are neither coming from a timeout or an `Atomics.wake()` + * call, so this may invalidate assumptions made by existing code. + * The embedder may accordingly wish to schedule an exception in the + * finishing |AtomicsWaitCallback|. + */ + void Wake(); + }; + + /** + * Embedder callback for `Atomics.wait()` that can be added through + * |SetAtomicsWaitCallback|. + * + * This will be called just before starting to wait with the |event| value + * |kStartWait| and after finishing waiting with one of the other + * values of |AtomicsWaitEvent| inside of an `Atomics.wait()` call. + * + * |array_buffer| will refer to the underlying SharedArrayBuffer, + * |offset_in_bytes| to the location of the waited-on memory address inside + * the SharedArrayBuffer. + * + * |value| and |timeout_in_ms| will be the values passed to + * the `Atomics.wait()` call. If no timeout was used, |timeout_in_ms| + * will be `INFINITY`. + * + * In the |kStartWait| callback, |stop_handle| will be an object that + * is only valid until the corresponding finishing callback and that + * can be used to stop the wait process while it is happening. + * + * This callback may schedule exceptions, *unless* |event| is equal to + * |kTerminatedExecution|. + */ + using AtomicsWaitCallback = void (*)(AtomicsWaitEvent event, + Local array_buffer, + size_t offset_in_bytes, int64_t value, + double timeout_in_ms, + AtomicsWaitWakeHandle* stop_handle, + void* data); + + /** + * Set a new |AtomicsWaitCallback|. This overrides an earlier + * |AtomicsWaitCallback|, if there was any. If |callback| is nullptr, + * this unsets the callback. |data| will be passed to the callback + * as its last parameter. + */ + void SetAtomicsWaitCallback(AtomicsWaitCallback callback, void* data); + + /** + * Enables the host application to receive a notification after a + * garbage collection. Allocations are allowed in the callback function, + * but the callback is not re-entrant: if the allocation inside it will + * trigger the garbage collection, the callback won't be called again. + * It is possible to specify the GCType filter for your callback. But it is + * not possible to register the same callback function two times with + * different GCType filters. + */ + void AddGCEpilogueCallback(GCCallbackWithData callback, void* data = nullptr, + GCType gc_type_filter = kGCTypeAll); + void AddGCEpilogueCallback(GCCallback callback, + GCType gc_type_filter = kGCTypeAll); + + /** + * This function removes callback which was installed by + * AddGCEpilogueCallback function. + */ + void RemoveGCEpilogueCallback(GCCallbackWithData callback, + void* data = nullptr); + void RemoveGCEpilogueCallback(GCCallback callback); + + using GetExternallyAllocatedMemoryInBytesCallback = size_t (*)(); + + /** + * Set the callback that tells V8 how much memory is currently allocated + * externally of the V8 heap. Ideally this memory is somehow connected to V8 + * objects and may get freed-up when the corresponding V8 objects get + * collected by a V8 garbage collection. + */ + void SetGetExternallyAllocatedMemoryInBytesCallback( + GetExternallyAllocatedMemoryInBytesCallback callback); + + /** + * Forcefully terminate the current thread of JavaScript execution + * in the given isolate. + * + * This method can be used by any thread even if that thread has not + * acquired the V8 lock with a Locker object. + */ + void TerminateExecution(); + + /** + * Is V8 terminating JavaScript execution. + * + * Returns true if JavaScript execution is currently terminating + * because of a call to TerminateExecution. In that case there are + * still JavaScript frames on the stack and the termination + * exception is still active. + */ + bool IsExecutionTerminating(); + + /** + * Resume execution capability in the given isolate, whose execution + * was previously forcefully terminated using TerminateExecution(). + * + * When execution is forcefully terminated using TerminateExecution(), + * the isolate can not resume execution until all JavaScript frames + * have propagated the uncatchable exception which is generated. This + * method allows the program embedding the engine to handle the + * termination event and resume execution capability, even if + * JavaScript frames remain on the stack. + * + * This method can be used by any thread even if that thread has not + * acquired the V8 lock with a Locker object. + */ + void CancelTerminateExecution(); + + /** + * Request V8 to interrupt long running JavaScript code and invoke + * the given |callback| passing the given |data| to it. After |callback| + * returns control will be returned to the JavaScript code. + * There may be a number of interrupt requests in flight. + * Can be called from another thread without acquiring a |Locker|. + * Registered |callback| must not reenter interrupted Isolate. + */ + void RequestInterrupt(InterruptCallback callback, void* data); + + /** + * Returns true if there is ongoing background work within V8 that will + * eventually post a foreground task, like asynchronous WebAssembly + * compilation. + */ + bool HasPendingBackgroundTasks(); + + /** + * Request garbage collection in this Isolate. It is only valid to call this + * function if --expose_gc was specified. + * + * This should only be used for testing purposes and not to enforce a garbage + * collection schedule. It has strong negative impact on the garbage + * collection performance. Use IdleNotificationDeadline() or + * LowMemoryNotification() instead to influence the garbage collection + * schedule. + */ + void RequestGarbageCollectionForTesting(GarbageCollectionType type); + + /** + * Request garbage collection with a specific embedderstack state in this + * Isolate. It is only valid to call this function if --expose_gc was + * specified. + * + * This should only be used for testing purposes and not to enforce a garbage + * collection schedule. It has strong negative impact on the garbage + * collection performance. Use IdleNotificationDeadline() or + * LowMemoryNotification() instead to influence the garbage collection + * schedule. + */ + void RequestGarbageCollectionForTesting(GarbageCollectionType type, + StackState stack_state); + + /** + * Set the callback to invoke for logging event. + */ + void SetEventLogger(LogEventCallback that); + + /** + * Adds a callback to notify the host application right before a script + * is about to run. If a script re-enters the runtime during executing, the + * BeforeCallEnteredCallback is invoked for each re-entrance. + * Executing scripts inside the callback will re-trigger the callback. + */ + void AddBeforeCallEnteredCallback(BeforeCallEnteredCallback callback); + + /** + * Removes callback that was installed by AddBeforeCallEnteredCallback. + */ + void RemoveBeforeCallEnteredCallback(BeforeCallEnteredCallback callback); + + /** + * Adds a callback to notify the host application when a script finished + * running. If a script re-enters the runtime during executing, the + * CallCompletedCallback is only invoked when the outer-most script + * execution ends. Executing scripts inside the callback do not trigger + * further callbacks. + */ + void AddCallCompletedCallback(CallCompletedCallback callback); + + /** + * Removes callback that was installed by AddCallCompletedCallback. + */ + void RemoveCallCompletedCallback(CallCompletedCallback callback); + + /** + * Set the PromiseHook callback for various promise lifecycle + * events. + */ + void SetPromiseHook(PromiseHook hook); + + /** + * Set callback to notify about promise reject with no handler, or + * revocation of such a previous notification once the handler is added. + */ + void SetPromiseRejectCallback(PromiseRejectCallback callback); + + /** + * Runs the default MicrotaskQueue until it gets empty and perform other + * microtask checkpoint steps, such as calling ClearKeptObjects. Asserts that + * the MicrotasksPolicy is not kScoped. Any exceptions thrown by microtask + * callbacks are swallowed. + */ + void PerformMicrotaskCheckpoint(); + + /** + * Enqueues the callback to the default MicrotaskQueue + */ + void EnqueueMicrotask(Local microtask); + + /** + * Enqueues the callback to the default MicrotaskQueue + */ + void EnqueueMicrotask(MicrotaskCallback callback, void* data = nullptr); + + /** + * Controls how Microtasks are invoked. See MicrotasksPolicy for details. + */ + void SetMicrotasksPolicy(MicrotasksPolicy policy); + + /** + * Returns the policy controlling how Microtasks are invoked. + */ + MicrotasksPolicy GetMicrotasksPolicy() const; + + /** + * Adds a callback to notify the host application after + * microtasks were run on the default MicrotaskQueue. The callback is + * triggered by explicit RunMicrotasks call or automatic microtasks execution + * (see SetMicrotaskPolicy). + * + * Callback will trigger even if microtasks were attempted to run, + * but the microtasks queue was empty and no single microtask was actually + * executed. + * + * Executing scripts inside the callback will not re-trigger microtasks and + * the callback. + */ + void AddMicrotasksCompletedCallback( + MicrotasksCompletedCallbackWithData callback, void* data = nullptr); + + /** + * Removes callback that was installed by AddMicrotasksCompletedCallback. + */ + void RemoveMicrotasksCompletedCallback( + MicrotasksCompletedCallbackWithData callback, void* data = nullptr); + + /** + * Sets a callback for counting the number of times a feature of V8 is used. + */ + void SetUseCounterCallback(UseCounterCallback callback); + + /** + * Enables the host application to provide a mechanism for recording + * statistics counters. + */ + void SetCounterFunction(CounterLookupCallback); + + /** + * Enables the host application to provide a mechanism for recording + * histograms. The CreateHistogram function returns a + * histogram which will later be passed to the AddHistogramSample + * function. + */ + void SetCreateHistogramFunction(CreateHistogramCallback); + void SetAddHistogramSampleFunction(AddHistogramSampleCallback); + + /** + * Enables the host application to provide a mechanism for recording + * event based metrics. In order to use this interface + * include/v8-metrics.h + * needs to be included and the recorder needs to be derived from the + * Recorder base class defined there. + * This method can only be called once per isolate and must happen during + * isolate initialization before background threads are spawned. + */ + void SetMetricsRecorder( + const std::shared_ptr& metrics_recorder); + + /** + * Enables the host application to provide a mechanism for recording a + * predefined set of data as crash keys to be used in postmortem debugging in + * case of a crash. + */ + void SetAddCrashKeyCallback(AddCrashKeyCallback); + + /** + * Optional notification that the embedder is idle. + * V8 uses the notification to perform garbage collection. + * This call can be used repeatedly if the embedder remains idle. + * Returns true if the embedder should stop calling IdleNotificationDeadline + * until real work has been done. This indicates that V8 has done + * as much cleanup as it will be able to do. + * + * The deadline_in_seconds argument specifies the deadline V8 has to finish + * garbage collection work. deadline_in_seconds is compared with + * MonotonicallyIncreasingTime() and should be based on the same timebase as + * that function. There is no guarantee that the actual work will be done + * within the time limit. + */ + bool IdleNotificationDeadline(double deadline_in_seconds); + + /** + * Optional notification that the system is running low on memory. + * V8 uses these notifications to attempt to free memory. + */ + void LowMemoryNotification(); + + /** + * Optional notification that a context has been disposed. V8 uses these + * notifications to guide the GC heuristic and cancel FinalizationRegistry + * cleanup tasks. Returns the number of context disposals - including this one + * - since the last time V8 had a chance to clean up. + * + * The optional parameter |dependant_context| specifies whether the disposed + * context was depending on state from other contexts or not. + */ + int ContextDisposedNotification(bool dependant_context = true); + + /** + * Optional notification that the isolate switched to the foreground. + * V8 uses these notifications to guide heuristics. + */ + void IsolateInForegroundNotification(); + + /** + * Optional notification that the isolate switched to the background. + * V8 uses these notifications to guide heuristics. + */ + void IsolateInBackgroundNotification(); + + /** + * Optional notification which will enable the memory savings mode. + * V8 uses this notification to guide heuristics which may result in a + * smaller memory footprint at the cost of reduced runtime performance. + */ + void EnableMemorySavingsMode(); + + /** + * Optional notification which will disable the memory savings mode. + */ + void DisableMemorySavingsMode(); + + /** + * Optional notification to tell V8 the current performance requirements + * of the embedder based on RAIL. + * V8 uses these notifications to guide heuristics. + * This is an unfinished experimental feature. Semantics and implementation + * may change frequently. + */ + void SetRAILMode(RAILMode rail_mode); + + /** + * Update load start time of the RAIL mode + */ + void UpdateLoadStartTime(); + + /** + * Optional notification to tell V8 the current isolate is used for debugging + * and requires higher heap limit. + */ + void IncreaseHeapLimitForDebugging(); + + /** + * Restores the original heap limit after IncreaseHeapLimitForDebugging(). + */ + void RestoreOriginalHeapLimit(); + + /** + * Returns true if the heap limit was increased for debugging and the + * original heap limit was not restored yet. + */ + bool IsHeapLimitIncreasedForDebugging(); + + /** + * Allows the host application to provide the address of a function that is + * notified each time code is added, moved or removed. + * + * \param options options for the JIT code event handler. + * \param event_handler the JIT code event handler, which will be invoked + * each time code is added, moved or removed. + * \note \p event_handler won't get notified of existent code. + * \note since code removal notifications are not currently issued, the + * \p event_handler may get notifications of code that overlaps earlier + * code notifications. This happens when code areas are reused, and the + * earlier overlapping code areas should therefore be discarded. + * \note the events passed to \p event_handler and the strings they point to + * are not guaranteed to live past each call. The \p event_handler must + * copy strings and other parameters it needs to keep around. + * \note the set of events declared in JitCodeEvent::EventType is expected to + * grow over time, and the JitCodeEvent structure is expected to accrue + * new members. The \p event_handler function must ignore event codes + * it does not recognize to maintain future compatibility. + * \note Use Isolate::CreateParams to get events for code executed during + * Isolate setup. + */ + void SetJitCodeEventHandler(JitCodeEventOptions options, + JitCodeEventHandler event_handler); + + /** + * Modifies the stack limit for this Isolate. + * + * \param stack_limit An address beyond which the Vm's stack may not grow. + * + * \note If you are using threads then you should hold the V8::Locker lock + * while setting the stack limit and you must set a non-default stack + * limit separately for each thread. + */ + void SetStackLimit(uintptr_t stack_limit); + + /** + * Returns a memory range that can potentially contain jitted code. Code for + * V8's 'builtins' will not be in this range if embedded builtins is enabled. + * + * On Win64, embedders are advised to install function table callbacks for + * these ranges, as default SEH won't be able to unwind through jitted code. + * The first page of the code range is reserved for the embedder and is + * committed, writable, and executable, to be used to store unwind data, as + * documented in + * https://docs.microsoft.com/en-us/cpp/build/exception-handling-x64. + * + * Might be empty on other platforms. + * + * https://code.google.com/p/v8/issues/detail?id=3598 + */ + void GetCodeRange(void** start, size_t* length_in_bytes); + + /** + * As GetCodeRange, but for embedded builtins (these live in a distinct + * memory region from other V8 Code objects). + */ + void GetEmbeddedCodeRange(const void** start, size_t* length_in_bytes); + + /** + * Returns the JSEntryStubs necessary for use with the Unwinder API. + */ + JSEntryStubs GetJSEntryStubs(); + + static constexpr size_t kMinCodePagesBufferSize = 32; + + /** + * Copies the code heap pages currently in use by V8 into |code_pages_out|. + * |code_pages_out| must have at least kMinCodePagesBufferSize capacity and + * must be empty. + * + * Signal-safe, does not allocate, does not access the V8 heap. + * No code on the stack can rely on pages that might be missing. + * + * Returns the number of pages available to be copied, which might be greater + * than |capacity|. In this case, only |capacity| pages will be copied into + * |code_pages_out|. The caller should provide a bigger buffer on the next + * call in order to get all available code pages, but this is not required. + */ + size_t CopyCodePages(size_t capacity, MemoryRange* code_pages_out); + + /** Set the callback to invoke in case of fatal errors. */ + void SetFatalErrorHandler(FatalErrorCallback that); + + /** Set the callback to invoke in case of OOM errors. */ + void SetOOMErrorHandler(OOMErrorCallback that); + + /** + * Add a callback to invoke in case the heap size is close to the heap limit. + * If multiple callbacks are added, only the most recently added callback is + * invoked. + */ + void AddNearHeapLimitCallback(NearHeapLimitCallback callback, void* data); + + /** + * Remove the given callback and restore the heap limit to the + * given limit. If the given limit is zero, then it is ignored. + * If the current heap size is greater than the given limit, + * then the heap limit is restored to the minimal limit that + * is possible for the current heap size. + */ + void RemoveNearHeapLimitCallback(NearHeapLimitCallback callback, + size_t heap_limit); + + /** + * If the heap limit was changed by the NearHeapLimitCallback, then the + * initial heap limit will be restored once the heap size falls below the + * given threshold percentage of the initial heap limit. + * The threshold percentage is a number in (0.0, 1.0) range. + */ + void AutomaticallyRestoreInitialHeapLimit(double threshold_percent = 0.5); + + /** + * Set the callback to invoke to check if code generation from + * strings should be allowed. + */ + void SetModifyCodeGenerationFromStringsCallback( + ModifyCodeGenerationFromStringsCallback2 callback); + + /** + * Set the callback to invoke to check if wasm code generation should + * be allowed. + */ + void SetAllowWasmCodeGenerationCallback( + AllowWasmCodeGenerationCallback callback); + + /** + * Embedder over{ride|load} injection points for wasm APIs. The expectation + * is that the embedder sets them at most once. + */ + void SetWasmModuleCallback(ExtensionCallback callback); + void SetWasmInstanceCallback(ExtensionCallback callback); + + void SetWasmStreamingCallback(WasmStreamingCallback callback); + + void SetWasmAsyncResolvePromiseCallback( + WasmAsyncResolvePromiseCallback callback); + + void SetWasmLoadSourceMapCallback(WasmLoadSourceMapCallback callback); + + V8_DEPRECATED("Wasm SIMD is always enabled") + void SetWasmSimdEnabledCallback(WasmSimdEnabledCallback callback); + + V8_DEPRECATED("Wasm exceptions are always enabled") + void SetWasmExceptionsEnabledCallback(WasmExceptionsEnabledCallback callback); + + void SetSharedArrayBufferConstructorEnabledCallback( + SharedArrayBufferConstructorEnabledCallback callback); + + /** + * This function can be called by the embedder to signal V8 that the dynamic + * enabling of features has finished. V8 can now set up dynamically added + * features. + */ + void InstallConditionalFeatures(Local context); + + /** + * Check if V8 is dead and therefore unusable. This is the case after + * fatal errors such as out-of-memory situations. + */ + bool IsDead(); + + /** + * Adds a message listener (errors only). + * + * The same message listener can be added more than once and in that + * case it will be called more than once for each message. + * + * If data is specified, it will be passed to the callback when it is called. + * Otherwise, the exception object will be passed to the callback instead. + */ + bool AddMessageListener(MessageCallback that, + Local data = Local()); + + /** + * Adds a message listener. + * + * The same message listener can be added more than once and in that + * case it will be called more than once for each message. + * + * If data is specified, it will be passed to the callback when it is called. + * Otherwise, the exception object will be passed to the callback instead. + * + * A listener can listen for particular error levels by providing a mask. + */ + bool AddMessageListenerWithErrorLevel(MessageCallback that, + int message_levels, + Local data = Local()); + + /** + * Remove all message listeners from the specified callback function. + */ + void RemoveMessageListeners(MessageCallback that); + + /** Callback function for reporting failed access checks.*/ + void SetFailedAccessCheckCallbackFunction(FailedAccessCheckCallback); + + /** + * Tells V8 to capture current stack trace when uncaught exception occurs + * and report it to the message listeners. The option is off by default. + */ + void SetCaptureStackTraceForUncaughtExceptions( + bool capture, int frame_limit = 10, + StackTrace::StackTraceOptions options = StackTrace::kOverview); + + /** + * Iterates through all external resources referenced from current isolate + * heap. GC is not invoked prior to iterating, therefore there is no + * guarantee that visited objects are still alive. + */ + void VisitExternalResources(ExternalResourceVisitor* visitor); + + /** + * Check if this isolate is in use. + * True if at least one thread Enter'ed this isolate. + */ + bool IsInUse(); + + /** + * Set whether calling Atomics.wait (a function that may block) is allowed in + * this isolate. This can also be configured via + * CreateParams::allow_atomics_wait. + */ + void SetAllowAtomicsWait(bool allow); + + /** + * Time zone redetection indicator for + * DateTimeConfigurationChangeNotification. + * + * kSkip indicates V8 that the notification should not trigger redetecting + * host time zone. kRedetect indicates V8 that host time zone should be + * redetected, and used to set the default time zone. + * + * The host time zone detection may require file system access or similar + * operations unlikely to be available inside a sandbox. If v8 is run inside a + * sandbox, the host time zone has to be detected outside the sandbox before + * calling DateTimeConfigurationChangeNotification function. + */ + enum class TimeZoneDetection { kSkip, kRedetect }; + + /** + * Notification that the embedder has changed the time zone, daylight savings + * time or other date / time configuration parameters. V8 keeps a cache of + * various values used for date / time computation. This notification will + * reset those cached values for the current context so that date / time + * configuration changes would be reflected. + * + * This API should not be called more than needed as it will negatively impact + * the performance of date operations. + */ + void DateTimeConfigurationChangeNotification( + TimeZoneDetection time_zone_detection = TimeZoneDetection::kSkip); + + /** + * Notification that the embedder has changed the locale. V8 keeps a cache of + * various values used for locale computation. This notification will reset + * those cached values for the current context so that locale configuration + * changes would be reflected. + * + * This API should not be called more than needed as it will negatively impact + * the performance of locale operations. + */ + void LocaleConfigurationChangeNotification(); + + Isolate() = delete; + ~Isolate() = delete; + Isolate(const Isolate&) = delete; + Isolate& operator=(const Isolate&) = delete; + // Deleting operator new and delete here is allowed as ctor and dtor is also + // deleted. + void* operator new(size_t size) = delete; + void* operator new[](size_t size) = delete; + void operator delete(void*, size_t) = delete; + void operator delete[](void*, size_t) = delete; + + private: + template + friend class PersistentValueMapBase; + + internal::Address* GetDataFromSnapshotOnce(size_t index); + void ReportExternalAllocationLimitReached(); +}; + +void Isolate::SetData(uint32_t slot, void* data) { + using I = internal::Internals; + I::SetEmbedderData(this, slot, data); +} + +void* Isolate::GetData(uint32_t slot) { + using I = internal::Internals; + return I::GetEmbedderData(this, slot); +} + +uint32_t Isolate::GetNumberOfDataSlots() { + using I = internal::Internals; + return I::kNumIsolateDataSlots; +} + +template +MaybeLocal Isolate::GetDataFromSnapshotOnce(size_t index) { + T* data = reinterpret_cast(GetDataFromSnapshotOnce(index)); + if (data) internal::PerformCastCheck(data); + return Local(data); +} + +} // namespace v8 + +#endif // INCLUDE_V8_ISOLATE_H_ diff --git a/deps/include/v8-json.h b/deps/include/v8-json.h new file mode 100755 index 0000000..23d918f --- /dev/null +++ b/deps/include/v8-json.h @@ -0,0 +1,47 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_JSON_H_ +#define INCLUDE_V8_JSON_H_ + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; +class Value; +class String; + +/** + * A JSON Parser and Stringifier. + */ +class V8_EXPORT JSON { + public: + /** + * Tries to parse the string |json_string| and returns it as value if + * successful. + * + * \param the context in which to parse and create the value. + * \param json_string The string to parse. + * \return The corresponding value if successfully parsed. + */ + static V8_WARN_UNUSED_RESULT MaybeLocal Parse( + Local context, Local json_string); + + /** + * Tries to stringify the JSON-serializable object |json_object| and returns + * it as string if successful. + * + * \param json_object The JSON-serializable object to stringify. + * \return The corresponding string if successfully stringified. + */ + static V8_WARN_UNUSED_RESULT MaybeLocal Stringify( + Local context, Local json_object, + Local gap = Local()); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_JSON_H_ diff --git a/deps/include/v8-local-handle.h b/deps/include/v8-local-handle.h new file mode 100755 index 0000000..cbf87f9 --- /dev/null +++ b/deps/include/v8-local-handle.h @@ -0,0 +1,455 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_LOCAL_HANDLE_H_ +#define INCLUDE_V8_LOCAL_HANDLE_H_ + +#include + +#include + +#include "v8-internal.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Boolean; +template +class BasicTracedReference; +class Context; +class EscapableHandleScope; +template +class Eternal; +template +class FunctionCallbackInfo; +class Isolate; +template +class MaybeLocal; +template +class NonCopyablePersistentTraits; +class Object; +template > +class Persistent; +template +class PersistentBase; +template +class PersistentValueMapBase; +template +class PersistentValueVector; +class Primitive; +class Private; +template +class PropertyCallbackInfo; +template +class ReturnValue; +class String; +template +class Traced; +template +class TracedReference; +class TracedReferenceBase; +class Utils; + +namespace internal { +template +class CustomArguments; +} // namespace internal + +namespace api_internal { +// Called when ToLocalChecked is called on an empty Local. +V8_EXPORT void ToLocalEmpty(); +} // namespace api_internal + +/** + * A stack-allocated class that governs a number of local handles. + * After a handle scope has been created, all local handles will be + * allocated within that handle scope until either the handle scope is + * deleted or another handle scope is created. If there is already a + * handle scope and a new one is created, all allocations will take + * place in the new handle scope until it is deleted. After that, + * new handles will again be allocated in the original handle scope. + * + * After the handle scope of a local handle has been deleted the + * garbage collector will no longer track the object stored in the + * handle and may deallocate it. The behavior of accessing a handle + * for which the handle scope has been deleted is undefined. + */ +class V8_EXPORT V8_NODISCARD HandleScope { + public: + explicit HandleScope(Isolate* isolate); + + ~HandleScope(); + + /** + * Counts the number of allocated handles. + */ + static int NumberOfHandles(Isolate* isolate); + + V8_INLINE Isolate* GetIsolate() const { + return reinterpret_cast(i_isolate_); + } + + HandleScope(const HandleScope&) = delete; + void operator=(const HandleScope&) = delete; + + protected: + V8_INLINE HandleScope() = default; + + void Initialize(Isolate* isolate); + + static internal::Address* CreateHandle(internal::Isolate* i_isolate, + internal::Address value); + + private: + // Declaring operator new and delete as deleted is not spec compliant. + // Therefore declare them private instead to disable dynamic alloc + void* operator new(size_t size); + void* operator new[](size_t size); + void operator delete(void*, size_t); + void operator delete[](void*, size_t); + + internal::Isolate* i_isolate_; + internal::Address* prev_next_; + internal::Address* prev_limit_; + + // Local::New uses CreateHandle with an Isolate* parameter. + template + friend class Local; + + // Object::GetInternalField and Context::GetEmbedderData use CreateHandle with + // a HeapObject in their shortcuts. + friend class Object; + friend class Context; +}; + +/** + * An object reference managed by the v8 garbage collector. + * + * All objects returned from v8 have to be tracked by the garbage collector so + * that it knows that the objects are still alive. Also, because the garbage + * collector may move objects, it is unsafe to point directly to an object. + * Instead, all objects are stored in handles which are known by the garbage + * collector and updated whenever an object moves. Handles should always be + * passed by value (except in cases like out-parameters) and they should never + * be allocated on the heap. + * + * There are two types of handles: local and persistent handles. + * + * Local handles are light-weight and transient and typically used in local + * operations. They are managed by HandleScopes. That means that a HandleScope + * must exist on the stack when they are created and that they are only valid + * inside of the HandleScope active during their creation. For passing a local + * handle to an outer HandleScope, an EscapableHandleScope and its Escape() + * method must be used. + * + * Persistent handles can be used when storing objects across several + * independent operations and have to be explicitly deallocated when they're no + * longer used. + * + * It is safe to extract the object stored in the handle by dereferencing the + * handle (for instance, to extract the Object* from a Local); the value + * will still be governed by a handle behind the scenes and the same rules apply + * to these values as to their handles. + */ +template +class Local { + public: + V8_INLINE Local() : val_(nullptr) {} + template + V8_INLINE Local(Local that) : val_(reinterpret_cast(*that)) { + /** + * This check fails when trying to convert between incompatible + * handles. For example, converting from a Local to a + * Local. + */ + static_assert(std::is_base_of::value, "type check"); + } + + /** + * Returns true if the handle is empty. + */ + V8_INLINE bool IsEmpty() const { return val_ == nullptr; } + + /** + * Sets the handle to be empty. IsEmpty() will then return true. + */ + V8_INLINE void Clear() { val_ = nullptr; } + + V8_INLINE T* operator->() const { return val_; } + + V8_INLINE T* operator*() const { return val_; } + + /** + * Checks whether two handles are the same. + * Returns true if both are empty, or if the objects to which they refer + * are identical. + * + * If both handles refer to JS objects, this is the same as strict equality. + * For primitives, such as numbers or strings, a `false` return value does not + * indicate that the values aren't equal in the JavaScript sense. + * Use `Value::StrictEquals()` to check primitives for equality. + */ + template + V8_INLINE bool operator==(const Local& that) const { + internal::Address* a = reinterpret_cast(this->val_); + internal::Address* b = reinterpret_cast(that.val_); + if (a == nullptr) return b == nullptr; + if (b == nullptr) return false; + return *a == *b; + } + + template + V8_INLINE bool operator==(const PersistentBase& that) const { + internal::Address* a = reinterpret_cast(this->val_); + internal::Address* b = reinterpret_cast(that.val_); + if (a == nullptr) return b == nullptr; + if (b == nullptr) return false; + return *a == *b; + } + + /** + * Checks whether two handles are different. + * Returns true if only one of the handles is empty, or if + * the objects to which they refer are different. + * + * If both handles refer to JS objects, this is the same as strict + * non-equality. For primitives, such as numbers or strings, a `true` return + * value does not indicate that the values aren't equal in the JavaScript + * sense. Use `Value::StrictEquals()` to check primitives for equality. + */ + template + V8_INLINE bool operator!=(const Local& that) const { + return !operator==(that); + } + + template + V8_INLINE bool operator!=(const Persistent& that) const { + return !operator==(that); + } + + /** + * Cast a handle to a subclass, e.g. Local to Local. + * This is only valid if the handle actually refers to a value of the + * target type. + */ + template + V8_INLINE static Local Cast(Local that) { +#ifdef V8_ENABLE_CHECKS + // If we're going to perform the type check then we have to check + // that the handle isn't empty before doing the checked cast. + if (that.IsEmpty()) return Local(); +#endif + return Local(T::Cast(*that)); + } + + /** + * Calling this is equivalent to Local::Cast(). + * In particular, this is only valid if the handle actually refers to a value + * of the target type. + */ + template + V8_INLINE Local As() const { + return Local::Cast(*this); + } + + /** + * Create a local handle for the content of another handle. + * The referee is kept alive by the local handle even when + * the original handle is destroyed/disposed. + */ + V8_INLINE static Local New(Isolate* isolate, Local that) { + return New(isolate, that.val_); + } + + V8_INLINE static Local New(Isolate* isolate, + const PersistentBase& that) { + return New(isolate, that.val_); + } + + V8_INLINE static Local New(Isolate* isolate, + const BasicTracedReference& that) { + return New(isolate, *that); + } + + private: + friend class TracedReferenceBase; + friend class Utils; + template + friend class Eternal; + template + friend class PersistentBase; + template + friend class Persistent; + template + friend class Local; + template + friend class MaybeLocal; + template + friend class FunctionCallbackInfo; + template + friend class PropertyCallbackInfo; + friend class String; + friend class Object; + friend class Context; + friend class Isolate; + friend class Private; + template + friend class internal::CustomArguments; + friend Local Undefined(Isolate* isolate); + friend Local Null(Isolate* isolate); + friend Local True(Isolate* isolate); + friend Local False(Isolate* isolate); + friend class HandleScope; + friend class EscapableHandleScope; + template + friend class PersistentValueMapBase; + template + friend class PersistentValueVector; + template + friend class ReturnValue; + template + friend class Traced; + template + friend class BasicTracedReference; + template + friend class TracedReference; + + explicit V8_INLINE Local(T* that) : val_(that) {} + V8_INLINE static Local New(Isolate* isolate, T* that) { + if (that == nullptr) return Local(); + T* that_ptr = that; + internal::Address* p = reinterpret_cast(that_ptr); + return Local(reinterpret_cast(HandleScope::CreateHandle( + reinterpret_cast(isolate), *p))); + } + T* val_; +}; + +#if !defined(V8_IMMINENT_DEPRECATION_WARNINGS) +// Handle is an alias for Local for historical reasons. +template +using Handle = Local; +#endif + +/** + * A MaybeLocal<> is a wrapper around Local<> that enforces a check whether + * the Local<> is empty before it can be used. + * + * If an API method returns a MaybeLocal<>, the API method can potentially fail + * either because an exception is thrown, or because an exception is pending, + * e.g. because a previous API call threw an exception that hasn't been caught + * yet, or because a TerminateExecution exception was thrown. In that case, an + * empty MaybeLocal is returned. + */ +template +class MaybeLocal { + public: + V8_INLINE MaybeLocal() : val_(nullptr) {} + template + V8_INLINE MaybeLocal(Local that) : val_(reinterpret_cast(*that)) { + static_assert(std::is_base_of::value, "type check"); + } + + V8_INLINE bool IsEmpty() const { return val_ == nullptr; } + + /** + * Converts this MaybeLocal<> to a Local<>. If this MaybeLocal<> is empty, + * |false| is returned and |out| is assigned with nullptr. + */ + template + V8_WARN_UNUSED_RESULT V8_INLINE bool ToLocal(Local* out) const { + out->val_ = IsEmpty() ? nullptr : this->val_; + return !IsEmpty(); + } + + /** + * Converts this MaybeLocal<> to a Local<>. If this MaybeLocal<> is empty, + * V8 will crash the process. + */ + V8_INLINE Local ToLocalChecked() { + if (V8_UNLIKELY(val_ == nullptr)) api_internal::ToLocalEmpty(); + return Local(val_); + } + + /** + * Converts this MaybeLocal<> to a Local<>, using a default value if this + * MaybeLocal<> is empty. + */ + template + V8_INLINE Local FromMaybe(Local default_value) const { + return IsEmpty() ? default_value : Local(val_); + } + + private: + T* val_; +}; + +/** + * A HandleScope which first allocates a handle in the current scope + * which will be later filled with the escape value. + */ +class V8_EXPORT V8_NODISCARD EscapableHandleScope : public HandleScope { + public: + explicit EscapableHandleScope(Isolate* isolate); + V8_INLINE ~EscapableHandleScope() = default; + + /** + * Pushes the value into the previous scope and returns a handle to it. + * Cannot be called twice. + */ + template + V8_INLINE Local Escape(Local value) { + internal::Address* slot = + Escape(reinterpret_cast(*value)); + return Local(reinterpret_cast(slot)); + } + + template + V8_INLINE MaybeLocal EscapeMaybe(MaybeLocal value) { + return Escape(value.FromMaybe(Local())); + } + + EscapableHandleScope(const EscapableHandleScope&) = delete; + void operator=(const EscapableHandleScope&) = delete; + + private: + // Declaring operator new and delete as deleted is not spec compliant. + // Therefore declare them private instead to disable dynamic alloc + void* operator new(size_t size); + void* operator new[](size_t size); + void operator delete(void*, size_t); + void operator delete[](void*, size_t); + + internal::Address* Escape(internal::Address* escape_value); + internal::Address* escape_slot_; +}; + +/** + * A SealHandleScope acts like a handle scope in which no handle allocations + * are allowed. It can be useful for debugging handle leaks. + * Handles can be allocated within inner normal HandleScopes. + */ +class V8_EXPORT V8_NODISCARD SealHandleScope { + public: + explicit SealHandleScope(Isolate* isolate); + ~SealHandleScope(); + + SealHandleScope(const SealHandleScope&) = delete; + void operator=(const SealHandleScope&) = delete; + + private: + // Declaring operator new and delete as deleted is not spec compliant. + // Therefore declare them private instead to disable dynamic alloc + void* operator new(size_t size); + void* operator new[](size_t size); + void operator delete(void*, size_t); + void operator delete[](void*, size_t); + + internal::Isolate* const i_isolate_; + internal::Address* prev_limit_; + int prev_sealed_level_; +}; + +} // namespace v8 + +#endif // INCLUDE_V8_LOCAL_HANDLE_H_ diff --git a/deps/include/v8-locker.h b/deps/include/v8-locker.h new file mode 100755 index 0000000..22b7a87 --- /dev/null +++ b/deps/include/v8-locker.h @@ -0,0 +1,138 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_LOCKER_H_ +#define INCLUDE_V8_LOCKER_H_ + +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +namespace internal { +class Isolate; +} // namespace internal + +class Isolate; + +/** + * Multiple threads in V8 are allowed, but only one thread at a time is allowed + * to use any given V8 isolate, see the comments in the Isolate class. The + * definition of 'using a V8 isolate' includes accessing handles or holding onto + * object pointers obtained from V8 handles while in the particular V8 isolate. + * It is up to the user of V8 to ensure, perhaps with locking, that this + * constraint is not violated. In addition to any other synchronization + * mechanism that may be used, the v8::Locker and v8::Unlocker classes must be + * used to signal thread switches to V8. + * + * v8::Locker is a scoped lock object. While it's active, i.e. between its + * construction and destruction, the current thread is allowed to use the locked + * isolate. V8 guarantees that an isolate can be locked by at most one thread at + * any time. In other words, the scope of a v8::Locker is a critical section. + * + * Sample usage: + * \code + * ... + * { + * v8::Locker locker(isolate); + * v8::Isolate::Scope isolate_scope(isolate); + * ... + * // Code using V8 and isolate goes here. + * ... + * } // Destructor called here + * \endcode + * + * If you wish to stop using V8 in a thread A you can do this either by + * destroying the v8::Locker object as above or by constructing a v8::Unlocker + * object: + * + * \code + * { + * isolate->Exit(); + * v8::Unlocker unlocker(isolate); + * ... + * // Code not using V8 goes here while V8 can run in another thread. + * ... + * } // Destructor called here. + * isolate->Enter(); + * \endcode + * + * The Unlocker object is intended for use in a long-running callback from V8, + * where you want to release the V8 lock for other threads to use. + * + * The v8::Locker is a recursive lock, i.e. you can lock more than once in a + * given thread. This can be useful if you have code that can be called either + * from code that holds the lock or from code that does not. The Unlocker is + * not recursive so you can not have several Unlockers on the stack at once, and + * you cannot use an Unlocker in a thread that is not inside a Locker's scope. + * + * An unlocker will unlock several lockers if it has to and reinstate the + * correct depth of locking on its destruction, e.g.: + * + * \code + * // V8 not locked. + * { + * v8::Locker locker(isolate); + * Isolate::Scope isolate_scope(isolate); + * // V8 locked. + * { + * v8::Locker another_locker(isolate); + * // V8 still locked (2 levels). + * { + * isolate->Exit(); + * v8::Unlocker unlocker(isolate); + * // V8 not locked. + * } + * isolate->Enter(); + * // V8 locked again (2 levels). + * } + * // V8 still locked (1 level). + * } + * // V8 Now no longer locked. + * \endcode + */ +class V8_EXPORT Unlocker { + public: + /** + * Initialize Unlocker for a given Isolate. + */ + V8_INLINE explicit Unlocker(Isolate* isolate) { Initialize(isolate); } + + ~Unlocker(); + + private: + void Initialize(Isolate* isolate); + + internal::Isolate* isolate_; +}; + +class V8_EXPORT Locker { + public: + /** + * Initialize Locker for a given Isolate. + */ + V8_INLINE explicit Locker(Isolate* isolate) { Initialize(isolate); } + + ~Locker(); + + /** + * Returns whether or not the locker for a given isolate, is locked by the + * current thread. + */ + static bool IsLocked(Isolate* isolate); + + // Disallow copying and assigning. + Locker(const Locker&) = delete; + void operator=(const Locker&) = delete; + + private: + void Initialize(Isolate* isolate); + + bool has_lock_; + bool top_level_; + internal::Isolate* isolate_; +}; + +} // namespace v8 + +#endif // INCLUDE_V8_LOCKER_H_ diff --git a/deps/include/v8-maybe.h b/deps/include/v8-maybe.h new file mode 100755 index 0000000..8d3aeab --- /dev/null +++ b/deps/include/v8-maybe.h @@ -0,0 +1,160 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_MAYBE_H_ +#define INCLUDE_V8_MAYBE_H_ + +#include +#include + +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +namespace api_internal { +// Called when ToChecked is called on an empty Maybe. +V8_EXPORT void FromJustIsNothing(); +} // namespace api_internal + +/** + * A simple Maybe type, representing an object which may or may not have a + * value, see https://hackage.haskell.org/package/base/docs/Data-Maybe.html. + * + * If an API method returns a Maybe<>, the API method can potentially fail + * either because an exception is thrown, or because an exception is pending, + * e.g. because a previous API call threw an exception that hasn't been caught + * yet, or because a TerminateExecution exception was thrown. In that case, a + * "Nothing" value is returned. + */ +template +class Maybe { + public: + V8_INLINE bool IsNothing() const { return !has_value_; } + V8_INLINE bool IsJust() const { return has_value_; } + + /** + * An alias for |FromJust|. Will crash if the Maybe<> is nothing. + */ + V8_INLINE T ToChecked() const { return FromJust(); } + + /** + * Short-hand for ToChecked(), which doesn't return a value. To be used, where + * the actual value of the Maybe is not needed like Object::Set. + */ + V8_INLINE void Check() const { + if (V8_UNLIKELY(!IsJust())) api_internal::FromJustIsNothing(); + } + + /** + * Converts this Maybe<> to a value of type T. If this Maybe<> is + * nothing (empty), |false| is returned and |out| is left untouched. + */ + V8_WARN_UNUSED_RESULT V8_INLINE bool To(T* out) const { + if (V8_LIKELY(IsJust())) *out = value_; + return IsJust(); + } + + /** + * Converts this Maybe<> to a value of type T. If this Maybe<> is + * nothing (empty), V8 will crash the process. + */ + V8_INLINE T FromJust() const& { + if (V8_UNLIKELY(!IsJust())) api_internal::FromJustIsNothing(); + return value_; + } + + /** + * Converts this Maybe<> to a value of type T. If this Maybe<> is + * nothing (empty), V8 will crash the process. + */ + V8_INLINE T FromJust() && { + if (V8_UNLIKELY(!IsJust())) api_internal::FromJustIsNothing(); + return std::move(value_); + } + + /** + * Converts this Maybe<> to a value of type T, using a default value if this + * Maybe<> is nothing (empty). + */ + V8_INLINE T FromMaybe(const T& default_value) const { + return has_value_ ? value_ : default_value; + } + + V8_INLINE bool operator==(const Maybe& other) const { + return (IsJust() == other.IsJust()) && + (!IsJust() || FromJust() == other.FromJust()); + } + + V8_INLINE bool operator!=(const Maybe& other) const { + return !operator==(other); + } + + private: + Maybe() : has_value_(false) {} + explicit Maybe(const T& t) : has_value_(true), value_(t) {} + explicit Maybe(T&& t) : has_value_(true), value_(std::move(t)) {} + + bool has_value_; + T value_; + + template + friend Maybe Nothing(); + template + friend Maybe Just(const U& u); + template >*> + friend Maybe Just(U&& u); +}; + +template +inline Maybe Nothing() { + return Maybe(); +} + +template +inline Maybe Just(const T& t) { + return Maybe(t); +} + +// Don't use forwarding references here but instead use two overloads. +// Forwarding references only work when type deduction takes place, which is not +// the case for callsites such as Just(t). +template >* = nullptr> +inline Maybe Just(T&& t) { + return Maybe(std::move(t)); +} + +// A template specialization of Maybe for the case of T = void. +template <> +class Maybe { + public: + V8_INLINE bool IsNothing() const { return !is_valid_; } + V8_INLINE bool IsJust() const { return is_valid_; } + + V8_INLINE bool operator==(const Maybe& other) const { + return IsJust() == other.IsJust(); + } + + V8_INLINE bool operator!=(const Maybe& other) const { + return !operator==(other); + } + + private: + struct JustTag {}; + + Maybe() : is_valid_(false) {} + explicit Maybe(JustTag) : is_valid_(true) {} + + bool is_valid_; + + template + friend Maybe Nothing(); + friend Maybe JustVoid(); +}; + +inline Maybe JustVoid() { return Maybe(Maybe::JustTag()); } + +} // namespace v8 + +#endif // INCLUDE_V8_MAYBE_H_ diff --git a/deps/include/v8-memory-span.h b/deps/include/v8-memory-span.h new file mode 100755 index 0000000..b26af4f --- /dev/null +++ b/deps/include/v8-memory-span.h @@ -0,0 +1,43 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_MEMORY_SPAN_H_ +#define INCLUDE_V8_MEMORY_SPAN_H_ + +#include + +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +/** + * Points to an unowned continous buffer holding a known number of elements. + * + * This is similar to std::span (under consideration for C++20), but does not + * require advanced C++ support. In the (far) future, this may be replaced with + * or aliased to std::span. + * + * To facilitate future migration, this class exposes a subset of the interface + * implemented by std::span. + */ +template +class V8_EXPORT MemorySpan { + public: + /** The default constructor creates an empty span. */ + constexpr MemorySpan() = default; + + constexpr MemorySpan(T* data, size_t size) : data_(data), size_(size) {} + + /** Returns a pointer to the beginning of the buffer. */ + constexpr T* data() const { return data_; } + /** Returns the number of elements that the buffer holds. */ + constexpr size_t size() const { return size_; } + + private: + T* data_ = nullptr; + size_t size_ = 0; +}; + +} // namespace v8 +#endif // INCLUDE_V8_MEMORY_SPAN_H_ diff --git a/deps/include/v8-message.h b/deps/include/v8-message.h new file mode 100755 index 0000000..09f9a0a --- /dev/null +++ b/deps/include/v8-message.h @@ -0,0 +1,214 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_MESSAGE_H_ +#define INCLUDE_V8_MESSAGE_H_ + +#include + +#include + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-maybe.h" // NOLINT(build/include_directory) +#include "v8-primitive.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Integer; +class PrimitiveArray; +class StackTrace; +class String; +class Value; + +/** + * The optional attributes of ScriptOrigin. + */ +class ScriptOriginOptions { + public: + V8_INLINE ScriptOriginOptions(bool is_shared_cross_origin = false, + bool is_opaque = false, bool is_wasm = false, + bool is_module = false) + : flags_((is_shared_cross_origin ? kIsSharedCrossOrigin : 0) | + (is_wasm ? kIsWasm : 0) | (is_opaque ? kIsOpaque : 0) | + (is_module ? kIsModule : 0)) {} + V8_INLINE ScriptOriginOptions(int flags) + : flags_(flags & + (kIsSharedCrossOrigin | kIsOpaque | kIsWasm | kIsModule)) {} + + bool IsSharedCrossOrigin() const { + return (flags_ & kIsSharedCrossOrigin) != 0; + } + bool IsOpaque() const { return (flags_ & kIsOpaque) != 0; } + bool IsWasm() const { return (flags_ & kIsWasm) != 0; } + bool IsModule() const { return (flags_ & kIsModule) != 0; } + + int Flags() const { return flags_; } + + private: + enum { + kIsSharedCrossOrigin = 1, + kIsOpaque = 1 << 1, + kIsWasm = 1 << 2, + kIsModule = 1 << 3 + }; + const int flags_; +}; + +/** + * The origin, within a file, of a script. + */ +class V8_EXPORT ScriptOrigin { + public: + V8_INLINE ScriptOrigin(Isolate* isolate, Local resource_name, + int resource_line_offset = 0, + int resource_column_offset = 0, + bool resource_is_shared_cross_origin = false, + int script_id = -1, + Local source_map_url = Local(), + bool resource_is_opaque = false, bool is_wasm = false, + bool is_module = false, + Local host_defined_options = Local()) + : v8_isolate_(isolate), + resource_name_(resource_name), + resource_line_offset_(resource_line_offset), + resource_column_offset_(resource_column_offset), + options_(resource_is_shared_cross_origin, resource_is_opaque, is_wasm, + is_module), + script_id_(script_id), + source_map_url_(source_map_url), + host_defined_options_(host_defined_options) { + VerifyHostDefinedOptions(); + } + + V8_INLINE Local ResourceName() const; + V8_INLINE int LineOffset() const; + V8_INLINE int ColumnOffset() const; + V8_INLINE int ScriptId() const; + V8_INLINE Local SourceMapUrl() const; + V8_INLINE Local GetHostDefinedOptions() const; + V8_INLINE ScriptOriginOptions Options() const { return options_; } + + private: + void VerifyHostDefinedOptions() const; + Isolate* v8_isolate_; + Local resource_name_; + int resource_line_offset_; + int resource_column_offset_; + ScriptOriginOptions options_; + int script_id_; + Local source_map_url_; + Local host_defined_options_; +}; + +/** + * An error message. + */ +class V8_EXPORT Message { + public: + Local Get() const; + + /** + * Return the isolate to which the Message belongs. + */ + Isolate* GetIsolate() const; + + V8_WARN_UNUSED_RESULT MaybeLocal GetSource( + Local context) const; + V8_WARN_UNUSED_RESULT MaybeLocal GetSourceLine( + Local context) const; + + /** + * Returns the origin for the script from where the function causing the + * error originates. + */ + ScriptOrigin GetScriptOrigin() const; + + /** + * Returns the resource name for the script from where the function causing + * the error originates. + */ + Local GetScriptResourceName() const; + + /** + * Exception stack trace. By default stack traces are not captured for + * uncaught exceptions. SetCaptureStackTraceForUncaughtExceptions allows + * to change this option. + */ + Local GetStackTrace() const; + + /** + * Returns the number, 1-based, of the line where the error occurred. + */ + V8_WARN_UNUSED_RESULT Maybe GetLineNumber(Local context) const; + + /** + * Returns the index within the script of the first character where + * the error occurred. + */ + int GetStartPosition() const; + + /** + * Returns the index within the script of the last character where + * the error occurred. + */ + int GetEndPosition() const; + + /** + * Returns the Wasm function index where the error occurred. Returns -1 if + * message is not from a Wasm script. + */ + int GetWasmFunctionIndex() const; + + /** + * Returns the error level of the message. + */ + int ErrorLevel() const; + + /** + * Returns the index within the line of the first character where + * the error occurred. + */ + int GetStartColumn() const; + V8_WARN_UNUSED_RESULT Maybe GetStartColumn(Local context) const; + + /** + * Returns the index within the line of the last character where + * the error occurred. + */ + int GetEndColumn() const; + V8_WARN_UNUSED_RESULT Maybe GetEndColumn(Local context) const; + + /** + * Passes on the value set by the embedder when it fed the script from which + * this Message was generated to V8. + */ + bool IsSharedCrossOrigin() const; + bool IsOpaque() const; + + static void PrintCurrentStackTrace(Isolate* isolate, std::ostream& out); + + static const int kNoLineNumberInfo = 0; + static const int kNoColumnInfo = 0; + static const int kNoScriptIdInfo = 0; + static const int kNoWasmFunctionIndexInfo = -1; +}; + +Local ScriptOrigin::ResourceName() const { return resource_name_; } + +Local ScriptOrigin::GetHostDefinedOptions() const { + return host_defined_options_; +} + +int ScriptOrigin::LineOffset() const { return resource_line_offset_; } + +int ScriptOrigin::ColumnOffset() const { return resource_column_offset_; } + +int ScriptOrigin::ScriptId() const { return script_id_; } + +Local ScriptOrigin::SourceMapUrl() const { return source_map_url_; } + +} // namespace v8 + +#endif // INCLUDE_V8_MESSAGE_H_ diff --git a/deps/include/v8-metrics.h b/deps/include/v8-metrics.h new file mode 100755 index 0000000..887012a --- /dev/null +++ b/deps/include/v8-metrics.h @@ -0,0 +1,237 @@ +// Copyright 2020 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_METRICS_H_ +#define V8_METRICS_H_ + +#include +#include + +#include + +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; +class Isolate; + +namespace metrics { + +struct GarbageCollectionPhases { + int64_t total_wall_clock_duration_in_us = -1; + int64_t compact_wall_clock_duration_in_us = -1; + int64_t mark_wall_clock_duration_in_us = -1; + int64_t sweep_wall_clock_duration_in_us = -1; + int64_t weak_wall_clock_duration_in_us = -1; +}; + +struct GarbageCollectionSizes { + int64_t bytes_before = -1; + int64_t bytes_after = -1; + int64_t bytes_freed = -1; +}; + +struct GarbageCollectionFullCycle { + int reason = -1; + GarbageCollectionPhases total; + GarbageCollectionPhases total_cpp; + GarbageCollectionPhases main_thread; + GarbageCollectionPhases main_thread_cpp; + GarbageCollectionPhases main_thread_atomic; + GarbageCollectionPhases main_thread_atomic_cpp; + GarbageCollectionPhases main_thread_incremental; + GarbageCollectionPhases main_thread_incremental_cpp; + GarbageCollectionSizes objects; + GarbageCollectionSizes objects_cpp; + GarbageCollectionSizes memory; + GarbageCollectionSizes memory_cpp; + double collection_rate_in_percent = -1.0; + double collection_rate_cpp_in_percent = -1.0; + double efficiency_in_bytes_per_us = -1.0; + double efficiency_cpp_in_bytes_per_us = -1.0; + double main_thread_efficiency_in_bytes_per_us = -1.0; + double main_thread_efficiency_cpp_in_bytes_per_us = -1.0; +}; + +struct GarbageCollectionFullMainThreadIncrementalMark { + int64_t wall_clock_duration_in_us = -1; + int64_t cpp_wall_clock_duration_in_us = -1; +}; + +struct GarbageCollectionFullMainThreadIncrementalSweep { + int64_t wall_clock_duration_in_us = -1; + int64_t cpp_wall_clock_duration_in_us = -1; +}; + +template +struct GarbageCollectionBatchedEvents { + std::vector events; +}; + +using GarbageCollectionFullMainThreadBatchedIncrementalMark = + GarbageCollectionBatchedEvents< + GarbageCollectionFullMainThreadIncrementalMark>; +using GarbageCollectionFullMainThreadBatchedIncrementalSweep = + GarbageCollectionBatchedEvents< + GarbageCollectionFullMainThreadIncrementalSweep>; + +struct GarbageCollectionYoungCycle { + int reason = -1; + int64_t total_wall_clock_duration_in_us = -1; + int64_t main_thread_wall_clock_duration_in_us = -1; + double collection_rate_in_percent = -1.0; + double efficiency_in_bytes_per_us = -1.0; + double main_thread_efficiency_in_bytes_per_us = -1.0; +#if defined(CPPGC_YOUNG_GENERATION) + GarbageCollectionPhases total_cpp; + GarbageCollectionSizes objects_cpp; + GarbageCollectionSizes memory_cpp; + double collection_rate_cpp_in_percent = -1.0; + double efficiency_cpp_in_bytes_per_us = -1.0; + double main_thread_efficiency_cpp_in_bytes_per_us = -1.0; +#endif // defined(CPPGC_YOUNG_GENERATION) +}; + +struct WasmModuleDecoded { + bool async = false; + bool streamed = false; + bool success = false; + size_t module_size_in_bytes = 0; + size_t function_count = 0; + int64_t wall_clock_duration_in_us = -1; + int64_t cpu_duration_in_us = -1; +}; + +struct WasmModuleCompiled { + bool async = false; + bool streamed = false; + bool cached = false; + bool deserialized = false; + bool lazy = false; + bool success = false; + size_t code_size_in_bytes = 0; + size_t liftoff_bailout_count = 0; + int64_t wall_clock_duration_in_us = -1; + int64_t cpu_duration_in_us = -1; +}; + +struct WasmModuleInstantiated { + bool async = false; + bool success = false; + size_t imported_function_count = 0; + int64_t wall_clock_duration_in_us = -1; +}; + +struct WasmModulesPerIsolate { + size_t count = 0; +}; + +/** + * This class serves as a base class for recording event-based metrics in V8. + * There a two kinds of metrics, those which are expected to be thread-safe and + * whose implementation is required to fulfill this requirement and those whose + * implementation does not have that requirement and only needs to be + * executable on the main thread. If such an event is triggered from a + * background thread, it will be delayed and executed by the foreground task + * runner. + * + * The embedder is expected to call v8::Isolate::SetMetricsRecorder() + * providing its implementation and have the virtual methods overwritten + * for the events it cares about. + */ +class V8_EXPORT Recorder { + public: + // A unique identifier for a context in this Isolate. + // It is guaranteed to not be reused throughout the lifetime of the Isolate. + class ContextId { + public: + ContextId() : id_(kEmptyId) {} + + bool IsEmpty() const { return id_ == kEmptyId; } + static const ContextId Empty() { return ContextId{kEmptyId}; } + + bool operator==(const ContextId& other) const { return id_ == other.id_; } + bool operator!=(const ContextId& other) const { return id_ != other.id_; } + + private: + friend class ::v8::Context; + friend class ::v8::internal::Isolate; + + explicit ContextId(uintptr_t id) : id_(id) {} + + static constexpr uintptr_t kEmptyId = 0; + uintptr_t id_; + }; + + virtual ~Recorder() = default; + + // Main thread events. Those are only triggered on the main thread, and hence + // can access the context. +#define ADD_MAIN_THREAD_EVENT(E) \ + virtual void AddMainThreadEvent(const E&, ContextId) {} + ADD_MAIN_THREAD_EVENT(GarbageCollectionFullCycle) + ADD_MAIN_THREAD_EVENT(GarbageCollectionFullMainThreadIncrementalMark) + ADD_MAIN_THREAD_EVENT(GarbageCollectionFullMainThreadBatchedIncrementalMark) + ADD_MAIN_THREAD_EVENT(GarbageCollectionFullMainThreadIncrementalSweep) + ADD_MAIN_THREAD_EVENT(GarbageCollectionFullMainThreadBatchedIncrementalSweep) + ADD_MAIN_THREAD_EVENT(GarbageCollectionYoungCycle) + ADD_MAIN_THREAD_EVENT(WasmModuleDecoded) + ADD_MAIN_THREAD_EVENT(WasmModuleCompiled) + ADD_MAIN_THREAD_EVENT(WasmModuleInstantiated) +#undef ADD_MAIN_THREAD_EVENT + + // Thread-safe events are not allowed to access the context and therefore do + // not carry a context ID with them. These IDs can be generated using + // Recorder::GetContextId() and the ID will be valid throughout the lifetime + // of the isolate. It is not guaranteed that the ID will still resolve to + // a valid context using Recorder::GetContext() at the time the metric is + // recorded. In this case, an empty handle will be returned. +#define ADD_THREAD_SAFE_EVENT(E) \ + virtual void AddThreadSafeEvent(const E&) {} + ADD_THREAD_SAFE_EVENT(WasmModulesPerIsolate) +#undef ADD_THREAD_SAFE_EVENT + + virtual void NotifyIsolateDisposal() {} + + // Return the context with the given id or an empty handle if the context + // was already garbage collected. + static MaybeLocal GetContext(Isolate* isolate, ContextId id); + // Return the unique id corresponding to the given context. + static ContextId GetContextId(Local context); +}; + +/** + * Experimental API intended for the LongTasks UKM (crbug.com/1173527). + * The Reset() method should be called at the start of a potential + * long task. The Get() method returns durations of V8 work that + * happened during the task. + * + * This API is experimental and may be removed/changed in the future. + */ +struct V8_EXPORT LongTaskStats { + /** + * Resets durations of V8 work for the new task. + */ + V8_INLINE static void Reset(Isolate* isolate) { + v8::internal::Internals::IncrementLongTasksStatsCounter(isolate); + } + + /** + * Returns durations of V8 work that happened since the last Reset(). + */ + static LongTaskStats Get(Isolate* isolate); + + int64_t gc_full_atomic_wall_clock_duration_us = 0; + int64_t gc_full_incremental_wall_clock_duration_us = 0; + int64_t gc_young_wall_clock_duration_us = 0; + // Only collected with --slow-histograms + int64_t v8_execute_us = 0; +}; + +} // namespace metrics +} // namespace v8 + +#endif // V8_METRICS_H_ diff --git a/deps/include/v8-microtask-queue.h b/deps/include/v8-microtask-queue.h new file mode 100755 index 0000000..85d227f --- /dev/null +++ b/deps/include/v8-microtask-queue.h @@ -0,0 +1,157 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_MICROTASKS_QUEUE_H_ +#define INCLUDE_V8_MICROTASKS_QUEUE_H_ + +#include + +#include + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-microtask.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Function; + +namespace internal { +class Isolate; +class MicrotaskQueue; +} // namespace internal + +/** + * Represents the microtask queue, where microtasks are stored and processed. + * https://html.spec.whatwg.org/multipage/webappapis.html#microtask-queue + * https://html.spec.whatwg.org/multipage/webappapis.html#enqueuejob(queuename,-job,-arguments) + * https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint + * + * A MicrotaskQueue instance may be associated to multiple Contexts by passing + * it to Context::New(), and they can be detached by Context::DetachGlobal(). + * The embedder must keep the MicrotaskQueue instance alive until all associated + * Contexts are gone or detached. + * + * Use the same instance of MicrotaskQueue for all Contexts that may access each + * other synchronously. E.g. for Web embedding, use the same instance for all + * origins that share the same URL scheme and eTLD+1. + */ +class V8_EXPORT MicrotaskQueue { + public: + /** + * Creates an empty MicrotaskQueue instance. + */ + static std::unique_ptr New( + Isolate* isolate, MicrotasksPolicy policy = MicrotasksPolicy::kAuto); + + virtual ~MicrotaskQueue() = default; + + /** + * Enqueues the callback to the queue. + */ + virtual void EnqueueMicrotask(Isolate* isolate, + Local microtask) = 0; + + /** + * Enqueues the callback to the queue. + */ + virtual void EnqueueMicrotask(v8::Isolate* isolate, + MicrotaskCallback callback, + void* data = nullptr) = 0; + + /** + * Adds a callback to notify the embedder after microtasks were run. The + * callback is triggered by explicit RunMicrotasks call or automatic + * microtasks execution (see Isolate::SetMicrotasksPolicy). + * + * Callback will trigger even if microtasks were attempted to run, + * but the microtasks queue was empty and no single microtask was actually + * executed. + * + * Executing scripts inside the callback will not re-trigger microtasks and + * the callback. + */ + virtual void AddMicrotasksCompletedCallback( + MicrotasksCompletedCallbackWithData callback, void* data = nullptr) = 0; + + /** + * Removes callback that was installed by AddMicrotasksCompletedCallback. + */ + virtual void RemoveMicrotasksCompletedCallback( + MicrotasksCompletedCallbackWithData callback, void* data = nullptr) = 0; + + /** + * Runs microtasks if no microtask is running on this MicrotaskQueue instance. + */ + virtual void PerformCheckpoint(Isolate* isolate) = 0; + + /** + * Returns true if a microtask is running on this MicrotaskQueue instance. + */ + virtual bool IsRunningMicrotasks() const = 0; + + /** + * Returns the current depth of nested MicrotasksScope that has + * kRunMicrotasks. + */ + virtual int GetMicrotasksScopeDepth() const = 0; + + MicrotaskQueue(const MicrotaskQueue&) = delete; + MicrotaskQueue& operator=(const MicrotaskQueue&) = delete; + + private: + friend class internal::MicrotaskQueue; + MicrotaskQueue() = default; +}; + +/** + * This scope is used to control microtasks when MicrotasksPolicy::kScoped + * is used on Isolate. In this mode every non-primitive call to V8 should be + * done inside some MicrotasksScope. + * Microtasks are executed when topmost MicrotasksScope marked as kRunMicrotasks + * exits. + * kDoNotRunMicrotasks should be used to annotate calls not intended to trigger + * microtasks. + */ +class V8_EXPORT V8_NODISCARD MicrotasksScope { + public: + enum Type { kRunMicrotasks, kDoNotRunMicrotasks }; + + V8_DEPRECATE_SOON( + "May be incorrect if context was created with non-default microtask " + "queue") + MicrotasksScope(Isolate* isolate, Type type); + + MicrotasksScope(Local context, Type type); + MicrotasksScope(Isolate* isolate, MicrotaskQueue* microtask_queue, Type type); + ~MicrotasksScope(); + + /** + * Runs microtasks if no kRunMicrotasks scope is currently active. + */ + static void PerformCheckpoint(Isolate* isolate); + + /** + * Returns current depth of nested kRunMicrotasks scopes. + */ + static int GetCurrentDepth(Isolate* isolate); + + /** + * Returns true while microtasks are being executed. + */ + static bool IsRunningMicrotasks(Isolate* isolate); + + // Prevent copying. + MicrotasksScope(const MicrotasksScope&) = delete; + MicrotasksScope& operator=(const MicrotasksScope&) = delete; + + private: + internal::Isolate* const i_isolate_; + internal::MicrotaskQueue* const microtask_queue_; + bool run_; +}; + +} // namespace v8 + +#endif // INCLUDE_V8_MICROTASKS_QUEUE_H_ diff --git a/deps/include/v8-microtask.h b/deps/include/v8-microtask.h new file mode 100755 index 0000000..c159203 --- /dev/null +++ b/deps/include/v8-microtask.h @@ -0,0 +1,28 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_MICROTASK_H_ +#define INCLUDE_V8_MICROTASK_H_ + +namespace v8 { + +class Isolate; + +// --- Microtasks Callbacks --- +using MicrotasksCompletedCallbackWithData = void (*)(Isolate*, void*); +using MicrotaskCallback = void (*)(void* data); + +/** + * Policy for running microtasks: + * - explicit: microtasks are invoked with the + * Isolate::PerformMicrotaskCheckpoint() method; + * - scoped: microtasks invocation is controlled by MicrotasksScope objects; + * - auto: microtasks are invoked when the script call depth decrements + * to zero. + */ +enum class MicrotasksPolicy { kExplicit, kScoped, kAuto }; + +} // namespace v8 + +#endif // INCLUDE_V8_MICROTASK_H_ diff --git a/deps/include/v8-object.h b/deps/include/v8-object.h new file mode 100755 index 0000000..d7332ba --- /dev/null +++ b/deps/include/v8-object.h @@ -0,0 +1,768 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_OBJECT_H_ +#define INCLUDE_V8_OBJECT_H_ + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-maybe.h" // NOLINT(build/include_directory) +#include "v8-persistent-handle.h" // NOLINT(build/include_directory) +#include "v8-primitive.h" // NOLINT(build/include_directory) +#include "v8-traced-handle.h" // NOLINT(build/include_directory) +#include "v8-value.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Array; +class Function; +class FunctionTemplate; +template +class PropertyCallbackInfo; + +/** + * A private symbol + * + * This is an experimental feature. Use at your own risk. + */ +class V8_EXPORT Private : public Data { + public: + /** + * Returns the print name string of the private symbol, or undefined if none. + */ + Local Name() const; + + /** + * Create a private symbol. If name is not empty, it will be the description. + */ + static Local New(Isolate* isolate, + Local name = Local()); + + /** + * Retrieve a global private symbol. If a symbol with this name has not + * been retrieved in the same isolate before, it is created. + * Note that private symbols created this way are never collected, so + * they should only be used for statically fixed properties. + * Also, there is only one global name space for the names used as keys. + * To minimize the potential for clashes, use qualified names as keys, + * e.g., "Class#property". + */ + static Local ForApi(Isolate* isolate, Local name); + + V8_INLINE static Private* Cast(Data* data); + + private: + Private(); + + static void CheckCast(Data* that); +}; + +/** + * An instance of a Property Descriptor, see Ecma-262 6.2.4. + * + * Properties in a descriptor are present or absent. If you do not set + * `enumerable`, `configurable`, and `writable`, they are absent. If `value`, + * `get`, or `set` are absent, but you must specify them in the constructor, use + * empty handles. + * + * Accessors `get` and `set` must be callable or undefined if they are present. + * + * \note Only query properties if they are present, i.e., call `x()` only if + * `has_x()` returns true. + * + * \code + * // var desc = {writable: false} + * v8::PropertyDescriptor d(Local()), false); + * d.value(); // error, value not set + * if (d.has_writable()) { + * d.writable(); // false + * } + * + * // var desc = {value: undefined} + * v8::PropertyDescriptor d(v8::Undefined(isolate)); + * + * // var desc = {get: undefined} + * v8::PropertyDescriptor d(v8::Undefined(isolate), Local())); + * \endcode + */ +class V8_EXPORT PropertyDescriptor { + public: + // GenericDescriptor + PropertyDescriptor(); + + // DataDescriptor + explicit PropertyDescriptor(Local value); + + // DataDescriptor with writable property + PropertyDescriptor(Local value, bool writable); + + // AccessorDescriptor + PropertyDescriptor(Local get, Local set); + + ~PropertyDescriptor(); + + Local value() const; + bool has_value() const; + + Local get() const; + bool has_get() const; + Local set() const; + bool has_set() const; + + void set_enumerable(bool enumerable); + bool enumerable() const; + bool has_enumerable() const; + + void set_configurable(bool configurable); + bool configurable() const; + bool has_configurable() const; + + bool writable() const; + bool has_writable() const; + + struct PrivateData; + PrivateData* get_private() const { return private_; } + + PropertyDescriptor(const PropertyDescriptor&) = delete; + void operator=(const PropertyDescriptor&) = delete; + + private: + PrivateData* private_; +}; + +/** + * PropertyAttribute. + */ +enum PropertyAttribute { + /** None. **/ + None = 0, + /** ReadOnly, i.e., not writable. **/ + ReadOnly = 1 << 0, + /** DontEnum, i.e., not enumerable. **/ + DontEnum = 1 << 1, + /** DontDelete, i.e., not configurable. **/ + DontDelete = 1 << 2 +}; + +/** + * Accessor[Getter|Setter] are used as callback functions when + * setting|getting a particular property. See Object and ObjectTemplate's + * method SetAccessor. + */ +using AccessorGetterCallback = + void (*)(Local property, const PropertyCallbackInfo& info); +using AccessorNameGetterCallback = + void (*)(Local property, const PropertyCallbackInfo& info); + +using AccessorSetterCallback = void (*)(Local property, + Local value, + const PropertyCallbackInfo& info); +using AccessorNameSetterCallback = + void (*)(Local property, Local value, + const PropertyCallbackInfo& info); + +/** + * Access control specifications. + * + * Some accessors should be accessible across contexts. These + * accessors have an explicit access control parameter which specifies + * the kind of cross-context access that should be allowed. + * + * TODO(dcarney): Remove PROHIBITS_OVERWRITING as it is now unused. + */ +enum AccessControl { + DEFAULT = 0, + ALL_CAN_READ = 1, + ALL_CAN_WRITE = 1 << 1, + PROHIBITS_OVERWRITING = 1 << 2 +}; + +/** + * Property filter bits. They can be or'ed to build a composite filter. + */ +enum PropertyFilter { + ALL_PROPERTIES = 0, + ONLY_WRITABLE = 1, + ONLY_ENUMERABLE = 2, + ONLY_CONFIGURABLE = 4, + SKIP_STRINGS = 8, + SKIP_SYMBOLS = 16 +}; + +/** + * Options for marking whether callbacks may trigger JS-observable side effects. + * Side-effect-free callbacks are allowlisted during debug evaluation with + * throwOnSideEffect. It applies when calling a Function, FunctionTemplate, + * or an Accessor callback. For Interceptors, please see + * PropertyHandlerFlags's kHasNoSideEffect. + * Callbacks that only cause side effects to the receiver are allowlisted if + * invoked on receiver objects that are created within the same debug-evaluate + * call, as these objects are temporary and the side effect does not escape. + */ +enum class SideEffectType { + kHasSideEffect, + kHasNoSideEffect, + kHasSideEffectToReceiver +}; + +/** + * Keys/Properties filter enums: + * + * KeyCollectionMode limits the range of collected properties. kOwnOnly limits + * the collected properties to the given Object only. kIncludesPrototypes will + * include all keys of the objects's prototype chain as well. + */ +enum class KeyCollectionMode { kOwnOnly, kIncludePrototypes }; + +/** + * kIncludesIndices allows for integer indices to be collected, while + * kSkipIndices will exclude integer indices from being collected. + */ +enum class IndexFilter { kIncludeIndices, kSkipIndices }; + +/** + * kConvertToString will convert integer indices to strings. + * kKeepNumbers will return numbers for integer indices. + */ +enum class KeyConversionMode { kConvertToString, kKeepNumbers, kNoNumbers }; + +/** + * Integrity level for objects. + */ +enum class IntegrityLevel { kFrozen, kSealed }; + +/** + * A JavaScript object (ECMA-262, 4.3.3) + */ +class V8_EXPORT Object : public Value { + public: + /** + * Set only return Just(true) or Empty(), so if it should never fail, use + * result.Check(). + */ + V8_WARN_UNUSED_RESULT Maybe Set(Local context, + Local key, Local value); + + V8_WARN_UNUSED_RESULT Maybe Set(Local context, uint32_t index, + Local value); + + // Implements CreateDataProperty (ECMA-262, 7.3.4). + // + // Defines a configurable, writable, enumerable property with the given value + // on the object unless the property already exists and is not configurable + // or the object is not extensible. + // + // Returns true on success. + V8_WARN_UNUSED_RESULT Maybe CreateDataProperty(Local context, + Local key, + Local value); + V8_WARN_UNUSED_RESULT Maybe CreateDataProperty(Local context, + uint32_t index, + Local value); + + // Implements DefineOwnProperty. + // + // In general, CreateDataProperty will be faster, however, does not allow + // for specifying attributes. + // + // Returns true on success. + V8_WARN_UNUSED_RESULT Maybe DefineOwnProperty( + Local context, Local key, Local value, + PropertyAttribute attributes = None); + + // Implements Object.DefineProperty(O, P, Attributes), see Ecma-262 19.1.2.4. + // + // The defineProperty function is used to add an own property or + // update the attributes of an existing own property of an object. + // + // Both data and accessor descriptors can be used. + // + // In general, CreateDataProperty is faster, however, does not allow + // for specifying attributes or an accessor descriptor. + // + // The PropertyDescriptor can change when redefining a property. + // + // Returns true on success. + V8_WARN_UNUSED_RESULT Maybe DefineProperty( + Local context, Local key, PropertyDescriptor& descriptor); + + V8_WARN_UNUSED_RESULT MaybeLocal Get(Local context, + Local key); + + V8_WARN_UNUSED_RESULT MaybeLocal Get(Local context, + uint32_t index); + + /** + * Gets the property attributes of a property which can be None or + * any combination of ReadOnly, DontEnum and DontDelete. Returns + * None when the property doesn't exist. + */ + V8_WARN_UNUSED_RESULT Maybe GetPropertyAttributes( + Local context, Local key); + + /** + * Returns Object.getOwnPropertyDescriptor as per ES2016 section 19.1.2.6. + */ + V8_WARN_UNUSED_RESULT MaybeLocal GetOwnPropertyDescriptor( + Local context, Local key); + + /** + * Object::Has() calls the abstract operation HasProperty(O, P) described + * in ECMA-262, 7.3.10. Has() returns + * true, if the object has the property, either own or on the prototype chain. + * Interceptors, i.e., PropertyQueryCallbacks, are called if present. + * + * Has() has the same side effects as JavaScript's `variable in object`. + * For example, calling Has() on a revoked proxy will throw an exception. + * + * \note Has() converts the key to a name, which possibly calls back into + * JavaScript. + * + * See also v8::Object::HasOwnProperty() and + * v8::Object::HasRealNamedProperty(). + */ + V8_WARN_UNUSED_RESULT Maybe Has(Local context, + Local key); + + V8_WARN_UNUSED_RESULT Maybe Delete(Local context, + Local key); + + V8_WARN_UNUSED_RESULT Maybe Has(Local context, uint32_t index); + + V8_WARN_UNUSED_RESULT Maybe Delete(Local context, + uint32_t index); + + /** + * Note: SideEffectType affects the getter only, not the setter. + */ + V8_WARN_UNUSED_RESULT Maybe SetAccessor( + Local context, Local name, + AccessorNameGetterCallback getter, + AccessorNameSetterCallback setter = nullptr, + MaybeLocal data = MaybeLocal(), + AccessControl settings = DEFAULT, PropertyAttribute attribute = None, + SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect, + SideEffectType setter_side_effect_type = SideEffectType::kHasSideEffect); + + void SetAccessorProperty(Local name, Local getter, + Local setter = Local(), + PropertyAttribute attribute = None, + AccessControl settings = DEFAULT); + + /** + * Sets a native data property like Template::SetNativeDataProperty, but + * this method sets on this object directly. + */ + V8_WARN_UNUSED_RESULT Maybe SetNativeDataProperty( + Local context, Local name, + AccessorNameGetterCallback getter, + AccessorNameSetterCallback setter = nullptr, + Local data = Local(), PropertyAttribute attributes = None, + SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect, + SideEffectType setter_side_effect_type = SideEffectType::kHasSideEffect); + + /** + * Attempts to create a property with the given name which behaves like a data + * property, except that the provided getter is invoked (and provided with the + * data value) to supply its value the first time it is read. After the + * property is accessed once, it is replaced with an ordinary data property. + * + * Analogous to Template::SetLazyDataProperty. + */ + V8_WARN_UNUSED_RESULT Maybe SetLazyDataProperty( + Local context, Local name, + AccessorNameGetterCallback getter, Local data = Local(), + PropertyAttribute attributes = None, + SideEffectType getter_side_effect_type = SideEffectType::kHasSideEffect, + SideEffectType setter_side_effect_type = SideEffectType::kHasSideEffect); + + /** + * Functionality for private properties. + * This is an experimental feature, use at your own risk. + * Note: Private properties are not inherited. Do not rely on this, since it + * may change. + */ + Maybe HasPrivate(Local context, Local key); + Maybe SetPrivate(Local context, Local key, + Local value); + Maybe DeletePrivate(Local context, Local key); + MaybeLocal GetPrivate(Local context, Local key); + + /** + * Returns an array containing the names of the enumerable properties + * of this object, including properties from prototype objects. The + * array returned by this method contains the same values as would + * be enumerated by a for-in statement over this object. + */ + V8_WARN_UNUSED_RESULT MaybeLocal GetPropertyNames( + Local context); + V8_WARN_UNUSED_RESULT MaybeLocal GetPropertyNames( + Local context, KeyCollectionMode mode, + PropertyFilter property_filter, IndexFilter index_filter, + KeyConversionMode key_conversion = KeyConversionMode::kKeepNumbers); + + /** + * This function has the same functionality as GetPropertyNames but + * the returned array doesn't contain the names of properties from + * prototype objects. + */ + V8_WARN_UNUSED_RESULT MaybeLocal GetOwnPropertyNames( + Local context); + + /** + * Returns an array containing the names of the filtered properties + * of this object, including properties from prototype objects. The + * array returned by this method contains the same values as would + * be enumerated by a for-in statement over this object. + */ + V8_WARN_UNUSED_RESULT MaybeLocal GetOwnPropertyNames( + Local context, PropertyFilter filter, + KeyConversionMode key_conversion = KeyConversionMode::kKeepNumbers); + + /** + * Get the prototype object. This does not skip objects marked to + * be skipped by __proto__ and it does not consult the security + * handler. + */ + Local GetPrototype(); + + /** + * Set the prototype object. This does not skip objects marked to + * be skipped by __proto__ and it does not consult the security + * handler. + */ + V8_WARN_UNUSED_RESULT Maybe SetPrototype(Local context, + Local prototype); + + /** + * Finds an instance of the given function template in the prototype + * chain. + */ + Local FindInstanceInPrototypeChain(Local tmpl); + + /** + * Call builtin Object.prototype.toString on this object. + * This is different from Value::ToString() that may call + * user-defined toString function. This one does not. + */ + V8_WARN_UNUSED_RESULT MaybeLocal ObjectProtoToString( + Local context); + + /** + * Returns the name of the function invoked as a constructor for this object. + */ + Local GetConstructorName(); + + /** + * Sets the integrity level of the object. + */ + Maybe SetIntegrityLevel(Local context, IntegrityLevel level); + + /** Gets the number of internal fields for this Object. */ + int InternalFieldCount() const; + + /** Same as above, but works for PersistentBase. */ + V8_INLINE static int InternalFieldCount( + const PersistentBase& object) { + return object.val_->InternalFieldCount(); + } + + /** Same as above, but works for BasicTracedReference. */ + V8_INLINE static int InternalFieldCount( + const BasicTracedReference& object) { + return object->InternalFieldCount(); + } + + /** Gets the value from an internal field. */ + V8_INLINE Local GetInternalField(int index); + + /** Sets the value in an internal field. */ + void SetInternalField(int index, Local value); + + /** + * Gets a 2-byte-aligned native pointer from an internal field. This field + * must have been set by SetAlignedPointerInInternalField, everything else + * leads to undefined behavior. + */ + V8_INLINE void* GetAlignedPointerFromInternalField(int index); + + /** Same as above, but works for PersistentBase. */ + V8_INLINE static void* GetAlignedPointerFromInternalField( + const PersistentBase& object, int index) { + return object.val_->GetAlignedPointerFromInternalField(index); + } + + /** Same as above, but works for TracedReference. */ + V8_INLINE static void* GetAlignedPointerFromInternalField( + const BasicTracedReference& object, int index) { + return object->GetAlignedPointerFromInternalField(index); + } + + /** + * Sets a 2-byte-aligned native pointer in an internal field. To retrieve such + * a field, GetAlignedPointerFromInternalField must be used, everything else + * leads to undefined behavior. + */ + void SetAlignedPointerInInternalField(int index, void* value); + void SetAlignedPointerInInternalFields(int argc, int indices[], + void* values[]); + + /** + * HasOwnProperty() is like JavaScript's Object.prototype.hasOwnProperty(). + * + * See also v8::Object::Has() and v8::Object::HasRealNamedProperty(). + */ + V8_WARN_UNUSED_RESULT Maybe HasOwnProperty(Local context, + Local key); + V8_WARN_UNUSED_RESULT Maybe HasOwnProperty(Local context, + uint32_t index); + /** + * Use HasRealNamedProperty() if you want to check if an object has an own + * property without causing side effects, i.e., without calling interceptors. + * + * This function is similar to v8::Object::HasOwnProperty(), but it does not + * call interceptors. + * + * \note Consider using non-masking interceptors, i.e., the interceptors are + * not called if the receiver has the real named property. See + * `v8::PropertyHandlerFlags::kNonMasking`. + * + * See also v8::Object::Has(). + */ + V8_WARN_UNUSED_RESULT Maybe HasRealNamedProperty(Local context, + Local key); + V8_WARN_UNUSED_RESULT Maybe HasRealIndexedProperty( + Local context, uint32_t index); + V8_WARN_UNUSED_RESULT Maybe HasRealNamedCallbackProperty( + Local context, Local key); + + /** + * If result.IsEmpty() no real property was located in the prototype chain. + * This means interceptors in the prototype chain are not called. + */ + V8_WARN_UNUSED_RESULT MaybeLocal GetRealNamedPropertyInPrototypeChain( + Local context, Local key); + + /** + * Gets the property attributes of a real property in the prototype chain, + * which can be None or any combination of ReadOnly, DontEnum and DontDelete. + * Interceptors in the prototype chain are not called. + */ + V8_WARN_UNUSED_RESULT Maybe + GetRealNamedPropertyAttributesInPrototypeChain(Local context, + Local key); + + /** + * If result.IsEmpty() no real property was located on the object or + * in the prototype chain. + * This means interceptors in the prototype chain are not called. + */ + V8_WARN_UNUSED_RESULT MaybeLocal GetRealNamedProperty( + Local context, Local key); + + /** + * Gets the property attributes of a real property which can be + * None or any combination of ReadOnly, DontEnum and DontDelete. + * Interceptors in the prototype chain are not called. + */ + V8_WARN_UNUSED_RESULT Maybe GetRealNamedPropertyAttributes( + Local context, Local key); + + /** Tests for a named lookup interceptor.*/ + bool HasNamedLookupInterceptor() const; + + /** Tests for an index lookup interceptor.*/ + bool HasIndexedLookupInterceptor() const; + + /** + * Returns the identity hash for this object. The current implementation + * uses a hidden property on the object to store the identity hash. + * + * The return value will never be 0. Also, it is not guaranteed to be + * unique. + */ + int GetIdentityHash(); + + /** + * Clone this object with a fast but shallow copy. Values will point + * to the same values as the original object. + */ + // TODO(dcarney): take an isolate and optionally bail out? + Local Clone(); + + /** + * Returns the context in which the object was created. + */ + MaybeLocal GetCreationContext(); + + /** + * Shortcut for GetCreationContext().ToLocalChecked(). + **/ + Local GetCreationContextChecked(); + + /** Same as above, but works for Persistents */ + V8_INLINE static MaybeLocal GetCreationContext( + const PersistentBase& object) { + return object.val_->GetCreationContext(); + } + + /** + * Checks whether a callback is set by the + * ObjectTemplate::SetCallAsFunctionHandler method. + * When an Object is callable this method returns true. + */ + bool IsCallable() const; + + /** + * True if this object is a constructor. + */ + bool IsConstructor() const; + + /** + * True if this object can carry information relevant to the embedder in its + * embedder fields, false otherwise. This is generally true for objects + * constructed through function templates but also holds for other types where + * V8 automatically adds internal fields at compile time, such as e.g. + * v8::ArrayBuffer. + */ + bool IsApiWrapper() const; + + /** + * True if this object was created from an object template which was marked + * as undetectable. See v8::ObjectTemplate::MarkAsUndetectable for more + * information. + */ + bool IsUndetectable() const; + + /** + * Call an Object as a function if a callback is set by the + * ObjectTemplate::SetCallAsFunctionHandler method. + */ + V8_WARN_UNUSED_RESULT MaybeLocal CallAsFunction(Local context, + Local recv, + int argc, + Local argv[]); + + /** + * Call an Object as a constructor if a callback is set by the + * ObjectTemplate::SetCallAsFunctionHandler method. + * Note: This method behaves like the Function::NewInstance method. + */ + V8_WARN_UNUSED_RESULT MaybeLocal CallAsConstructor( + Local context, int argc, Local argv[]); + + /** + * Return the isolate to which the Object belongs to. + */ + Isolate* GetIsolate(); + + /** + * If this object is a Set, Map, WeakSet or WeakMap, this returns a + * representation of the elements of this object as an array. + * If this object is a SetIterator or MapIterator, this returns all + * elements of the underlying collection, starting at the iterator's current + * position. + * For other types, this will return an empty MaybeLocal (without + * scheduling an exception). + */ + MaybeLocal PreviewEntries(bool* is_key_value); + + static Local New(Isolate* isolate); + + /** + * Creates a JavaScript object with the given properties, and + * a the given prototype_or_null (which can be any JavaScript + * value, and if it's null, the newly created object won't have + * a prototype at all). This is similar to Object.create(). + * All properties will be created as enumerable, configurable + * and writable properties. + */ + static Local New(Isolate* isolate, Local prototype_or_null, + Local* names, Local* values, + size_t length); + + V8_INLINE static Object* Cast(Value* obj); + + /** + * Support for TC39 "dynamic code brand checks" proposal. + * + * This API allows to query whether an object was constructed from a + * "code like" ObjectTemplate. + * + * See also: v8::ObjectTemplate::SetCodeLike + */ + bool IsCodeLike(Isolate* isolate) const; + + private: + Object(); + static void CheckCast(Value* obj); + Local SlowGetInternalField(int index); + void* SlowGetAlignedPointerFromInternalField(int index); +}; + +// --- Implementation --- + +Local Object::GetInternalField(int index) { +#ifndef V8_ENABLE_CHECKS + using A = internal::Address; + using I = internal::Internals; + A obj = *reinterpret_cast(this); + // Fast path: If the object is a plain JSObject, which is the common case, we + // know where to find the internal fields and can return the value directly. + int instance_type = I::GetInstanceType(obj); + if (I::CanHaveInternalField(instance_type)) { + int offset = I::kJSObjectHeaderSize + (I::kEmbedderDataSlotSize * index); + A value = I::ReadRawField(obj, offset); +#ifdef V8_COMPRESS_POINTERS + // We read the full pointer value and then decompress it in order to avoid + // dealing with potential endiannes issues. + value = I::DecompressTaggedAnyField(obj, static_cast(value)); +#endif + internal::Isolate* isolate = + internal::IsolateFromNeverReadOnlySpaceObject(obj); + A* result = HandleScope::CreateHandle(isolate, value); + return Local(reinterpret_cast(result)); + } +#endif + return SlowGetInternalField(index); +} + +void* Object::GetAlignedPointerFromInternalField(int index) { +#if !defined(V8_ENABLE_CHECKS) + using A = internal::Address; + using I = internal::Internals; + A obj = *reinterpret_cast(this); + // Fast path: If the object is a plain JSObject, which is the common case, we + // know where to find the internal fields and can return the value directly. + auto instance_type = I::GetInstanceType(obj); + if (I::CanHaveInternalField(instance_type)) { + int offset = I::kJSObjectHeaderSize + (I::kEmbedderDataSlotSize * index) + + I::kEmbedderDataSlotExternalPointerOffset; + Isolate* isolate = I::GetIsolateForSandbox(obj); + A value = + I::ReadExternalPointerField( + isolate, obj, offset); + return reinterpret_cast(value); + } +#endif + return SlowGetAlignedPointerFromInternalField(index); +} + +Private* Private::Cast(Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return reinterpret_cast(data); +} + +Object* Object::Cast(v8::Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); +} + +} // namespace v8 + +#endif // INCLUDE_V8_OBJECT_H_ diff --git a/deps/include/v8-persistent-handle.h b/deps/include/v8-persistent-handle.h new file mode 100755 index 0000000..4fe7986 --- /dev/null +++ b/deps/include/v8-persistent-handle.h @@ -0,0 +1,588 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_PERSISTENT_HANDLE_H_ +#define INCLUDE_V8_PERSISTENT_HANDLE_H_ + +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-weak-callback-info.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Isolate; +template +class PersistentValueMapBase; +template +class PersistentValueVector; +template +class Global; +template +class PersistentBase; +template +class PersistentValueMap; +class Value; + +namespace api_internal { +V8_EXPORT Value* Eternalize(v8::Isolate* isolate, Value* handle); +V8_EXPORT internal::Address* CopyGlobalReference(internal::Address* from); +V8_EXPORT void DisposeGlobal(internal::Address* global_handle); +V8_EXPORT void MakeWeak(internal::Address** location_addr); +V8_EXPORT void* ClearWeak(internal::Address* location); +V8_EXPORT void AnnotateStrongRetainer(internal::Address* location, + const char* label); +V8_EXPORT internal::Address* GlobalizeReference(internal::Isolate* isolate, + internal::Address* handle); +V8_EXPORT void MoveGlobalReference(internal::Address** from, + internal::Address** to); +} // namespace api_internal + +/** + * Eternal handles are set-once handles that live for the lifetime of the + * isolate. + */ +template +class Eternal { + public: + V8_INLINE Eternal() : val_(nullptr) {} + template + V8_INLINE Eternal(Isolate* isolate, Local handle) : val_(nullptr) { + Set(isolate, handle); + } + // Can only be safely called if already set. + V8_INLINE Local Get(Isolate* isolate) const { + // The eternal handle will never go away, so as with the roots, we don't + // even need to open a handle. + return Local(val_); + } + + V8_INLINE bool IsEmpty() const { return val_ == nullptr; } + + template + void Set(Isolate* isolate, Local handle) { + static_assert(std::is_base_of::value, "type check"); + val_ = reinterpret_cast( + api_internal::Eternalize(isolate, reinterpret_cast(*handle))); + } + + private: + T* val_; +}; + +namespace api_internal { +V8_EXPORT void MakeWeak(internal::Address* location, void* data, + WeakCallbackInfo::Callback weak_callback, + WeakCallbackType type); +} // namespace api_internal + +/** + * An object reference that is independent of any handle scope. Where + * a Local handle only lives as long as the HandleScope in which it was + * allocated, a PersistentBase handle remains valid until it is explicitly + * disposed using Reset(). + * + * A persistent handle contains a reference to a storage cell within + * the V8 engine which holds an object value and which is updated by + * the garbage collector whenever the object is moved. A new storage + * cell can be created using the constructor or PersistentBase::Reset and + * existing handles can be disposed using PersistentBase::Reset. + * + */ +template +class PersistentBase { + public: + /** + * If non-empty, destroy the underlying storage cell + * IsEmpty() will return true after this call. + */ + V8_INLINE void Reset(); + + /** + * If non-empty, destroy the underlying storage cell + * and create a new one with the contents of other if other is non empty + */ + template + V8_INLINE void Reset(Isolate* isolate, const Local& other); + + /** + * If non-empty, destroy the underlying storage cell + * and create a new one with the contents of other if other is non empty + */ + template + V8_INLINE void Reset(Isolate* isolate, const PersistentBase& other); + + V8_INLINE bool IsEmpty() const { return val_ == nullptr; } + V8_INLINE void Empty() { val_ = 0; } + + V8_INLINE Local Get(Isolate* isolate) const { + return Local::New(isolate, *this); + } + + template + V8_INLINE bool operator==(const PersistentBase& that) const { + internal::Address* a = reinterpret_cast(this->val_); + internal::Address* b = reinterpret_cast(that.val_); + if (a == nullptr) return b == nullptr; + if (b == nullptr) return false; + return *a == *b; + } + + template + V8_INLINE bool operator==(const Local& that) const { + internal::Address* a = reinterpret_cast(this->val_); + internal::Address* b = reinterpret_cast(that.val_); + if (a == nullptr) return b == nullptr; + if (b == nullptr) return false; + return *a == *b; + } + + template + V8_INLINE bool operator!=(const PersistentBase& that) const { + return !operator==(that); + } + + template + V8_INLINE bool operator!=(const Local& that) const { + return !operator==(that); + } + + /** + * Install a finalization callback on this object. + * NOTE: There is no guarantee as to *when* or even *if* the callback is + * invoked. The invocation is performed solely on a best effort basis. + * As always, GC-based finalization should *not* be relied upon for any + * critical form of resource management! + * + * The callback is supposed to reset the handle. No further V8 API may be + * called in this callback. In case additional work involving V8 needs to be + * done, a second callback can be scheduled using + * WeakCallbackInfo::SetSecondPassCallback. + */ + template + V8_INLINE void SetWeak(P* parameter, + typename WeakCallbackInfo

::Callback callback, + WeakCallbackType type); + + /** + * Turns this handle into a weak phantom handle without finalization callback. + * The handle will be reset automatically when the garbage collector detects + * that the object is no longer reachable. + */ + V8_INLINE void SetWeak(); + + template + V8_INLINE P* ClearWeak(); + + // TODO(dcarney): remove this. + V8_INLINE void ClearWeak() { ClearWeak(); } + + /** + * Annotates the strong handle with the given label, which is then used by the + * heap snapshot generator as a name of the edge from the root to the handle. + * The function does not take ownership of the label and assumes that the + * label is valid as long as the handle is valid. + */ + V8_INLINE void AnnotateStrongRetainer(const char* label); + + /** Returns true if the handle's reference is weak. */ + V8_INLINE bool IsWeak() const; + + /** + * Assigns a wrapper class ID to the handle. + */ + V8_INLINE void SetWrapperClassId(uint16_t class_id); + + /** + * Returns the class ID previously assigned to this handle or 0 if no class ID + * was previously assigned. + */ + V8_INLINE uint16_t WrapperClassId() const; + + PersistentBase(const PersistentBase& other) = delete; + void operator=(const PersistentBase&) = delete; + + private: + friend class Isolate; + friend class Utils; + template + friend class Local; + template + friend class Persistent; + template + friend class Global; + template + friend class PersistentBase; + template + friend class ReturnValue; + template + friend class PersistentValueMapBase; + template + friend class PersistentValueVector; + friend class Object; + + explicit V8_INLINE PersistentBase(T* val) : val_(val) {} + V8_INLINE static T* New(Isolate* isolate, T* that); + + T* val_; +}; + +/** + * Default traits for Persistent. This class does not allow + * use of the copy constructor or assignment operator. + * At present kResetInDestructor is not set, but that will change in a future + * version. + */ +template +class NonCopyablePersistentTraits { + public: + using NonCopyablePersistent = Persistent>; + static const bool kResetInDestructor = false; + template + V8_INLINE static void Copy(const Persistent& source, + NonCopyablePersistent* dest) { + static_assert(sizeof(S) < 0, + "NonCopyablePersistentTraits::Copy is not instantiable"); + } +}; + +/** + * Helper class traits to allow copying and assignment of Persistent. + * This will clone the contents of storage cell, but not any of the flags, etc. + */ +template +struct V8_DEPRECATED("Use v8::Global instead") CopyablePersistentTraits { + using CopyablePersistent = Persistent>; + static const bool kResetInDestructor = true; + template + static V8_INLINE void Copy(const Persistent& source, + CopyablePersistent* dest) { + // do nothing, just allow copy + } +}; + +/** + * A PersistentBase which allows copy and assignment. + * + * Copy, assignment and destructor behavior is controlled by the traits + * class M. + * + * Note: Persistent class hierarchy is subject to future changes. + */ +template +class Persistent : public PersistentBase { + public: + /** + * A Persistent with no storage cell. + */ + V8_INLINE Persistent() : PersistentBase(nullptr) {} + /** + * Construct a Persistent from a Local. + * When the Local is non-empty, a new storage cell is created + * pointing to the same object, and no flags are set. + */ + template + V8_INLINE Persistent(Isolate* isolate, Local that) + : PersistentBase(PersistentBase::New(isolate, *that)) { + static_assert(std::is_base_of::value, "type check"); + } + /** + * Construct a Persistent from a Persistent. + * When the Persistent is non-empty, a new storage cell is created + * pointing to the same object, and no flags are set. + */ + template + V8_INLINE Persistent(Isolate* isolate, const Persistent& that) + : PersistentBase(PersistentBase::New(isolate, *that)) { + static_assert(std::is_base_of::value, "type check"); + } + /** + * The copy constructors and assignment operator create a Persistent + * exactly as the Persistent constructor, but the Copy function from the + * traits class is called, allowing the setting of flags based on the + * copied Persistent. + */ + V8_INLINE Persistent(const Persistent& that) : PersistentBase(nullptr) { + Copy(that); + } + template + V8_INLINE Persistent(const Persistent& that) : PersistentBase(0) { + Copy(that); + } + V8_INLINE Persistent& operator=(const Persistent& that) { + Copy(that); + return *this; + } + template + V8_INLINE Persistent& operator=(const Persistent& that) { + Copy(that); + return *this; + } + /** + * The destructor will dispose the Persistent based on the + * kResetInDestructor flags in the traits class. Since not calling dispose + * can result in a memory leak, it is recommended to always set this flag. + */ + V8_INLINE ~Persistent() { + if (M::kResetInDestructor) this->Reset(); + } + + // TODO(dcarney): this is pretty useless, fix or remove + template + V8_INLINE static Persistent& Cast(const Persistent& that) { +#ifdef V8_ENABLE_CHECKS + // If we're going to perform the type check then we have to check + // that the handle isn't empty before doing the checked cast. + if (!that.IsEmpty()) T::Cast(*that); +#endif + return reinterpret_cast&>(const_cast&>(that)); + } + + // TODO(dcarney): this is pretty useless, fix or remove + template + V8_INLINE Persistent& As() const { + return Persistent::Cast(*this); + } + + private: + friend class Isolate; + friend class Utils; + template + friend class Local; + template + friend class Persistent; + template + friend class ReturnValue; + + explicit V8_INLINE Persistent(T* that) : PersistentBase(that) {} + V8_INLINE T* operator*() const { return this->val_; } + template + V8_INLINE void Copy(const Persistent& that); +}; + +/** + * A PersistentBase which has move semantics. + * + * Note: Persistent class hierarchy is subject to future changes. + */ +template +class Global : public PersistentBase { + public: + /** + * A Global with no storage cell. + */ + V8_INLINE Global() : PersistentBase(nullptr) {} + + /** + * Construct a Global from a Local. + * When the Local is non-empty, a new storage cell is created + * pointing to the same object, and no flags are set. + */ + template + V8_INLINE Global(Isolate* isolate, Local that) + : PersistentBase(PersistentBase::New(isolate, *that)) { + static_assert(std::is_base_of::value, "type check"); + } + + /** + * Construct a Global from a PersistentBase. + * When the Persistent is non-empty, a new storage cell is created + * pointing to the same object, and no flags are set. + */ + template + V8_INLINE Global(Isolate* isolate, const PersistentBase& that) + : PersistentBase(PersistentBase::New(isolate, that.val_)) { + static_assert(std::is_base_of::value, "type check"); + } + + /** + * Move constructor. + */ + V8_INLINE Global(Global&& other); + + V8_INLINE ~Global() { this->Reset(); } + + /** + * Move via assignment. + */ + template + V8_INLINE Global& operator=(Global&& rhs); + + /** + * Pass allows returning uniques from functions, etc. + */ + Global Pass() { return static_cast(*this); } + + /* + * For compatibility with Chromium's base::Bind (base::Passed). + */ + using MoveOnlyTypeForCPP03 = void; + + Global(const Global&) = delete; + void operator=(const Global&) = delete; + + private: + template + friend class ReturnValue; + V8_INLINE T* operator*() const { return this->val_; } +}; + +// UniquePersistent is an alias for Global for historical reason. +template +using UniquePersistent = Global; + +/** + * Interface for iterating through all the persistent handles in the heap. + */ +class V8_EXPORT PersistentHandleVisitor { + public: + virtual ~PersistentHandleVisitor() = default; + virtual void VisitPersistentHandle(Persistent* value, + uint16_t class_id) {} +}; + +template +T* PersistentBase::New(Isolate* isolate, T* that) { + if (that == nullptr) return nullptr; + internal::Address* p = reinterpret_cast(that); + return reinterpret_cast(api_internal::GlobalizeReference( + reinterpret_cast(isolate), p)); +} + +template +template +void Persistent::Copy(const Persistent& that) { + static_assert(std::is_base_of::value, "type check"); + this->Reset(); + if (that.IsEmpty()) return; + internal::Address* p = reinterpret_cast(that.val_); + this->val_ = reinterpret_cast(api_internal::CopyGlobalReference(p)); + M::Copy(that, this); +} + +template +bool PersistentBase::IsWeak() const { + using I = internal::Internals; + if (this->IsEmpty()) return false; + return I::GetNodeState(reinterpret_cast(this->val_)) == + I::kNodeStateIsWeakValue; +} + +template +void PersistentBase::Reset() { + if (this->IsEmpty()) return; + api_internal::DisposeGlobal(reinterpret_cast(this->val_)); + val_ = nullptr; +} + +/** + * If non-empty, destroy the underlying storage cell + * and create a new one with the contents of other if other is non empty + */ +template +template +void PersistentBase::Reset(Isolate* isolate, const Local& other) { + static_assert(std::is_base_of::value, "type check"); + Reset(); + if (other.IsEmpty()) return; + this->val_ = New(isolate, other.val_); +} + +/** + * If non-empty, destroy the underlying storage cell + * and create a new one with the contents of other if other is non empty + */ +template +template +void PersistentBase::Reset(Isolate* isolate, + const PersistentBase& other) { + static_assert(std::is_base_of::value, "type check"); + Reset(); + if (other.IsEmpty()) return; + this->val_ = New(isolate, other.val_); +} + +template +template +V8_INLINE void PersistentBase::SetWeak( + P* parameter, typename WeakCallbackInfo

::Callback callback, + WeakCallbackType type) { + using Callback = WeakCallbackInfo::Callback; +#if (__GNUC__ >= 8) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + api_internal::MakeWeak(reinterpret_cast(this->val_), + parameter, reinterpret_cast(callback), type); +#if (__GNUC__ >= 8) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif +} + +template +void PersistentBase::SetWeak() { + api_internal::MakeWeak(reinterpret_cast(&this->val_)); +} + +template +template +P* PersistentBase::ClearWeak() { + return reinterpret_cast(api_internal::ClearWeak( + reinterpret_cast(this->val_))); +} + +template +void PersistentBase::AnnotateStrongRetainer(const char* label) { + api_internal::AnnotateStrongRetainer( + reinterpret_cast(this->val_), label); +} + +template +void PersistentBase::SetWrapperClassId(uint16_t class_id) { + using I = internal::Internals; + if (this->IsEmpty()) return; + internal::Address* obj = reinterpret_cast(this->val_); + uint8_t* addr = reinterpret_cast(obj) + I::kNodeClassIdOffset; + *reinterpret_cast(addr) = class_id; +} + +template +uint16_t PersistentBase::WrapperClassId() const { + using I = internal::Internals; + if (this->IsEmpty()) return 0; + internal::Address* obj = reinterpret_cast(this->val_); + uint8_t* addr = reinterpret_cast(obj) + I::kNodeClassIdOffset; + return *reinterpret_cast(addr); +} + +template +Global::Global(Global&& other) : PersistentBase(other.val_) { + if (other.val_ != nullptr) { + api_internal::MoveGlobalReference( + reinterpret_cast(&other.val_), + reinterpret_cast(&this->val_)); + other.val_ = nullptr; + } +} + +template +template +Global& Global::operator=(Global&& rhs) { + static_assert(std::is_base_of::value, "type check"); + if (this != &rhs) { + this->Reset(); + if (rhs.val_ != nullptr) { + this->val_ = rhs.val_; + api_internal::MoveGlobalReference( + reinterpret_cast(&rhs.val_), + reinterpret_cast(&this->val_)); + rhs.val_ = nullptr; + } + } + return *this; +} + +} // namespace v8 + +#endif // INCLUDE_V8_PERSISTENT_HANDLE_H_ diff --git a/deps/include/v8-platform.h b/deps/include/v8-platform.h new file mode 100755 index 0000000..32a82f8 --- /dev/null +++ b/deps/include/v8-platform.h @@ -0,0 +1,1122 @@ +// Copyright 2013 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_V8_PLATFORM_H_ +#define V8_V8_PLATFORM_H_ + +#include +#include +#include // For abort. +#include +#include + +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Isolate; + +// Valid priorities supported by the task scheduling infrastructure. +enum class TaskPriority : uint8_t { + /** + * Best effort tasks are not critical for performance of the application. The + * platform implementation should preempt such tasks if higher priority tasks + * arrive. + */ + kBestEffort, + /** + * User visible tasks are long running background tasks that will + * improve performance and memory usage of the application upon completion. + * Example: background compilation and garbage collection. + */ + kUserVisible, + /** + * User blocking tasks are highest priority tasks that block the execution + * thread (e.g. major garbage collection). They must be finished as soon as + * possible. + */ + kUserBlocking, +}; + +/** + * A Task represents a unit of work. + */ +class Task { + public: + virtual ~Task() = default; + + virtual void Run() = 0; +}; + +/** + * An IdleTask represents a unit of work to be performed in idle time. + * The Run method is invoked with an argument that specifies the deadline in + * seconds returned by MonotonicallyIncreasingTime(). + * The idle task is expected to complete by this deadline. + */ +class IdleTask { + public: + virtual ~IdleTask() = default; + virtual void Run(double deadline_in_seconds) = 0; +}; + +/** + * A TaskRunner allows scheduling of tasks. The TaskRunner may still be used to + * post tasks after the isolate gets destructed, but these tasks may not get + * executed anymore. All tasks posted to a given TaskRunner will be invoked in + * sequence. Tasks can be posted from any thread. + */ +class TaskRunner { + public: + /** + * Schedules a task to be invoked by this TaskRunner. The TaskRunner + * implementation takes ownership of |task|. + */ + virtual void PostTask(std::unique_ptr task) = 0; + + /** + * Schedules a task to be invoked by this TaskRunner. The TaskRunner + * implementation takes ownership of |task|. The |task| cannot be nested + * within other task executions. + * + * Tasks which shouldn't be interleaved with JS execution must be posted with + * |PostNonNestableTask| or |PostNonNestableDelayedTask|. This is because the + * embedder may process tasks in a callback which is called during JS + * execution. + * + * In particular, tasks which execute JS must be non-nestable, since JS + * execution is not allowed to nest. + * + * Requires that |TaskRunner::NonNestableTasksEnabled()| is true. + */ + virtual void PostNonNestableTask(std::unique_ptr task) {} + + /** + * Schedules a task to be invoked by this TaskRunner. The task is scheduled + * after the given number of seconds |delay_in_seconds|. The TaskRunner + * implementation takes ownership of |task|. + */ + virtual void PostDelayedTask(std::unique_ptr task, + double delay_in_seconds) = 0; + + /** + * Schedules a task to be invoked by this TaskRunner. The task is scheduled + * after the given number of seconds |delay_in_seconds|. The TaskRunner + * implementation takes ownership of |task|. The |task| cannot be nested + * within other task executions. + * + * Tasks which shouldn't be interleaved with JS execution must be posted with + * |PostNonNestableTask| or |PostNonNestableDelayedTask|. This is because the + * embedder may process tasks in a callback which is called during JS + * execution. + * + * In particular, tasks which execute JS must be non-nestable, since JS + * execution is not allowed to nest. + * + * Requires that |TaskRunner::NonNestableDelayedTasksEnabled()| is true. + */ + virtual void PostNonNestableDelayedTask(std::unique_ptr task, + double delay_in_seconds) {} + + /** + * Schedules an idle task to be invoked by this TaskRunner. The task is + * scheduled when the embedder is idle. Requires that + * |TaskRunner::IdleTasksEnabled()| is true. Idle tasks may be reordered + * relative to other task types and may be starved for an arbitrarily long + * time if no idle time is available. The TaskRunner implementation takes + * ownership of |task|. + */ + virtual void PostIdleTask(std::unique_ptr task) = 0; + + /** + * Returns true if idle tasks are enabled for this TaskRunner. + */ + virtual bool IdleTasksEnabled() = 0; + + /** + * Returns true if non-nestable tasks are enabled for this TaskRunner. + */ + virtual bool NonNestableTasksEnabled() const { return false; } + + /** + * Returns true if non-nestable delayed tasks are enabled for this TaskRunner. + */ + virtual bool NonNestableDelayedTasksEnabled() const { return false; } + + TaskRunner() = default; + virtual ~TaskRunner() = default; + + TaskRunner(const TaskRunner&) = delete; + TaskRunner& operator=(const TaskRunner&) = delete; +}; + +/** + * Delegate that's passed to Job's worker task, providing an entry point to + * communicate with the scheduler. + */ +class JobDelegate { + public: + /** + * Returns true if this thread *must* return from the worker task on the + * current thread ASAP. Workers should periodically invoke ShouldYield (or + * YieldIfNeeded()) as often as is reasonable. + * After this method returned true, ShouldYield must not be called again. + */ + virtual bool ShouldYield() = 0; + + /** + * Notifies the scheduler that max concurrency was increased, and the number + * of worker should be adjusted accordingly. See Platform::PostJob() for more + * details. + */ + virtual void NotifyConcurrencyIncrease() = 0; + + /** + * Returns a task_id unique among threads currently running this job, such + * that GetTaskId() < worker count. To achieve this, the same task_id may be + * reused by a different thread after a worker_task returns. + */ + virtual uint8_t GetTaskId() = 0; + + /** + * Returns true if the current task is called from the thread currently + * running JobHandle::Join(). + */ + virtual bool IsJoiningThread() const = 0; +}; + +/** + * Handle returned when posting a Job. Provides methods to control execution of + * the posted Job. + */ +class JobHandle { + public: + virtual ~JobHandle() = default; + + /** + * Notifies the scheduler that max concurrency was increased, and the number + * of worker should be adjusted accordingly. See Platform::PostJob() for more + * details. + */ + virtual void NotifyConcurrencyIncrease() = 0; + + /** + * Contributes to the job on this thread. Doesn't return until all tasks have + * completed and max concurrency becomes 0. When Join() is called and max + * concurrency reaches 0, it should not increase again. This also promotes + * this Job's priority to be at least as high as the calling thread's + * priority. + */ + virtual void Join() = 0; + + /** + * Forces all existing workers to yield ASAP. Waits until they have all + * returned from the Job's callback before returning. + */ + virtual void Cancel() = 0; + + /* + * Forces all existing workers to yield ASAP but doesn’t wait for them. + * Warning, this is dangerous if the Job's callback is bound to or has access + * to state which may be deleted after this call. + */ + virtual void CancelAndDetach() = 0; + + /** + * Returns true if there's any work pending or any worker running. + */ + virtual bool IsActive() = 0; + + /** + * Returns true if associated with a Job and other methods may be called. + * Returns false after Join() or Cancel() was called. This may return true + * even if no workers are running and IsCompleted() returns true + */ + virtual bool IsValid() = 0; + + /** + * Returns true if job priority can be changed. + */ + virtual bool UpdatePriorityEnabled() const { return false; } + + /** + * Update this Job's priority. + */ + virtual void UpdatePriority(TaskPriority new_priority) {} +}; + +/** + * A JobTask represents work to run in parallel from Platform::PostJob(). + */ +class JobTask { + public: + virtual ~JobTask() = default; + + virtual void Run(JobDelegate* delegate) = 0; + + /** + * Controls the maximum number of threads calling Run() concurrently, given + * the number of threads currently assigned to this job and executing Run(). + * Run() is only invoked if the number of threads previously running Run() was + * less than the value returned. Since GetMaxConcurrency() is a leaf function, + * it must not call back any JobHandle methods. + */ + virtual size_t GetMaxConcurrency(size_t worker_count) const = 0; +}; + +/** + * The interface represents complex arguments to trace events. + */ +class ConvertableToTraceFormat { + public: + virtual ~ConvertableToTraceFormat() = default; + + /** + * Append the class info to the provided |out| string. The appended + * data must be a valid JSON object. Strings must be properly quoted, and + * escaped. There is no processing applied to the content after it is + * appended. + */ + virtual void AppendAsTraceFormat(std::string* out) const = 0; +}; + +/** + * V8 Tracing controller. + * + * Can be implemented by an embedder to record trace events from V8. + */ +class TracingController { + public: + virtual ~TracingController() = default; + + // In Perfetto mode, trace events are written using Perfetto's Track Event + // API directly without going through the embedder. However, it is still + // possible to observe tracing being enabled and disabled. +#if !defined(V8_USE_PERFETTO) + /** + * Called by TRACE_EVENT* macros, don't call this directly. + * The name parameter is a category group for example: + * TRACE_EVENT0("v8,parse", "V8.Parse") + * The pointer returned points to a value with zero or more of the bits + * defined in CategoryGroupEnabledFlags. + **/ + virtual const uint8_t* GetCategoryGroupEnabled(const char* name) { + static uint8_t no = 0; + return &no; + } + + /** + * Adds a trace event to the platform tracing system. These function calls are + * usually the result of a TRACE_* macro from trace_event_common.h when + * tracing and the category of the particular trace are enabled. It is not + * advisable to call these functions on their own; they are really only meant + * to be used by the trace macros. The returned handle can be used by + * UpdateTraceEventDuration to update the duration of COMPLETE events. + */ + virtual uint64_t AddTraceEvent( + char phase, const uint8_t* category_enabled_flag, const char* name, + const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args, + const char** arg_names, const uint8_t* arg_types, + const uint64_t* arg_values, + std::unique_ptr* arg_convertables, + unsigned int flags) { + return 0; + } + virtual uint64_t AddTraceEventWithTimestamp( + char phase, const uint8_t* category_enabled_flag, const char* name, + const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args, + const char** arg_names, const uint8_t* arg_types, + const uint64_t* arg_values, + std::unique_ptr* arg_convertables, + unsigned int flags, int64_t timestamp) { + return 0; + } + + /** + * Sets the duration field of a COMPLETE trace event. It must be called with + * the handle returned from AddTraceEvent(). + **/ + virtual void UpdateTraceEventDuration(const uint8_t* category_enabled_flag, + const char* name, uint64_t handle) {} +#endif // !defined(V8_USE_PERFETTO) + + class TraceStateObserver { + public: + virtual ~TraceStateObserver() = default; + virtual void OnTraceEnabled() = 0; + virtual void OnTraceDisabled() = 0; + }; + + /** Adds tracing state change observer. */ + virtual void AddTraceStateObserver(TraceStateObserver*) {} + + /** Removes tracing state change observer. */ + virtual void RemoveTraceStateObserver(TraceStateObserver*) {} +}; + +/** + * A V8 memory page allocator. + * + * Can be implemented by an embedder to manage large host OS allocations. + */ +class PageAllocator { + public: + virtual ~PageAllocator() = default; + + /** + * Gets the page granularity for AllocatePages and FreePages. Addresses and + * lengths for those calls should be multiples of AllocatePageSize(). + */ + virtual size_t AllocatePageSize() = 0; + + /** + * Gets the page granularity for SetPermissions and ReleasePages. Addresses + * and lengths for those calls should be multiples of CommitPageSize(). + */ + virtual size_t CommitPageSize() = 0; + + /** + * Sets the random seed so that GetRandomMmapAddr() will generate repeatable + * sequences of random mmap addresses. + */ + virtual void SetRandomMmapSeed(int64_t seed) = 0; + + /** + * Returns a randomized address, suitable for memory allocation under ASLR. + * The address will be aligned to AllocatePageSize. + */ + virtual void* GetRandomMmapAddr() = 0; + + /** + * Memory permissions. + */ + enum Permission { + kNoAccess, + kRead, + kReadWrite, + kReadWriteExecute, + kReadExecute, + // Set this when reserving memory that will later require kReadWriteExecute + // permissions. The resulting behavior is platform-specific, currently + // this is used to set the MAP_JIT flag on Apple Silicon. + // TODO(jkummerow): Remove this when Wasm has a platform-independent + // w^x implementation. + // TODO(saelo): Remove this once all JIT pages are allocated through the + // VirtualAddressSpace API. + kNoAccessWillJitLater + }; + + /** + * Allocates memory in range with the given alignment and permission. + */ + virtual void* AllocatePages(void* address, size_t length, size_t alignment, + Permission permissions) = 0; + + /** + * Frees memory in a range that was allocated by a call to AllocatePages. + */ + virtual bool FreePages(void* address, size_t length) = 0; + + /** + * Releases memory in a range that was allocated by a call to AllocatePages. + */ + virtual bool ReleasePages(void* address, size_t length, + size_t new_length) = 0; + + /** + * Sets permissions on pages in an allocated range. + */ + virtual bool SetPermissions(void* address, size_t length, + Permission permissions) = 0; + + /** + * Recommits discarded pages in the given range with given permissions. + * Discarded pages must be recommitted with their original permissions + * before they are used again. + */ + virtual bool RecommitPages(void* address, size_t length, + Permission permissions) { + // TODO(v8:12797): make it pure once it's implemented on Chromium side. + return false; + } + + /** + * Frees memory in the given [address, address + size) range. address and size + * should be operating system page-aligned. The next write to this + * memory area brings the memory transparently back. This should be treated as + * a hint to the OS that the pages are no longer needed. It does not guarantee + * that the pages will be discarded immediately or at all. + */ + virtual bool DiscardSystemPages(void* address, size_t size) { return true; } + + /** + * Decommits any wired memory pages in the given range, allowing the OS to + * reclaim them, and marks the region as inacessible (kNoAccess). The address + * range stays reserved and can be accessed again later by changing its + * permissions. However, in that case the memory content is guaranteed to be + * zero-initialized again. The memory must have been previously allocated by a + * call to AllocatePages. Returns true on success, false otherwise. + */ + virtual bool DecommitPages(void* address, size_t size) = 0; + + /** + * INTERNAL ONLY: This interface has not been stabilised and may change + * without notice from one release to another without being deprecated first. + */ + class SharedMemoryMapping { + public: + // Implementations are expected to free the shared memory mapping in the + // destructor. + virtual ~SharedMemoryMapping() = default; + virtual void* GetMemory() const = 0; + }; + + /** + * INTERNAL ONLY: This interface has not been stabilised and may change + * without notice from one release to another without being deprecated first. + */ + class SharedMemory { + public: + // Implementations are expected to free the shared memory in the destructor. + virtual ~SharedMemory() = default; + virtual std::unique_ptr RemapTo( + void* new_address) const = 0; + virtual void* GetMemory() const = 0; + virtual size_t GetSize() const = 0; + }; + + /** + * INTERNAL ONLY: This interface has not been stabilised and may change + * without notice from one release to another without being deprecated first. + * + * Reserve pages at a fixed address returning whether the reservation is + * possible. The reserved memory is detached from the PageAllocator and so + * should not be freed by it. It's intended for use with + * SharedMemory::RemapTo, where ~SharedMemoryMapping would free the memory. + */ + virtual bool ReserveForSharedMemoryMapping(void* address, size_t size) { + return false; + } + + /** + * INTERNAL ONLY: This interface has not been stabilised and may change + * without notice from one release to another without being deprecated first. + * + * Allocates shared memory pages. Not all PageAllocators need support this and + * so this method need not be overridden. + * Allocates a new read-only shared memory region of size |length| and copies + * the memory at |original_address| into it. + */ + virtual std::unique_ptr AllocateSharedPages( + size_t length, const void* original_address) { + return {}; + } + + /** + * INTERNAL ONLY: This interface has not been stabilised and may change + * without notice from one release to another without being deprecated first. + * + * If not overridden and changed to return true, V8 will not attempt to call + * AllocateSharedPages or RemapSharedPages. If overridden, AllocateSharedPages + * and RemapSharedPages must also be overridden. + */ + virtual bool CanAllocateSharedPages() { return false; } +}; + +// Opaque type representing a handle to a shared memory region. +using PlatformSharedMemoryHandle = intptr_t; +static constexpr PlatformSharedMemoryHandle kInvalidSharedMemoryHandle = -1; + +// Conversion routines from the platform-dependent shared memory identifiers +// into the opaque PlatformSharedMemoryHandle type. These use the underlying +// types (e.g. unsigned int) instead of the typedef'd ones (e.g. mach_port_t) +// to avoid pulling in large OS header files into this header file. Instead, +// the users of these routines are expected to include the respecitve OS +// headers in addition to this one. +#if V8_OS_MACOS +// Convert between a shared memory handle and a mach_port_t referencing a memory +// entry object. +inline PlatformSharedMemoryHandle SharedMemoryHandleFromMachMemoryEntry( + unsigned int port) { + return static_cast(port); +} +inline unsigned int MachMemoryEntryFromSharedMemoryHandle( + PlatformSharedMemoryHandle handle) { + return static_cast(handle); +} +#elif V8_OS_FUCHSIA +// Convert between a shared memory handle and a zx_handle_t to a VMO. +inline PlatformSharedMemoryHandle SharedMemoryHandleFromVMO(uint32_t handle) { + return static_cast(handle); +} +inline uint32_t VMOFromSharedMemoryHandle(PlatformSharedMemoryHandle handle) { + return static_cast(handle); +} +#elif V8_OS_WIN +// Convert between a shared memory handle and a Windows HANDLE to a file mapping +// object. +inline PlatformSharedMemoryHandle SharedMemoryHandleFromFileMapping( + void* handle) { + return reinterpret_cast(handle); +} +inline void* FileMappingFromSharedMemoryHandle( + PlatformSharedMemoryHandle handle) { + return reinterpret_cast(handle); +} +#else +// Convert between a shared memory handle and a file descriptor. +inline PlatformSharedMemoryHandle SharedMemoryHandleFromFileDescriptor(int fd) { + return static_cast(fd); +} +inline int FileDescriptorFromSharedMemoryHandle( + PlatformSharedMemoryHandle handle) { + return static_cast(handle); +} +#endif + +/** + * Possible permissions for memory pages. + */ +enum class PagePermissions { + kNoAccess, + kRead, + kReadWrite, + kReadWriteExecute, + kReadExecute, +}; + +/** + * Class to manage a virtual memory address space. + * + * This class represents a contiguous region of virtual address space in which + * sub-spaces and (private or shared) memory pages can be allocated, freed, and + * modified. This interface is meant to eventually replace the PageAllocator + * interface, and can be used as an alternative in the meantime. + * + * This API is not yet stable and may change without notice! + */ +class VirtualAddressSpace { + public: + using Address = uintptr_t; + + VirtualAddressSpace(size_t page_size, size_t allocation_granularity, + Address base, size_t size, + PagePermissions max_page_permissions) + : page_size_(page_size), + allocation_granularity_(allocation_granularity), + base_(base), + size_(size), + max_page_permissions_(max_page_permissions) {} + + virtual ~VirtualAddressSpace() = default; + + /** + * The page size used inside this space. Guaranteed to be a power of two. + * Used as granularity for all page-related operations except for allocation, + * which use the allocation_granularity(), see below. + * + * \returns the page size in bytes. + */ + size_t page_size() const { return page_size_; } + + /** + * The granularity of page allocations and, by extension, of subspace + * allocations. This is guaranteed to be a power of two and a multiple of the + * page_size(). In practice, this is equal to the page size on most OSes, but + * on Windows it is usually 64KB, while the page size is 4KB. + * + * \returns the allocation granularity in bytes. + */ + size_t allocation_granularity() const { return allocation_granularity_; } + + /** + * The base address of the address space managed by this instance. + * + * \returns the base address of this address space. + */ + Address base() const { return base_; } + + /** + * The size of the address space managed by this instance. + * + * \returns the size of this address space in bytes. + */ + size_t size() const { return size_; } + + /** + * The maximum page permissions that pages allocated inside this space can + * obtain. + * + * \returns the maximum page permissions. + */ + PagePermissions max_page_permissions() const { return max_page_permissions_; } + + /** + * Sets the random seed so that GetRandomPageAddress() will generate + * repeatable sequences of random addresses. + * + * \param The seed for the PRNG. + */ + virtual void SetRandomSeed(int64_t seed) = 0; + + /** + * Returns a random address inside this address space, suitable for page + * allocations hints. + * + * \returns a random address aligned to allocation_granularity(). + */ + virtual Address RandomPageAddress() = 0; + + /** + * Allocates private memory pages with the given alignment and permissions. + * + * \param hint If nonzero, the allocation is attempted to be placed at the + * given address first. If that fails, the allocation is attempted to be + * placed elsewhere, possibly nearby, but that is not guaranteed. Specifying + * zero for the hint always causes this function to choose a random address. + * The hint, if specified, must be aligned to the specified alignment. + * + * \param size The size of the allocation in bytes. Must be a multiple of the + * allocation_granularity(). + * + * \param alignment The alignment of the allocation in bytes. Must be a + * multiple of the allocation_granularity() and should be a power of two. + * + * \param permissions The page permissions of the newly allocated pages. + * + * \returns the start address of the allocated pages on success, zero on + * failure. + */ + static constexpr Address kNoHint = 0; + virtual V8_WARN_UNUSED_RESULT Address + AllocatePages(Address hint, size_t size, size_t alignment, + PagePermissions permissions) = 0; + + /** + * Frees previously allocated pages. + * + * This function will terminate the process on failure as this implies a bug + * in the client. As such, there is no return value. + * + * \param address The start address of the pages to free. This address must + * have been obtained through a call to AllocatePages. + * + * \param size The size in bytes of the region to free. This must match the + * size passed to AllocatePages when the pages were allocated. + */ + virtual void FreePages(Address address, size_t size) = 0; + + /** + * Sets permissions of all allocated pages in the given range. + * + * This operation can fail due to OOM, in which case false is returned. If + * the operation fails for a reason other than OOM, this function will + * terminate the process as this implies a bug in the client. + * + * \param address The start address of the range. Must be aligned to + * page_size(). + * + * \param size The size in bytes of the range. Must be a multiple + * of page_size(). + * + * \param permissions The new permissions for the range. + * + * \returns true on success, false on OOM. + */ + virtual V8_WARN_UNUSED_RESULT bool SetPagePermissions( + Address address, size_t size, PagePermissions permissions) = 0; + + /** + * Creates a guard region at the specified address. + * + * Guard regions are guaranteed to cause a fault when accessed and generally + * do not count towards any memory consumption limits. Further, allocating + * guard regions can usually not fail in subspaces if the region does not + * overlap with another region, subspace, or page allocation. + * + * \param address The start address of the guard region. Must be aligned to + * the allocation_granularity(). + * + * \param size The size of the guard region in bytes. Must be a multiple of + * the allocation_granularity(). + * + * \returns true on success, false otherwise. + */ + virtual V8_WARN_UNUSED_RESULT bool AllocateGuardRegion(Address address, + size_t size) = 0; + + /** + * Frees an existing guard region. + * + * This function will terminate the process on failure as this implies a bug + * in the client. As such, there is no return value. + * + * \param address The start address of the guard region to free. This address + * must have previously been used as address parameter in a successful + * invocation of AllocateGuardRegion. + * + * \param size The size in bytes of the guard region to free. This must match + * the size passed to AllocateGuardRegion when the region was created. + */ + virtual void FreeGuardRegion(Address address, size_t size) = 0; + + /** + * Allocates shared memory pages with the given permissions. + * + * \param hint Placement hint. See AllocatePages. + * + * \param size The size of the allocation in bytes. Must be a multiple of the + * allocation_granularity(). + * + * \param permissions The page permissions of the newly allocated pages. + * + * \param handle A platform-specific handle to a shared memory object. See + * the SharedMemoryHandleFromX routines above for ways to obtain these. + * + * \param offset The offset in the shared memory object at which the mapping + * should start. Must be a multiple of the allocation_granularity(). + * + * \returns the start address of the allocated pages on success, zero on + * failure. + */ + virtual V8_WARN_UNUSED_RESULT Address + AllocateSharedPages(Address hint, size_t size, PagePermissions permissions, + PlatformSharedMemoryHandle handle, uint64_t offset) = 0; + + /** + * Frees previously allocated shared pages. + * + * This function will terminate the process on failure as this implies a bug + * in the client. As such, there is no return value. + * + * \param address The start address of the pages to free. This address must + * have been obtained through a call to AllocateSharedPages. + * + * \param size The size in bytes of the region to free. This must match the + * size passed to AllocateSharedPages when the pages were allocated. + */ + virtual void FreeSharedPages(Address address, size_t size) = 0; + + /** + * Whether this instance can allocate subspaces or not. + * + * \returns true if subspaces can be allocated, false if not. + */ + virtual bool CanAllocateSubspaces() = 0; + + /* + * Allocate a subspace. + * + * The address space of a subspace stays reserved in the parent space for the + * lifetime of the subspace. As such, it is guaranteed that page allocations + * on the parent space cannot end up inside a subspace. + * + * \param hint Hints where the subspace should be allocated. See + * AllocatePages() for more details. + * + * \param size The size in bytes of the subspace. Must be a multiple of the + * allocation_granularity(). + * + * \param alignment The alignment of the subspace in bytes. Must be a multiple + * of the allocation_granularity() and should be a power of two. + * + * \param max_page_permissions The maximum permissions that pages allocated in + * the subspace can obtain. + * + * \returns a new subspace or nullptr on failure. + */ + virtual std::unique_ptr AllocateSubspace( + Address hint, size_t size, size_t alignment, + PagePermissions max_page_permissions) = 0; + + // + // TODO(v8) maybe refactor the methods below before stabilizing the API. For + // example by combining them into some form of page operation method that + // takes a command enum as parameter. + // + + /** + * Recommits discarded pages in the given range with given permissions. + * Discarded pages must be recommitted with their original permissions + * before they are used again. + * + * \param address The start address of the range. Must be aligned to + * page_size(). + * + * \param size The size in bytes of the range. Must be a multiple + * of page_size(). + * + * \param permissions The permissions for the range that the pages must have. + * + * \returns true on success, false otherwise. + */ + virtual V8_WARN_UNUSED_RESULT bool RecommitPages( + Address address, size_t size, PagePermissions permissions) = 0; + + /** + * Frees memory in the given [address, address + size) range. address and + * size should be aligned to the page_size(). The next write to this memory + * area brings the memory transparently back. This should be treated as a + * hint to the OS that the pages are no longer needed. It does not guarantee + * that the pages will be discarded immediately or at all. + * + * \returns true on success, false otherwise. Since this method is only a + * hint, a successful invocation does not imply that pages have been removed. + */ + virtual V8_WARN_UNUSED_RESULT bool DiscardSystemPages(Address address, + size_t size) { + return true; + } + /** + * Decommits any wired memory pages in the given range, allowing the OS to + * reclaim them, and marks the region as inacessible (kNoAccess). The address + * range stays reserved and can be accessed again later by changing its + * permissions. However, in that case the memory content is guaranteed to be + * zero-initialized again. The memory must have been previously allocated by a + * call to AllocatePages. + * + * \returns true on success, false otherwise. + */ + virtual V8_WARN_UNUSED_RESULT bool DecommitPages(Address address, + size_t size) = 0; + + private: + const size_t page_size_; + const size_t allocation_granularity_; + const Address base_; + const size_t size_; + const PagePermissions max_page_permissions_; +}; + +/** + * V8 Allocator used for allocating zone backings. + */ +class ZoneBackingAllocator { + public: + using MallocFn = void* (*)(size_t); + using FreeFn = void (*)(void*); + + virtual MallocFn GetMallocFn() const { return ::malloc; } + virtual FreeFn GetFreeFn() const { return ::free; } +}; + +/** + * Observer used by V8 to notify the embedder about entering/leaving sections + * with high throughput of malloc/free operations. + */ +class HighAllocationThroughputObserver { + public: + virtual void EnterSection() {} + virtual void LeaveSection() {} +}; + +/** + * V8 Platform abstraction layer. + * + * The embedder has to provide an implementation of this interface before + * initializing the rest of V8. + */ +class Platform { + public: + virtual ~Platform() = default; + + /** + * Allows the embedder to manage memory page allocations. + * Returning nullptr will cause V8 to use the default page allocator. + */ + virtual PageAllocator* GetPageAllocator() = 0; + + /** + * Allows the embedder to specify a custom allocator used for zones. + */ + virtual ZoneBackingAllocator* GetZoneBackingAllocator() { + static ZoneBackingAllocator default_allocator; + return &default_allocator; + } + + /** + * Enables the embedder to respond in cases where V8 can't allocate large + * blocks of memory. V8 retries the failed allocation once after calling this + * method. On success, execution continues; otherwise V8 exits with a fatal + * error. + * Embedder overrides of this function must NOT call back into V8. + */ + virtual void OnCriticalMemoryPressure() {} + + /** + * Gets the number of worker threads used by + * Call(BlockingTask)OnWorkerThread(). This can be used to estimate the number + * of tasks a work package should be split into. A return value of 0 means + * that there are no worker threads available. Note that a value of 0 won't + * prohibit V8 from posting tasks using |CallOnWorkerThread|. + */ + virtual int NumberOfWorkerThreads() = 0; + + /** + * Returns a TaskRunner which can be used to post a task on the foreground. + * The TaskRunner's NonNestableTasksEnabled() must be true. This function + * should only be called from a foreground thread. + */ + virtual std::shared_ptr GetForegroundTaskRunner( + Isolate* isolate) = 0; + + /** + * Schedules a task to be invoked on a worker thread. + */ + virtual void CallOnWorkerThread(std::unique_ptr task) = 0; + + /** + * Schedules a task that blocks the main thread to be invoked with + * high-priority on a worker thread. + */ + virtual void CallBlockingTaskOnWorkerThread(std::unique_ptr task) { + // Embedders may optionally override this to process these tasks in a high + // priority pool. + CallOnWorkerThread(std::move(task)); + } + + /** + * Schedules a task to be invoked with low-priority on a worker thread. + */ + virtual void CallLowPriorityTaskOnWorkerThread(std::unique_ptr task) { + // Embedders may optionally override this to process these tasks in a low + // priority pool. + CallOnWorkerThread(std::move(task)); + } + + /** + * Schedules a task to be invoked on a worker thread after |delay_in_seconds| + * expires. + */ + virtual void CallDelayedOnWorkerThread(std::unique_ptr task, + double delay_in_seconds) = 0; + + /** + * Returns true if idle tasks are enabled for the given |isolate|. + */ + virtual bool IdleTasksEnabled(Isolate* isolate) { return false; } + + /** + * Posts |job_task| to run in parallel. Returns a JobHandle associated with + * the Job, which can be joined or canceled. + * This avoids degenerate cases: + * - Calling CallOnWorkerThread() for each work item, causing significant + * overhead. + * - Fixed number of CallOnWorkerThread() calls that split the work and might + * run for a long time. This is problematic when many components post + * "num cores" tasks and all expect to use all the cores. In these cases, + * the scheduler lacks context to be fair to multiple same-priority requests + * and/or ability to request lower priority work to yield when high priority + * work comes in. + * A canonical implementation of |job_task| looks like: + * class MyJobTask : public JobTask { + * public: + * MyJobTask(...) : worker_queue_(...) {} + * // JobTask: + * void Run(JobDelegate* delegate) override { + * while (!delegate->ShouldYield()) { + * // Smallest unit of work. + * auto work_item = worker_queue_.TakeWorkItem(); // Thread safe. + * if (!work_item) return; + * ProcessWork(work_item); + * } + * } + * + * size_t GetMaxConcurrency() const override { + * return worker_queue_.GetSize(); // Thread safe. + * } + * }; + * auto handle = PostJob(TaskPriority::kUserVisible, + * std::make_unique(...)); + * handle->Join(); + * + * PostJob() and methods of the returned JobHandle/JobDelegate, must never be + * called while holding a lock that could be acquired by JobTask::Run or + * JobTask::GetMaxConcurrency -- that could result in a deadlock. This is + * because [1] JobTask::GetMaxConcurrency may be invoked while holding + * internal lock (A), hence JobTask::GetMaxConcurrency can only use a lock (B) + * if that lock is *never* held while calling back into JobHandle from any + * thread (A=>B/B=>A deadlock) and [2] JobTask::Run or + * JobTask::GetMaxConcurrency may be invoked synchronously from JobHandle + * (B=>JobHandle::foo=>B deadlock). + */ + virtual std::unique_ptr PostJob( + TaskPriority priority, std::unique_ptr job_task) { + auto handle = CreateJob(priority, std::move(job_task)); + handle->NotifyConcurrencyIncrease(); + return handle; + } + + /** + * Creates and returns a JobHandle associated with a Job. Unlike PostJob(), + * this doesn't immediately schedules |worker_task| to run; the Job is then + * scheduled by calling either NotifyConcurrencyIncrease() or Join(). + * + * A sufficient CreateJob() implementation that uses the default Job provided + * in libplatform looks like: + * std::unique_ptr CreateJob( + * TaskPriority priority, std::unique_ptr job_task) override { + * return v8::platform::NewDefaultJobHandle( + * this, priority, std::move(job_task), NumberOfWorkerThreads()); + * } + */ + virtual std::unique_ptr CreateJob( + TaskPriority priority, std::unique_ptr job_task) = 0; + + /** + * Monotonically increasing time in seconds from an arbitrary fixed point in + * the past. This function is expected to return at least + * millisecond-precision values. For this reason, + * it is recommended that the fixed point be no further in the past than + * the epoch. + **/ + virtual double MonotonicallyIncreasingTime() = 0; + + /** + * Current wall-clock time in milliseconds since epoch. + * This function is expected to return at least millisecond-precision values. + */ + virtual double CurrentClockTimeMillis() = 0; + + typedef void (*StackTracePrinter)(); + + /** + * Returns a function pointer that print a stack trace of the current stack + * on invocation. Disables printing of the stack trace if nullptr. + */ + virtual StackTracePrinter GetStackTracePrinter() { return nullptr; } + + /** + * Returns an instance of a v8::TracingController. This must be non-nullptr. + */ + virtual TracingController* GetTracingController() = 0; + + /** + * Tells the embedder to generate and upload a crashdump during an unexpected + * but non-critical scenario. + */ + virtual void DumpWithoutCrashing() {} + + /** + * Allows the embedder to observe sections with high throughput allocation + * operations. + */ + virtual HighAllocationThroughputObserver* + GetHighAllocationThroughputObserver() { + static HighAllocationThroughputObserver default_observer; + return &default_observer; + } + + protected: + /** + * Default implementation of current wall-clock time in milliseconds + * since epoch. Useful for implementing |CurrentClockTimeMillis| if + * nothing special needed. + */ + V8_EXPORT static double SystemClockTimeMillis(); +}; + +} // namespace v8 + +#endif // V8_V8_PLATFORM_H_ diff --git a/deps/include/v8-primitive-object.h b/deps/include/v8-primitive-object.h new file mode 100755 index 0000000..573932d --- /dev/null +++ b/deps/include/v8-primitive-object.h @@ -0,0 +1,118 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_PRIMITIVE_OBJECT_H_ +#define INCLUDE_V8_PRIMITIVE_OBJECT_H_ + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-object.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Isolate; + +/** + * A Number object (ECMA-262, 4.3.21). + */ +class V8_EXPORT NumberObject : public Object { + public: + static Local New(Isolate* isolate, double value); + + double ValueOf() const; + + V8_INLINE static NumberObject* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + static void CheckCast(Value* obj); +}; + +/** + * A BigInt object (https://tc39.github.io/proposal-bigint) + */ +class V8_EXPORT BigIntObject : public Object { + public: + static Local New(Isolate* isolate, int64_t value); + + Local ValueOf() const; + + V8_INLINE static BigIntObject* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + static void CheckCast(Value* obj); +}; + +/** + * A Boolean object (ECMA-262, 4.3.15). + */ +class V8_EXPORT BooleanObject : public Object { + public: + static Local New(Isolate* isolate, bool value); + + bool ValueOf() const; + + V8_INLINE static BooleanObject* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + static void CheckCast(Value* obj); +}; + +/** + * A String object (ECMA-262, 4.3.18). + */ +class V8_EXPORT StringObject : public Object { + public: + static Local New(Isolate* isolate, Local value); + + Local ValueOf() const; + + V8_INLINE static StringObject* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + static void CheckCast(Value* obj); +}; + +/** + * A Symbol object (ECMA-262 edition 6). + */ +class V8_EXPORT SymbolObject : public Object { + public: + static Local New(Isolate* isolate, Local value); + + Local ValueOf() const; + + V8_INLINE static SymbolObject* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + static void CheckCast(Value* obj); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_PRIMITIVE_OBJECT_H_ diff --git a/deps/include/v8-primitive.h b/deps/include/v8-primitive.h new file mode 100755 index 0000000..4fef8da --- /dev/null +++ b/deps/include/v8-primitive.h @@ -0,0 +1,866 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_PRIMITIVE_H_ +#define INCLUDE_V8_PRIMITIVE_H_ + +#include "v8-data.h" // NOLINT(build/include_directory) +#include "v8-internal.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-value.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; +class Isolate; +class String; + +namespace internal { +class ExternalString; +class ScopedExternalStringLock; +class StringForwardingTable; +} // namespace internal + +/** + * The superclass of primitive values. See ECMA-262 4.3.2. + */ +class V8_EXPORT Primitive : public Value {}; + +/** + * A primitive boolean value (ECMA-262, 4.3.14). Either the true + * or false value. + */ +class V8_EXPORT Boolean : public Primitive { + public: + bool Value() const; + V8_INLINE static Boolean* Cast(v8::Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + V8_INLINE static Local New(Isolate* isolate, bool value); + + private: + static void CheckCast(v8::Data* that); +}; + +/** + * An array to hold Primitive values. This is used by the embedder to + * pass host defined options to the ScriptOptions during compilation. + * + * This is passed back to the embedder as part of + * HostImportModuleDynamicallyCallback for module loading. + */ +class V8_EXPORT PrimitiveArray : public Data { + public: + static Local New(Isolate* isolate, int length); + int Length() const; + void Set(Isolate* isolate, int index, Local item); + Local Get(Isolate* isolate, int index); + + V8_INLINE static PrimitiveArray* Cast(Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return reinterpret_cast(data); + } + + private: + static void CheckCast(Data* obj); +}; + +/** + * A superclass for symbols and strings. + */ +class V8_EXPORT Name : public Primitive { + public: + /** + * Returns the identity hash for this object. The current implementation + * uses an inline property on the object to store the identity hash. + * + * The return value will never be 0. Also, it is not guaranteed to be + * unique. + */ + int GetIdentityHash(); + + V8_INLINE static Name* Cast(Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + private: + static void CheckCast(Data* that); +}; + +/** + * A flag describing different modes of string creation. + * + * Aside from performance implications there are no differences between the two + * creation modes. + */ +enum class NewStringType { + /** + * Create a new string, always allocating new storage memory. + */ + kNormal, + + /** + * Acts as a hint that the string should be created in the + * old generation heap space and be deduplicated if an identical string + * already exists. + */ + kInternalized +}; + +/** + * A JavaScript string value (ECMA-262, 4.3.17). + */ +class V8_EXPORT String : public Name { + public: + static constexpr int kMaxLength = + internal::kApiSystemPointerSize == 4 ? (1 << 28) - 16 : (1 << 29) - 24; + + enum Encoding { + UNKNOWN_ENCODING = 0x1, + TWO_BYTE_ENCODING = 0x0, + ONE_BYTE_ENCODING = 0x8 + }; + /** + * Returns the number of characters (UTF-16 code units) in this string. + */ + int Length() const; + + /** + * Returns the number of bytes in the UTF-8 encoded + * representation of this string. + */ + int Utf8Length(Isolate* isolate) const; + + /** + * Returns whether this string is known to contain only one byte data, + * i.e. ISO-8859-1 code points. + * Does not read the string. + * False negatives are possible. + */ + bool IsOneByte() const; + + /** + * Returns whether this string contain only one byte data, + * i.e. ISO-8859-1 code points. + * Will read the entire string in some cases. + */ + bool ContainsOnlyOneByte() const; + + /** + * Write the contents of the string to an external buffer. + * If no arguments are given, expects the buffer to be large + * enough to hold the entire string and NULL terminator. Copies + * the contents of the string and the NULL terminator into the + * buffer. + * + * WriteUtf8 will not write partial UTF-8 sequences, preferring to stop + * before the end of the buffer. + * + * Copies up to length characters into the output buffer. + * Only null-terminates if there is enough space in the buffer. + * + * \param buffer The buffer into which the string will be copied. + * \param start The starting position within the string at which + * copying begins. + * \param length The number of characters to copy from the string. For + * WriteUtf8 the number of bytes in the buffer. + * \param nchars_ref The number of characters written, can be NULL. + * \param options Various options that might affect performance of this or + * subsequent operations. + * \return The number of characters copied to the buffer excluding the null + * terminator. For WriteUtf8: The number of bytes copied to the buffer + * including the null terminator (if written). + */ + enum WriteOptions { + NO_OPTIONS = 0, + HINT_MANY_WRITES_EXPECTED = 1, + NO_NULL_TERMINATION = 2, + PRESERVE_ONE_BYTE_NULL = 4, + // Used by WriteUtf8 to replace orphan surrogate code units with the + // unicode replacement character. Needs to be set to guarantee valid UTF-8 + // output. + REPLACE_INVALID_UTF8 = 8 + }; + + // 16-bit character codes. + int Write(Isolate* isolate, uint16_t* buffer, int start = 0, int length = -1, + int options = NO_OPTIONS) const; + // One byte characters. + int WriteOneByte(Isolate* isolate, uint8_t* buffer, int start = 0, + int length = -1, int options = NO_OPTIONS) const; + // UTF-8 encoded characters. + int WriteUtf8(Isolate* isolate, char* buffer, int length = -1, + int* nchars_ref = nullptr, int options = NO_OPTIONS) const; + + /** + * A zero length string. + */ + V8_INLINE static Local Empty(Isolate* isolate); + + /** + * Returns true if the string is external. + */ + bool IsExternal() const; + + /** + * Returns true if the string is both external and two-byte. + */ + bool IsExternalTwoByte() const; + + /** + * Returns true if the string is both external and one-byte. + */ + bool IsExternalOneByte() const; + + class V8_EXPORT ExternalStringResourceBase { + public: + virtual ~ExternalStringResourceBase() = default; + + /** + * If a string is cacheable, the value returned by + * ExternalStringResource::data() may be cached, otherwise it is not + * expected to be stable beyond the current top-level task. + */ + virtual bool IsCacheable() const { return true; } + + // Disallow copying and assigning. + ExternalStringResourceBase(const ExternalStringResourceBase&) = delete; + void operator=(const ExternalStringResourceBase&) = delete; + + protected: + ExternalStringResourceBase() = default; + + /** + * Internally V8 will call this Dispose method when the external string + * resource is no longer needed. The default implementation will use the + * delete operator. This method can be overridden in subclasses to + * control how allocated external string resources are disposed. + */ + virtual void Dispose() { delete this; } + + /** + * For a non-cacheable string, the value returned by + * |ExternalStringResource::data()| has to be stable between |Lock()| and + * |Unlock()|, that is the string must behave as is |IsCacheable()| returned + * true. + * + * These two functions must be thread-safe, and can be called from anywhere. + * They also must handle lock depth, in the sense that each can be called + * several times, from different threads, and unlocking should only happen + * when the balance of Lock() and Unlock() calls is 0. + */ + virtual void Lock() const {} + + /** + * Unlocks the string. + */ + virtual void Unlock() const {} + + private: + friend class internal::ExternalString; + friend class v8::String; + friend class internal::StringForwardingTable; + friend class internal::ScopedExternalStringLock; + }; + + /** + * An ExternalStringResource is a wrapper around a two-byte string + * buffer that resides outside V8's heap. Implement an + * ExternalStringResource to manage the life cycle of the underlying + * buffer. Note that the string data must be immutable. + */ + class V8_EXPORT ExternalStringResource : public ExternalStringResourceBase { + public: + /** + * Override the destructor to manage the life cycle of the underlying + * buffer. + */ + ~ExternalStringResource() override = default; + + /** + * The string data from the underlying buffer. If the resource is cacheable + * then data() must return the same value for all invocations. + */ + virtual const uint16_t* data() const = 0; + + /** + * The length of the string. That is, the number of two-byte characters. + */ + virtual size_t length() const = 0; + + /** + * Returns the cached data from the underlying buffer. This method can be + * called only for cacheable resources (i.e. IsCacheable() == true) and only + * after UpdateDataCache() was called. + */ + const uint16_t* cached_data() const { + CheckCachedDataInvariants(); + return cached_data_; + } + + /** + * Update {cached_data_} with the data from the underlying buffer. This can + * be called only for cacheable resources. + */ + void UpdateDataCache(); + + protected: + ExternalStringResource() = default; + + private: + void CheckCachedDataInvariants() const; + + const uint16_t* cached_data_ = nullptr; + }; + + /** + * An ExternalOneByteStringResource is a wrapper around an one-byte + * string buffer that resides outside V8's heap. Implement an + * ExternalOneByteStringResource to manage the life cycle of the + * underlying buffer. Note that the string data must be immutable + * and that the data must be Latin-1 and not UTF-8, which would require + * special treatment internally in the engine and do not allow efficient + * indexing. Use String::New or convert to 16 bit data for non-Latin1. + */ + + class V8_EXPORT ExternalOneByteStringResource + : public ExternalStringResourceBase { + public: + /** + * Override the destructor to manage the life cycle of the underlying + * buffer. + */ + ~ExternalOneByteStringResource() override = default; + + /** + * The string data from the underlying buffer. If the resource is cacheable + * then data() must return the same value for all invocations. + */ + virtual const char* data() const = 0; + + /** The number of Latin-1 characters in the string.*/ + virtual size_t length() const = 0; + + /** + * Returns the cached data from the underlying buffer. If the resource is + * uncacheable or if UpdateDataCache() was not called before, it has + * undefined behaviour. + */ + const char* cached_data() const { + CheckCachedDataInvariants(); + return cached_data_; + } + + /** + * Update {cached_data_} with the data from the underlying buffer. This can + * be called only for cacheable resources. + */ + void UpdateDataCache(); + + protected: + ExternalOneByteStringResource() = default; + + private: + void CheckCachedDataInvariants() const; + + const char* cached_data_ = nullptr; + }; + + /** + * If the string is an external string, return the ExternalStringResourceBase + * regardless of the encoding, otherwise return NULL. The encoding of the + * string is returned in encoding_out. + */ + V8_INLINE ExternalStringResourceBase* GetExternalStringResourceBase( + Encoding* encoding_out) const; + + /** + * Get the ExternalStringResource for an external string. Returns + * NULL if IsExternal() doesn't return true. + */ + V8_INLINE ExternalStringResource* GetExternalStringResource() const; + + /** + * Get the ExternalOneByteStringResource for an external one-byte string. + * Returns NULL if IsExternalOneByte() doesn't return true. + */ + const ExternalOneByteStringResource* GetExternalOneByteStringResource() const; + + V8_INLINE static String* Cast(v8::Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + /** + * Allocates a new string from a UTF-8 literal. This is equivalent to calling + * String::NewFromUtf(isolate, "...").ToLocalChecked(), but without the check + * overhead. + * + * When called on a string literal containing '\0', the inferred length is the + * length of the input array minus 1 (for the final '\0') and not the value + * returned by strlen. + **/ + template + static V8_WARN_UNUSED_RESULT Local NewFromUtf8Literal( + Isolate* isolate, const char (&literal)[N], + NewStringType type = NewStringType::kNormal) { + static_assert(N <= kMaxLength, "String is too long"); + return NewFromUtf8Literal(isolate, literal, type, N - 1); + } + + /** Allocates a new string from UTF-8 data. Only returns an empty value when + * length > kMaxLength. **/ + static V8_WARN_UNUSED_RESULT MaybeLocal NewFromUtf8( + Isolate* isolate, const char* data, + NewStringType type = NewStringType::kNormal, int length = -1); + + /** Allocates a new string from Latin-1 data. Only returns an empty value + * when length > kMaxLength. **/ + static V8_WARN_UNUSED_RESULT MaybeLocal NewFromOneByte( + Isolate* isolate, const uint8_t* data, + NewStringType type = NewStringType::kNormal, int length = -1); + + /** Allocates a new string from UTF-16 data. Only returns an empty value when + * length > kMaxLength. **/ + static V8_WARN_UNUSED_RESULT MaybeLocal NewFromTwoByte( + Isolate* isolate, const uint16_t* data, + NewStringType type = NewStringType::kNormal, int length = -1); + + /** + * Creates a new string by concatenating the left and the right strings + * passed in as parameters. + */ + static Local Concat(Isolate* isolate, Local left, + Local right); + + /** + * Creates a new external string using the data defined in the given + * resource. When the external string is no longer live on V8's heap the + * resource will be disposed by calling its Dispose method. The caller of + * this function should not otherwise delete or modify the resource. Neither + * should the underlying buffer be deallocated or modified except through the + * destructor of the external string resource. + */ + static V8_WARN_UNUSED_RESULT MaybeLocal NewExternalTwoByte( + Isolate* isolate, ExternalStringResource* resource); + + /** + * Associate an external string resource with this string by transforming it + * in place so that existing references to this string in the JavaScript heap + * will use the external string resource. The external string resource's + * character contents need to be equivalent to this string. + * Returns true if the string has been changed to be an external string. + * The string is not modified if the operation fails. See NewExternal for + * information on the lifetime of the resource. + */ + bool MakeExternal(ExternalStringResource* resource); + + /** + * Creates a new external string using the one-byte data defined in the given + * resource. When the external string is no longer live on V8's heap the + * resource will be disposed by calling its Dispose method. The caller of + * this function should not otherwise delete or modify the resource. Neither + * should the underlying buffer be deallocated or modified except through the + * destructor of the external string resource. + */ + static V8_WARN_UNUSED_RESULT MaybeLocal NewExternalOneByte( + Isolate* isolate, ExternalOneByteStringResource* resource); + + /** + * Associate an external string resource with this string by transforming it + * in place so that existing references to this string in the JavaScript heap + * will use the external string resource. The external string resource's + * character contents need to be equivalent to this string. + * Returns true if the string has been changed to be an external string. + * The string is not modified if the operation fails. See NewExternal for + * information on the lifetime of the resource. + */ + bool MakeExternal(ExternalOneByteStringResource* resource); + + /** + * Returns true if this string can be made external. + */ + bool CanMakeExternal() const; + + /** + * Returns true if the strings values are equal. Same as JS ==/===. + */ + bool StringEquals(Local str) const; + + /** + * Converts an object to a UTF-8-encoded character array. Useful if + * you want to print the object. If conversion to a string fails + * (e.g. due to an exception in the toString() method of the object) + * then the length() method returns 0 and the * operator returns + * NULL. + */ + class V8_EXPORT Utf8Value { + public: + Utf8Value(Isolate* isolate, Local obj); + ~Utf8Value(); + char* operator*() { return str_; } + const char* operator*() const { return str_; } + int length() const { return length_; } + + // Disallow copying and assigning. + Utf8Value(const Utf8Value&) = delete; + void operator=(const Utf8Value&) = delete; + + private: + char* str_; + int length_; + }; + + /** + * Converts an object to a two-byte (UTF-16-encoded) string. + * If conversion to a string fails (eg. due to an exception in the toString() + * method of the object) then the length() method returns 0 and the * operator + * returns NULL. + */ + class V8_EXPORT Value { + public: + Value(Isolate* isolate, Local obj); + ~Value(); + uint16_t* operator*() { return str_; } + const uint16_t* operator*() const { return str_; } + int length() const { return length_; } + + // Disallow copying and assigning. + Value(const Value&) = delete; + void operator=(const Value&) = delete; + + private: + uint16_t* str_; + int length_; + }; + + private: + void VerifyExternalStringResourceBase(ExternalStringResourceBase* v, + Encoding encoding) const; + void VerifyExternalStringResource(ExternalStringResource* val) const; + ExternalStringResource* GetExternalStringResourceSlow() const; + ExternalStringResourceBase* GetExternalStringResourceBaseSlow( + String::Encoding* encoding_out) const; + + static Local NewFromUtf8Literal(Isolate* isolate, + const char* literal, + NewStringType type, int length); + + static void CheckCast(v8::Data* that); +}; + +// Zero-length string specialization (templated string size includes +// terminator). +template <> +inline V8_WARN_UNUSED_RESULT Local String::NewFromUtf8Literal( + Isolate* isolate, const char (&literal)[1], NewStringType type) { + return String::Empty(isolate); +} + +/** + * Interface for iterating through all external resources in the heap. + */ +class V8_EXPORT ExternalResourceVisitor { + public: + virtual ~ExternalResourceVisitor() = default; + virtual void VisitExternalString(Local string) {} +}; + +/** + * A JavaScript symbol (ECMA-262 edition 6) + */ +class V8_EXPORT Symbol : public Name { + public: + /** + * Returns the description string of the symbol, or undefined if none. + */ + Local Description(Isolate* isolate) const; + + /** + * Create a symbol. If description is not empty, it will be used as the + * description. + */ + static Local New(Isolate* isolate, + Local description = Local()); + + /** + * Access global symbol registry. + * Note that symbols created this way are never collected, so + * they should only be used for statically fixed properties. + * Also, there is only one global name space for the descriptions used as + * keys. + * To minimize the potential for clashes, use qualified names as keys. + */ + static Local For(Isolate* isolate, Local description); + + /** + * Retrieve a global symbol. Similar to |For|, but using a separate + * registry that is not accessible by (and cannot clash with) JavaScript code. + */ + static Local ForApi(Isolate* isolate, Local description); + + // Well-known symbols + static Local GetAsyncIterator(Isolate* isolate); + static Local GetHasInstance(Isolate* isolate); + static Local GetIsConcatSpreadable(Isolate* isolate); + static Local GetIterator(Isolate* isolate); + static Local GetMatch(Isolate* isolate); + static Local GetReplace(Isolate* isolate); + static Local GetSearch(Isolate* isolate); + static Local GetSplit(Isolate* isolate); + static Local GetToPrimitive(Isolate* isolate); + static Local GetToStringTag(Isolate* isolate); + static Local GetUnscopables(Isolate* isolate); + + V8_INLINE static Symbol* Cast(Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + private: + Symbol(); + static void CheckCast(Data* that); +}; + +/** + * A JavaScript number value (ECMA-262, 4.3.20) + */ +class V8_EXPORT Number : public Primitive { + public: + double Value() const; + static Local New(Isolate* isolate, double value); + V8_INLINE static Number* Cast(v8::Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + private: + Number(); + static void CheckCast(v8::Data* that); +}; + +/** + * A JavaScript value representing a signed integer. + */ +class V8_EXPORT Integer : public Number { + public: + static Local New(Isolate* isolate, int32_t value); + static Local NewFromUnsigned(Isolate* isolate, uint32_t value); + int64_t Value() const; + V8_INLINE static Integer* Cast(v8::Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + private: + Integer(); + static void CheckCast(v8::Data* that); +}; + +/** + * A JavaScript value representing a 32-bit signed integer. + */ +class V8_EXPORT Int32 : public Integer { + public: + int32_t Value() const; + V8_INLINE static Int32* Cast(v8::Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + private: + Int32(); + static void CheckCast(v8::Data* that); +}; + +/** + * A JavaScript value representing a 32-bit unsigned integer. + */ +class V8_EXPORT Uint32 : public Integer { + public: + uint32_t Value() const; + V8_INLINE static Uint32* Cast(v8::Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + private: + Uint32(); + static void CheckCast(v8::Data* that); +}; + +/** + * A JavaScript BigInt value (https://tc39.github.io/proposal-bigint) + */ +class V8_EXPORT BigInt : public Primitive { + public: + static Local New(Isolate* isolate, int64_t value); + static Local NewFromUnsigned(Isolate* isolate, uint64_t value); + /** + * Creates a new BigInt object using a specified sign bit and a + * specified list of digits/words. + * The resulting number is calculated as: + * + * (-1)^sign_bit * (words[0] * (2^64)^0 + words[1] * (2^64)^1 + ...) + */ + static MaybeLocal NewFromWords(Local context, int sign_bit, + int word_count, const uint64_t* words); + + /** + * Returns the value of this BigInt as an unsigned 64-bit integer. + * If `lossless` is provided, it will reflect whether the return value was + * truncated or wrapped around. In particular, it is set to `false` if this + * BigInt is negative. + */ + uint64_t Uint64Value(bool* lossless = nullptr) const; + + /** + * Returns the value of this BigInt as a signed 64-bit integer. + * If `lossless` is provided, it will reflect whether this BigInt was + * truncated or not. + */ + int64_t Int64Value(bool* lossless = nullptr) const; + + /** + * Returns the number of 64-bit words needed to store the result of + * ToWordsArray(). + */ + int WordCount() const; + + /** + * Writes the contents of this BigInt to a specified memory location. + * `sign_bit` must be provided and will be set to 1 if this BigInt is + * negative. + * `*word_count` has to be initialized to the length of the `words` array. + * Upon return, it will be set to the actual number of words that would + * be needed to store this BigInt (i.e. the return value of `WordCount()`). + */ + void ToWordsArray(int* sign_bit, int* word_count, uint64_t* words) const; + + V8_INLINE static BigInt* Cast(v8::Data* data) { +#ifdef V8_ENABLE_CHECKS + CheckCast(data); +#endif + return static_cast(data); + } + + private: + BigInt(); + static void CheckCast(v8::Data* that); +}; + +Local String::Empty(Isolate* isolate) { + using S = internal::Address; + using I = internal::Internals; + I::CheckInitialized(isolate); + S* slot = I::GetRoot(isolate, I::kEmptyStringRootIndex); + return Local(reinterpret_cast(slot)); +} + +String::ExternalStringResource* String::GetExternalStringResource() const { + using A = internal::Address; + using I = internal::Internals; + A obj = *reinterpret_cast(this); + + ExternalStringResource* result; + if (I::IsExternalTwoByteString(I::GetInstanceType(obj))) { + Isolate* isolate = I::GetIsolateForSandbox(obj); + A value = I::ReadExternalPointerField( + isolate, obj, I::kStringResourceOffset); + result = reinterpret_cast(value); + } else { + result = GetExternalStringResourceSlow(); + } +#ifdef V8_ENABLE_CHECKS + VerifyExternalStringResource(result); +#endif + return result; +} + +String::ExternalStringResourceBase* String::GetExternalStringResourceBase( + String::Encoding* encoding_out) const { + using A = internal::Address; + using I = internal::Internals; + A obj = *reinterpret_cast(this); + int type = I::GetInstanceType(obj) & I::kStringRepresentationAndEncodingMask; + *encoding_out = static_cast(type & I::kStringEncodingMask); + ExternalStringResourceBase* resource; + if (type == I::kExternalOneByteRepresentationTag || + type == I::kExternalTwoByteRepresentationTag) { + Isolate* isolate = I::GetIsolateForSandbox(obj); + A value = I::ReadExternalPointerField( + isolate, obj, I::kStringResourceOffset); + resource = reinterpret_cast(value); + } else { + resource = GetExternalStringResourceBaseSlow(encoding_out); + } +#ifdef V8_ENABLE_CHECKS + VerifyExternalStringResourceBase(resource, *encoding_out); +#endif + return resource; +} + +// --- Statics --- + +V8_INLINE Local Undefined(Isolate* isolate) { + using S = internal::Address; + using I = internal::Internals; + I::CheckInitialized(isolate); + S* slot = I::GetRoot(isolate, I::kUndefinedValueRootIndex); + return Local(reinterpret_cast(slot)); +} + +V8_INLINE Local Null(Isolate* isolate) { + using S = internal::Address; + using I = internal::Internals; + I::CheckInitialized(isolate); + S* slot = I::GetRoot(isolate, I::kNullValueRootIndex); + return Local(reinterpret_cast(slot)); +} + +V8_INLINE Local True(Isolate* isolate) { + using S = internal::Address; + using I = internal::Internals; + I::CheckInitialized(isolate); + S* slot = I::GetRoot(isolate, I::kTrueValueRootIndex); + return Local(reinterpret_cast(slot)); +} + +V8_INLINE Local False(Isolate* isolate) { + using S = internal::Address; + using I = internal::Internals; + I::CheckInitialized(isolate); + S* slot = I::GetRoot(isolate, I::kFalseValueRootIndex); + return Local(reinterpret_cast(slot)); +} + +Local Boolean::New(Isolate* isolate, bool value) { + return value ? True(isolate) : False(isolate); +} + +} // namespace v8 + +#endif // INCLUDE_V8_PRIMITIVE_H_ diff --git a/deps/include/v8-profiler.h b/deps/include/v8-profiler.h new file mode 100755 index 0000000..6b73fc6 --- /dev/null +++ b/deps/include/v8-profiler.h @@ -0,0 +1,1277 @@ +// Copyright 2010 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_V8_PROFILER_H_ +#define V8_V8_PROFILER_H_ + +#include + +#include +#include +#include + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-message.h" // NOLINT(build/include_directory) +#include "v8-persistent-handle.h" // NOLINT(build/include_directory) + +/** + * Profiler support for the V8 JavaScript engine. + */ +namespace v8 { + +enum class EmbedderStateTag : uint8_t; +class HeapGraphNode; +struct HeapStatsUpdate; +class Object; +enum StateTag : int; + +using NativeObject = void*; +using SnapshotObjectId = uint32_t; +using ProfilerId = uint32_t; + +struct CpuProfileDeoptFrame { + int script_id; + size_t position; +}; + +namespace internal { +class CpuProfile; +} // namespace internal + +} // namespace v8 + +#ifdef V8_OS_WIN +template class V8_EXPORT std::vector; +#endif + +namespace v8 { + +struct V8_EXPORT CpuProfileDeoptInfo { + /** A pointer to a static string owned by v8. */ + const char* deopt_reason; + std::vector stack; +}; + +} // namespace v8 + +#ifdef V8_OS_WIN +template class V8_EXPORT std::vector; +#endif + +namespace v8 { + +/** + * CpuProfileNode represents a node in a call graph. + */ +class V8_EXPORT CpuProfileNode { + public: + struct LineTick { + /** The 1-based number of the source line where the function originates. */ + int line; + + /** The count of samples associated with the source line. */ + unsigned int hit_count; + }; + + // An annotation hinting at the source of a CpuProfileNode. + enum SourceType { + // User-supplied script with associated resource information. + kScript = 0, + // Native scripts and provided builtins. + kBuiltin = 1, + // Callbacks into native code. + kCallback = 2, + // VM-internal functions or state. + kInternal = 3, + // A node that failed to symbolize. + kUnresolved = 4, + }; + + /** Returns function name (empty string for anonymous functions.) */ + Local GetFunctionName() const; + + /** + * Returns function name (empty string for anonymous functions.) + * The string ownership is *not* passed to the caller. It stays valid until + * profile is deleted. The function is thread safe. + */ + const char* GetFunctionNameStr() const; + + /** Returns id of the script where function is located. */ + int GetScriptId() const; + + /** Returns resource name for script from where the function originates. */ + Local GetScriptResourceName() const; + + /** + * Returns resource name for script from where the function originates. + * The string ownership is *not* passed to the caller. It stays valid until + * profile is deleted. The function is thread safe. + */ + const char* GetScriptResourceNameStr() const; + + /** + * Return true if the script from where the function originates is flagged as + * being shared cross-origin. + */ + bool IsScriptSharedCrossOrigin() const; + + /** + * Returns the number, 1-based, of the line where the function originates. + * kNoLineNumberInfo if no line number information is available. + */ + int GetLineNumber() const; + + /** + * Returns 1-based number of the column where the function originates. + * kNoColumnNumberInfo if no column number information is available. + */ + int GetColumnNumber() const; + + /** + * Returns the number of the function's source lines that collect the samples. + */ + unsigned int GetHitLineCount() const; + + /** Returns the set of source lines that collect the samples. + * The caller allocates buffer and responsible for releasing it. + * True if all available entries are copied, otherwise false. + * The function copies nothing if buffer is not large enough. + */ + bool GetLineTicks(LineTick* entries, unsigned int length) const; + + /** Returns bailout reason for the function + * if the optimization was disabled for it. + */ + const char* GetBailoutReason() const; + + /** + * Returns the count of samples where the function was currently executing. + */ + unsigned GetHitCount() const; + + /** Returns id of the node. The id is unique within the tree */ + unsigned GetNodeId() const; + + /** + * Gets the type of the source which the node was captured from. + */ + SourceType GetSourceType() const; + + /** Returns child nodes count of the node. */ + int GetChildrenCount() const; + + /** Retrieves a child node by index. */ + const CpuProfileNode* GetChild(int index) const; + + /** Retrieves the ancestor node, or null if the root. */ + const CpuProfileNode* GetParent() const; + + /** Retrieves deopt infos for the node. */ + const std::vector& GetDeoptInfos() const; + + static const int kNoLineNumberInfo = Message::kNoLineNumberInfo; + static const int kNoColumnNumberInfo = Message::kNoColumnInfo; +}; + +/** + * An interface for exporting data from V8, using "push" model. + */ +class V8_EXPORT OutputStream { + public: + enum WriteResult { kContinue = 0, kAbort = 1 }; + virtual ~OutputStream() = default; + /** Notify about the end of stream. */ + virtual void EndOfStream() = 0; + /** Get preferred output chunk size. Called only once. */ + virtual int GetChunkSize() { return 1024; } + /** + * Writes the next chunk of snapshot data into the stream. Writing + * can be stopped by returning kAbort as function result. EndOfStream + * will not be called in case writing was aborted. + */ + virtual WriteResult WriteAsciiChunk(char* data, int size) = 0; + /** + * Writes the next chunk of heap stats data into the stream. Writing + * can be stopped by returning kAbort as function result. EndOfStream + * will not be called in case writing was aborted. + */ + virtual WriteResult WriteHeapStatsChunk(HeapStatsUpdate* data, int count) { + return kAbort; + } +}; + +/** + * CpuProfile contains a CPU profile in a form of top-down call tree + * (from main() down to functions that do all the work). + */ +class V8_EXPORT CpuProfile { + public: + enum SerializationFormat { + kJSON = 0 // See format description near 'Serialize' method. + }; + /** Returns CPU profile title. */ + Local GetTitle() const; + + /** Returns the root node of the top down call tree. */ + const CpuProfileNode* GetTopDownRoot() const; + + /** + * Returns number of samples recorded. The samples are not recorded unless + * |record_samples| parameter of CpuProfiler::StartCpuProfiling is true. + */ + int GetSamplesCount() const; + + /** + * Returns profile node corresponding to the top frame the sample at + * the given index. + */ + const CpuProfileNode* GetSample(int index) const; + + /** + * Returns the timestamp of the sample. The timestamp is the number of + * microseconds since some unspecified starting point. + * The point is equal to the starting point used by GetStartTime. + */ + int64_t GetSampleTimestamp(int index) const; + + /** + * Returns time when the profile recording was started (in microseconds) + * since some unspecified starting point. + */ + int64_t GetStartTime() const; + + /** + * Returns state of the vm when sample was captured. + */ + StateTag GetSampleState(int index) const; + + /** + * Returns state of the embedder when sample was captured. + */ + EmbedderStateTag GetSampleEmbedderState(int index) const; + + /** + * Returns time when the profile recording was stopped (in microseconds) + * since some unspecified starting point. + * The point is equal to the starting point used by GetStartTime. + */ + int64_t GetEndTime() const; + + /** + * Deletes the profile and removes it from CpuProfiler's list. + * All pointers to nodes previously returned become invalid. + */ + void Delete(); + + /** + * Prepare a serialized representation of the profile. The result + * is written into the stream provided in chunks of specified size. + * + * For the JSON format, heap contents are represented as an object + * with the following structure: + * + * { + * nodes: [nodes array], + * startTime: number, + * endTime: number + * samples: [strings array] + * timeDeltas: [numbers array] + * } + * + */ + void Serialize(OutputStream* stream, + SerializationFormat format = kJSON) const; +}; + +enum CpuProfilingMode { + // In the resulting CpuProfile tree, intermediate nodes in a stack trace + // (from the root to a leaf) will have line numbers that point to the start + // line of the function, rather than the line of the callsite of the child. + kLeafNodeLineNumbers, + // In the resulting CpuProfile tree, nodes are separated based on the line + // number of their callsite in their parent. + kCallerLineNumbers, +}; + +// Determines how names are derived for functions sampled. +enum CpuProfilingNamingMode { + // Use the immediate name of functions at compilation time. + kStandardNaming, + // Use more verbose naming for functions without names, inferred from scope + // where possible. + kDebugNaming, +}; + +enum CpuProfilingLoggingMode { + // Enables logging when a profile is active, and disables logging when all + // profiles are detached. + kLazyLogging, + // Enables logging for the lifetime of the CpuProfiler. Calls to + // StartRecording are faster, at the expense of runtime overhead. + kEagerLogging, +}; + +// Enum for returning profiling status. Once StartProfiling is called, +// we want to return to clients whether the profiling was able to start +// correctly, or return a descriptive error. +enum class CpuProfilingStatus { + kStarted, + kAlreadyStarted, + kErrorTooManyProfilers +}; + +/** + * Result from StartProfiling returning the Profiling Status, and + * id of the started profiler, or 0 if profiler is not started + */ +struct CpuProfilingResult { + const ProfilerId id; + const CpuProfilingStatus status; +}; + +/** + * Delegate for when max samples reached and samples are discarded. + */ +class V8_EXPORT DiscardedSamplesDelegate { + public: + DiscardedSamplesDelegate() = default; + + virtual ~DiscardedSamplesDelegate() = default; + virtual void Notify() = 0; + + ProfilerId GetId() const { return profiler_id_; } + + private: + friend internal::CpuProfile; + + void SetId(ProfilerId id) { profiler_id_ = id; } + + ProfilerId profiler_id_; +}; + +/** + * Optional profiling attributes. + */ +class V8_EXPORT CpuProfilingOptions { + public: + // Indicates that the sample buffer size should not be explicitly limited. + static const unsigned kNoSampleLimit = UINT_MAX; + + /** + * \param mode Type of computation of stack frame line numbers. + * \param max_samples The maximum number of samples that should be recorded by + * the profiler. Samples obtained after this limit will be + * discarded. + * \param sampling_interval_us controls the profile-specific target + * sampling interval. The provided sampling + * interval will be snapped to the next lowest + * non-zero multiple of the profiler's sampling + * interval, set via SetSamplingInterval(). If + * zero, the sampling interval will be equal to + * the profiler's sampling interval. + * \param filter_context If specified, profiles will only contain frames + * using this context. Other frames will be elided. + */ + CpuProfilingOptions( + CpuProfilingMode mode = kLeafNodeLineNumbers, + unsigned max_samples = kNoSampleLimit, int sampling_interval_us = 0, + MaybeLocal filter_context = MaybeLocal()); + + CpuProfilingOptions(CpuProfilingOptions&&) = default; + CpuProfilingOptions& operator=(CpuProfilingOptions&&) = default; + + CpuProfilingMode mode() const { return mode_; } + unsigned max_samples() const { return max_samples_; } + int sampling_interval_us() const { return sampling_interval_us_; } + + private: + friend class internal::CpuProfile; + + bool has_filter_context() const { return !filter_context_.IsEmpty(); } + void* raw_filter_context() const; + + CpuProfilingMode mode_; + unsigned max_samples_; + int sampling_interval_us_; + Global filter_context_; +}; + +/** + * Interface for controlling CPU profiling. Instance of the + * profiler can be created using v8::CpuProfiler::New method. + */ +class V8_EXPORT CpuProfiler { + public: + /** + * Creates a new CPU profiler for the |isolate|. The isolate must be + * initialized. The profiler object must be disposed after use by calling + * |Dispose| method. + */ + static CpuProfiler* New(Isolate* isolate, + CpuProfilingNamingMode = kDebugNaming, + CpuProfilingLoggingMode = kLazyLogging); + + /** + * Synchronously collect current stack sample in all profilers attached to + * the |isolate|. The call does not affect number of ticks recorded for + * the current top node. + */ + static void CollectSample(Isolate* isolate); + + /** + * Disposes the CPU profiler object. + */ + void Dispose(); + + /** + * Changes default CPU profiler sampling interval to the specified number + * of microseconds. Default interval is 1000us. This method must be called + * when there are no profiles being recorded. + */ + void SetSamplingInterval(int us); + + /** + * Sets whether or not the profiler should prioritize consistency of sample + * periodicity on Windows. Disabling this can greatly reduce CPU usage, but + * may result in greater variance in sample timings from the platform's + * scheduler. Defaults to enabled. This method must be called when there are + * no profiles being recorded. + */ + void SetUsePreciseSampling(bool); + + /** + * Starts collecting a CPU profile. Several profiles may be collected at once. + * Generates an anonymous profiler, without a String identifier. + */ + CpuProfilingResult Start( + CpuProfilingOptions options, + std::unique_ptr delegate = nullptr); + + /** + * Starts collecting a CPU profile. Title may be an empty string. Several + * profiles may be collected at once. Attempts to start collecting several + * profiles with the same title are silently ignored. + */ + CpuProfilingResult Start( + Local title, CpuProfilingOptions options, + std::unique_ptr delegate = nullptr); + + /** + * Starts profiling with the same semantics as above, except with expanded + * parameters. + * + * |record_samples| parameter controls whether individual samples should + * be recorded in addition to the aggregated tree. + * + * |max_samples| controls the maximum number of samples that should be + * recorded by the profiler. Samples obtained after this limit will be + * discarded. + */ + CpuProfilingResult Start( + Local title, CpuProfilingMode mode, bool record_samples = false, + unsigned max_samples = CpuProfilingOptions::kNoSampleLimit); + + /** + * The same as StartProfiling above, but the CpuProfilingMode defaults to + * kLeafNodeLineNumbers mode, which was the previous default behavior of the + * profiler. + */ + CpuProfilingResult Start(Local title, bool record_samples = false); + + /** + * Starts collecting a CPU profile. Title may be an empty string. Several + * profiles may be collected at once. Attempts to start collecting several + * profiles with the same title are silently ignored. + */ + CpuProfilingStatus StartProfiling( + Local title, CpuProfilingOptions options, + std::unique_ptr delegate = nullptr); + + /** + * Starts profiling with the same semantics as above, except with expanded + * parameters. + * + * |record_samples| parameter controls whether individual samples should + * be recorded in addition to the aggregated tree. + * + * |max_samples| controls the maximum number of samples that should be + * recorded by the profiler. Samples obtained after this limit will be + * discarded. + */ + CpuProfilingStatus StartProfiling( + Local title, CpuProfilingMode mode, bool record_samples = false, + unsigned max_samples = CpuProfilingOptions::kNoSampleLimit); + + /** + * The same as StartProfiling above, but the CpuProfilingMode defaults to + * kLeafNodeLineNumbers mode, which was the previous default behavior of the + * profiler. + */ + CpuProfilingStatus StartProfiling(Local title, + bool record_samples = false); + + /** + * Stops collecting CPU profile with a given id and returns it. + */ + CpuProfile* Stop(ProfilerId id); + + /** + * Stops collecting CPU profile with a given title and returns it. + * If the title given is empty, finishes the last profile started. + */ + CpuProfile* StopProfiling(Local title); + + /** + * Generate more detailed source positions to code objects. This results in + * better results when mapping profiling samples to script source. + */ + static void UseDetailedSourcePositionsForProfiling(Isolate* isolate); + + private: + CpuProfiler(); + ~CpuProfiler(); + CpuProfiler(const CpuProfiler&); + CpuProfiler& operator=(const CpuProfiler&); +}; + +/** + * HeapSnapshotEdge represents a directed connection between heap + * graph nodes: from retainers to retained nodes. + */ +class V8_EXPORT HeapGraphEdge { + public: + enum Type { + kContextVariable = 0, // A variable from a function context. + kElement = 1, // An element of an array. + kProperty = 2, // A named object property. + kInternal = 3, // A link that can't be accessed from JS, + // thus, its name isn't a real property name + // (e.g. parts of a ConsString). + kHidden = 4, // A link that is needed for proper sizes + // calculation, but may be hidden from user. + kShortcut = 5, // A link that must not be followed during + // sizes calculation. + kWeak = 6 // A weak reference (ignored by the GC). + }; + + /** Returns edge type (see HeapGraphEdge::Type). */ + Type GetType() const; + + /** + * Returns edge name. This can be a variable name, an element index, or + * a property name. + */ + Local GetName() const; + + /** Returns origin node. */ + const HeapGraphNode* GetFromNode() const; + + /** Returns destination node. */ + const HeapGraphNode* GetToNode() const; +}; + + +/** + * HeapGraphNode represents a node in a heap graph. + */ +class V8_EXPORT HeapGraphNode { + public: + enum Type { + kHidden = 0, // Hidden node, may be filtered when shown to user. + kArray = 1, // An array of elements. + kString = 2, // A string. + kObject = 3, // A JS object (except for arrays and strings). + kCode = 4, // Compiled code. + kClosure = 5, // Function closure. + kRegExp = 6, // RegExp. + kHeapNumber = 7, // Number stored in the heap. + kNative = 8, // Native object (not from V8 heap). + kSynthetic = 9, // Synthetic object, usually used for grouping + // snapshot items together. + kConsString = 10, // Concatenated string. A pair of pointers to strings. + kSlicedString = 11, // Sliced string. A fragment of another string. + kSymbol = 12, // A Symbol (ES6). + kBigInt = 13, // BigInt. + kObjectShape = 14, // Internal data used for tracking the shapes (or + // "hidden classes") of JS objects. + }; + + /** Returns node type (see HeapGraphNode::Type). */ + Type GetType() const; + + /** + * Returns node name. Depending on node's type this can be the name + * of the constructor (for objects), the name of the function (for + * closures), string value, or an empty string (for compiled code). + */ + Local GetName() const; + + /** + * Returns node id. For the same heap object, the id remains the same + * across all snapshots. + */ + SnapshotObjectId GetId() const; + + /** Returns node's own size, in bytes. */ + size_t GetShallowSize() const; + + /** Returns child nodes count of the node. */ + int GetChildrenCount() const; + + /** Retrieves a child by index. */ + const HeapGraphEdge* GetChild(int index) const; +}; + +/** + * HeapSnapshots record the state of the JS heap at some moment. + */ +class V8_EXPORT HeapSnapshot { + public: + enum SerializationFormat { + kJSON = 0 // See format description near 'Serialize' method. + }; + + /** Returns the root node of the heap graph. */ + const HeapGraphNode* GetRoot() const; + + /** Returns a node by its id. */ + const HeapGraphNode* GetNodeById(SnapshotObjectId id) const; + + /** Returns total nodes count in the snapshot. */ + int GetNodesCount() const; + + /** Returns a node by index. */ + const HeapGraphNode* GetNode(int index) const; + + /** Returns a max seen JS object Id. */ + SnapshotObjectId GetMaxSnapshotJSObjectId() const; + + /** + * Deletes the snapshot and removes it from HeapProfiler's list. + * All pointers to nodes, edges and paths previously returned become + * invalid. + */ + void Delete(); + + /** + * Prepare a serialized representation of the snapshot. The result + * is written into the stream provided in chunks of specified size. + * The total length of the serialized snapshot is unknown in + * advance, it can be roughly equal to JS heap size (that means, + * it can be really big - tens of megabytes). + * + * For the JSON format, heap contents are represented as an object + * with the following structure: + * + * { + * snapshot: { + * title: "...", + * uid: nnn, + * meta: { meta-info }, + * node_count: nnn, + * edge_count: nnn + * }, + * nodes: [nodes array], + * edges: [edges array], + * strings: [strings array] + * } + * + * Nodes reference strings, other nodes, and edges by their indexes + * in corresponding arrays. + */ + void Serialize(OutputStream* stream, + SerializationFormat format = kJSON) const; +}; + + +/** + * An interface for reporting progress and controlling long-running + * activities. + */ +class V8_EXPORT ActivityControl { + public: + enum ControlOption { + kContinue = 0, + kAbort = 1 + }; + virtual ~ActivityControl() = default; + /** + * Notify about current progress. The activity can be stopped by + * returning kAbort as the callback result. + */ + virtual ControlOption ReportProgressValue(uint32_t done, uint32_t total) = 0; +}; + +/** + * AllocationProfile is a sampled profile of allocations done by the program. + * This is structured as a call-graph. + */ +class V8_EXPORT AllocationProfile { + public: + struct Allocation { + /** + * Size of the sampled allocation object. + */ + size_t size; + + /** + * The number of objects of such size that were sampled. + */ + unsigned int count; + }; + + /** + * Represents a node in the call-graph. + */ + struct Node { + /** + * Name of the function. May be empty for anonymous functions or if the + * script corresponding to this function has been unloaded. + */ + Local name; + + /** + * Name of the script containing the function. May be empty if the script + * name is not available, or if the script has been unloaded. + */ + Local script_name; + + /** + * id of the script where the function is located. May be equal to + * v8::UnboundScript::kNoScriptId in cases where the script doesn't exist. + */ + int script_id; + + /** + * Start position of the function in the script. + */ + int start_position; + + /** + * 1-indexed line number where the function starts. May be + * kNoLineNumberInfo if no line number information is available. + */ + int line_number; + + /** + * 1-indexed column number where the function starts. May be + * kNoColumnNumberInfo if no line number information is available. + */ + int column_number; + + /** + * Unique id of the node. + */ + uint32_t node_id; + + /** + * List of callees called from this node for which we have sampled + * allocations. The lifetime of the children is scoped to the containing + * AllocationProfile. + */ + std::vector children; + + /** + * List of self allocations done by this node in the call-graph. + */ + std::vector allocations; + }; + + /** + * Represent a single sample recorded for an allocation. + */ + struct Sample { + /** + * id of the node in the profile tree. + */ + uint32_t node_id; + + /** + * Size of the sampled allocation object. + */ + size_t size; + + /** + * The number of objects of such size that were sampled. + */ + unsigned int count; + + /** + * Unique time-ordered id of the allocation sample. Can be used to track + * what samples were added or removed between two snapshots. + */ + uint64_t sample_id; + }; + + /** + * Returns the root node of the call-graph. The root node corresponds to an + * empty JS call-stack. The lifetime of the returned Node* is scoped to the + * containing AllocationProfile. + */ + virtual Node* GetRootNode() = 0; + virtual const std::vector& GetSamples() = 0; + + virtual ~AllocationProfile() = default; + + static const int kNoLineNumberInfo = Message::kNoLineNumberInfo; + static const int kNoColumnNumberInfo = Message::kNoColumnInfo; +}; + +/** + * An object graph consisting of embedder objects and V8 objects. + * Edges of the graph are strong references between the objects. + * The embedder can build this graph during heap snapshot generation + * to include the embedder objects in the heap snapshot. + * Usage: + * 1) Define derived class of EmbedderGraph::Node for embedder objects. + * 2) Set the build embedder graph callback on the heap profiler using + * HeapProfiler::AddBuildEmbedderGraphCallback. + * 3) In the callback use graph->AddEdge(node1, node2) to add an edge from + * node1 to node2. + * 4) To represent references from/to V8 object, construct V8 nodes using + * graph->V8Node(value). + */ +class V8_EXPORT EmbedderGraph { + public: + class Node { + public: + /** + * Detachedness specifies whether an object is attached or detached from the + * main application state. While unkown in general, there may be objects + * that specifically know their state. V8 passes this information along in + * the snapshot. Users of the snapshot may use it to annotate the object + * graph. + */ + enum class Detachedness : uint8_t { + kUnknown = 0, + kAttached = 1, + kDetached = 2, + }; + + Node() = default; + virtual ~Node() = default; + virtual const char* Name() = 0; + virtual size_t SizeInBytes() = 0; + /** + * The corresponding V8 wrapper node if not null. + * During heap snapshot generation the embedder node and the V8 wrapper + * node will be merged into one node to simplify retaining paths. + */ + virtual Node* WrapperNode() { return nullptr; } + virtual bool IsRootNode() { return false; } + /** Must return true for non-V8 nodes. */ + virtual bool IsEmbedderNode() { return true; } + /** + * Optional name prefix. It is used in Chrome for tagging detached nodes. + */ + virtual const char* NamePrefix() { return nullptr; } + + /** + * Returns the NativeObject that can be used for querying the + * |HeapSnapshot|. + */ + virtual NativeObject GetNativeObject() { return nullptr; } + + /** + * Detachedness state of a given object. While unkown in general, there may + * be objects that specifically know their state. V8 passes this information + * along in the snapshot. Users of the snapshot may use it to annotate the + * object graph. + */ + virtual Detachedness GetDetachedness() { return Detachedness::kUnknown; } + + Node(const Node&) = delete; + Node& operator=(const Node&) = delete; + }; + + /** + * Returns a node corresponding to the given V8 value. Ownership is not + * transferred. The result pointer is valid while the graph is alive. + */ + virtual Node* V8Node(const v8::Local& value) = 0; + + /** + * Adds the given node to the graph and takes ownership of the node. + * Returns a raw pointer to the node that is valid while the graph is alive. + */ + virtual Node* AddNode(std::unique_ptr node) = 0; + + /** + * Adds an edge that represents a strong reference from the given + * node |from| to the given node |to|. The nodes must be added to the graph + * before calling this function. + * + * If name is nullptr, the edge will have auto-increment indexes, otherwise + * it will be named accordingly. + */ + virtual void AddEdge(Node* from, Node* to, const char* name = nullptr) = 0; + + virtual ~EmbedderGraph() = default; +}; + +/** + * Interface for controlling heap profiling. Instance of the + * profiler can be retrieved using v8::Isolate::GetHeapProfiler. + */ +class V8_EXPORT HeapProfiler { + public: + enum SamplingFlags { + kSamplingNoFlags = 0, + kSamplingForceGC = 1 << 0, + kSamplingIncludeObjectsCollectedByMajorGC = 1 << 1, + kSamplingIncludeObjectsCollectedByMinorGC = 1 << 2, + }; + + /** + * Callback function invoked during heap snapshot generation to retrieve + * the embedder object graph. The callback should use graph->AddEdge(..) to + * add references between the objects. + * The callback must not trigger garbage collection in V8. + */ + typedef void (*BuildEmbedderGraphCallback)(v8::Isolate* isolate, + v8::EmbedderGraph* graph, + void* data); + + /** + * Callback function invoked during heap snapshot generation to retrieve + * the detachedness state of an object referenced by a TracedReference. + * + * The callback takes Local as parameter to allow the embedder to + * unpack the TracedReference into a Local and reuse that Local for different + * purposes. + */ + using GetDetachednessCallback = EmbedderGraph::Node::Detachedness (*)( + v8::Isolate* isolate, const v8::Local& v8_value, + uint16_t class_id, void* data); + + /** Returns the number of snapshots taken. */ + int GetSnapshotCount(); + + /** Returns a snapshot by index. */ + const HeapSnapshot* GetHeapSnapshot(int index); + + /** + * Returns SnapshotObjectId for a heap object referenced by |value| if + * it has been seen by the heap profiler, kUnknownObjectId otherwise. + */ + SnapshotObjectId GetObjectId(Local value); + + /** + * Returns SnapshotObjectId for a native object referenced by |value| if it + * has been seen by the heap profiler, kUnknownObjectId otherwise. + */ + SnapshotObjectId GetObjectId(NativeObject value); + + /** + * Returns heap object with given SnapshotObjectId if the object is alive, + * otherwise empty handle is returned. + */ + Local FindObjectById(SnapshotObjectId id); + + /** + * Clears internal map from SnapshotObjectId to heap object. The new objects + * will not be added into it unless a heap snapshot is taken or heap object + * tracking is kicked off. + */ + void ClearObjectIds(); + + /** + * A constant for invalid SnapshotObjectId. GetSnapshotObjectId will return + * it in case heap profiler cannot find id for the object passed as + * parameter. HeapSnapshot::GetNodeById will always return NULL for such id. + */ + static const SnapshotObjectId kUnknownObjectId = 0; + + /** + * Callback interface for retrieving user friendly names of global objects. + */ + class ObjectNameResolver { + public: + /** + * Returns name to be used in the heap snapshot for given node. Returned + * string must stay alive until snapshot collection is completed. + */ + virtual const char* GetName(Local object) = 0; + + protected: + virtual ~ObjectNameResolver() = default; + }; + + enum class HeapSnapshotMode { + /** + * Heap snapshot for regular developers. + */ + kRegular, + /** + * Heap snapshot is exposing internals that may be useful for experts. + */ + kExposeInternals, + }; + + enum class NumericsMode { + /** + * Numeric values are hidden as they are values of the corresponding + * objects. + */ + kHideNumericValues, + /** + * Numeric values are exposed in artificial fields. + */ + kExposeNumericValues + }; + + struct HeapSnapshotOptions final { + // Manually define default constructor here to be able to use it in + // `TakeSnapshot()` below. + // NOLINTNEXTLINE + HeapSnapshotOptions() {} + + /** + * The control used to report intermediate progress to. + */ + ActivityControl* control = nullptr; + /** + * The resolver used by the snapshot generator to get names for V8 objects. + */ + ObjectNameResolver* global_object_name_resolver = nullptr; + /** + * Mode for taking the snapshot, see `HeapSnapshotMode`. + */ + HeapSnapshotMode snapshot_mode = HeapSnapshotMode::kRegular; + /** + * Mode for dealing with numeric values, see `NumericsMode`. + */ + NumericsMode numerics_mode = NumericsMode::kHideNumericValues; + }; + + /** + * Takes a heap snapshot. + * + * \returns the snapshot. + */ + const HeapSnapshot* TakeHeapSnapshot( + const HeapSnapshotOptions& options = HeapSnapshotOptions()); + + /** + * Takes a heap snapshot. See `HeapSnapshotOptions` for details on the + * parameters. + * + * \returns the snapshot. + */ + const HeapSnapshot* TakeHeapSnapshot( + ActivityControl* control, + ObjectNameResolver* global_object_name_resolver = nullptr, + bool hide_internals = true, bool capture_numeric_value = false); + + /** + * Starts tracking of heap objects population statistics. After calling + * this method, all heap objects relocations done by the garbage collector + * are being registered. + * + * |track_allocations| parameter controls whether stack trace of each + * allocation in the heap will be recorded and reported as part of + * HeapSnapshot. + */ + void StartTrackingHeapObjects(bool track_allocations = false); + + /** + * Adds a new time interval entry to the aggregated statistics array. The + * time interval entry contains information on the current heap objects + * population size. The method also updates aggregated statistics and + * reports updates for all previous time intervals via the OutputStream + * object. Updates on each time interval are provided as a stream of the + * HeapStatsUpdate structure instances. + * If |timestamp_us| is supplied, timestamp of the new entry will be written + * into it. The return value of the function is the last seen heap object Id. + * + * StartTrackingHeapObjects must be called before the first call to this + * method. + */ + SnapshotObjectId GetHeapStats(OutputStream* stream, + int64_t* timestamp_us = nullptr); + + /** + * Stops tracking of heap objects population statistics, cleans up all + * collected data. StartHeapObjectsTracking must be called again prior to + * calling GetHeapStats next time. + */ + void StopTrackingHeapObjects(); + + /** + * Starts gathering a sampling heap profile. A sampling heap profile is + * similar to tcmalloc's heap profiler and Go's mprof. It samples object + * allocations and builds an online 'sampling' heap profile. At any point in + * time, this profile is expected to be a representative sample of objects + * currently live in the system. Each sampled allocation includes the stack + * trace at the time of allocation, which makes this really useful for memory + * leak detection. + * + * This mechanism is intended to be cheap enough that it can be used in + * production with minimal performance overhead. + * + * Allocations are sampled using a randomized Poisson process. On average, one + * allocation will be sampled every |sample_interval| bytes allocated. The + * |stack_depth| parameter controls the maximum number of stack frames to be + * captured on each allocation. + * + * NOTE: Support for native allocations doesn't exist yet, but is anticipated + * in the future. + * + * Objects allocated before the sampling is started will not be included in + * the profile. + * + * Returns false if a sampling heap profiler is already running. + */ + bool StartSamplingHeapProfiler(uint64_t sample_interval = 512 * 1024, + int stack_depth = 16, + SamplingFlags flags = kSamplingNoFlags); + + /** + * Stops the sampling heap profile and discards the current profile. + */ + void StopSamplingHeapProfiler(); + + /** + * Returns the sampled profile of allocations allocated (and still live) since + * StartSamplingHeapProfiler was called. The ownership of the pointer is + * transferred to the caller. Returns nullptr if sampling heap profiler is not + * active. + */ + AllocationProfile* GetAllocationProfile(); + + /** + * Deletes all snapshots taken. All previously returned pointers to + * snapshots and their contents become invalid after this call. + */ + void DeleteAllHeapSnapshots(); + + void AddBuildEmbedderGraphCallback(BuildEmbedderGraphCallback callback, + void* data); + void RemoveBuildEmbedderGraphCallback(BuildEmbedderGraphCallback callback, + void* data); + + void SetGetDetachednessCallback(GetDetachednessCallback callback, void* data); + + /** + * Default value of persistent handle class ID. Must not be used to + * define a class. Can be used to reset a class of a persistent + * handle. + */ + static const uint16_t kPersistentHandleNoClassId = 0; + + private: + HeapProfiler(); + ~HeapProfiler(); + HeapProfiler(const HeapProfiler&); + HeapProfiler& operator=(const HeapProfiler&); +}; + +/** + * A struct for exporting HeapStats data from V8, using "push" model. + * See HeapProfiler::GetHeapStats. + */ +struct HeapStatsUpdate { + HeapStatsUpdate(uint32_t index, uint32_t count, uint32_t size) + : index(index), count(count), size(size) { } + uint32_t index; // Index of the time interval that was changed. + uint32_t count; // New value of count field for the interval with this index. + uint32_t size; // New value of size field for the interval with this index. +}; + +#define CODE_EVENTS_LIST(V) \ + V(Builtin) \ + V(Callback) \ + V(Eval) \ + V(Function) \ + V(InterpretedFunction) \ + V(Handler) \ + V(BytecodeHandler) \ + V(LazyCompile) /* Unused, use kFunction instead */ \ + V(RegExp) \ + V(Script) \ + V(Stub) \ + V(Relocation) + +/** + * Note that this enum may be extended in the future. Please include a default + * case if this enum is used in a switch statement. + */ +enum CodeEventType { + kUnknownType = 0 +#define V(Name) , k##Name##Type + CODE_EVENTS_LIST(V) +#undef V +}; + +/** + * Representation of a code creation event + */ +class V8_EXPORT CodeEvent { + public: + uintptr_t GetCodeStartAddress(); + size_t GetCodeSize(); + Local GetFunctionName(); + Local GetScriptName(); + int GetScriptLine(); + int GetScriptColumn(); + /** + * NOTE (mmarchini): We can't allocate objects in the heap when we collect + * existing code, and both the code type and the comment are not stored in the + * heap, so we return those as const char*. + */ + CodeEventType GetCodeType(); + const char* GetComment(); + + static const char* GetCodeEventTypeName(CodeEventType code_event_type); + + uintptr_t GetPreviousCodeStartAddress(); +}; + +/** + * Interface to listen to code creation and code relocation events. + */ +class V8_EXPORT CodeEventHandler { + public: + /** + * Creates a new listener for the |isolate|. The isolate must be initialized. + * The listener object must be disposed after use by calling |Dispose| method. + * Multiple listeners can be created for the same isolate. + */ + explicit CodeEventHandler(Isolate* isolate); + virtual ~CodeEventHandler(); + + /** + * Handle is called every time a code object is created or moved. Information + * about each code event will be available through the `code_event` + * parameter. + * + * When the CodeEventType is kRelocationType, the code for this CodeEvent has + * moved from `GetPreviousCodeStartAddress()` to `GetCodeStartAddress()`. + */ + virtual void Handle(CodeEvent* code_event) = 0; + + /** + * Call `Enable()` to starts listening to code creation and code relocation + * events. These events will be handled by `Handle()`. + */ + void Enable(); + + /** + * Call `Disable()` to stop listening to code creation and code relocation + * events. + */ + void Disable(); + + private: + CodeEventHandler(); + CodeEventHandler(const CodeEventHandler&); + CodeEventHandler& operator=(const CodeEventHandler&); + void* internal_listener_; +}; + +} // namespace v8 + + +#endif // V8_V8_PROFILER_H_ diff --git a/deps/include/v8-promise.h b/deps/include/v8-promise.h new file mode 100755 index 0000000..9da8e4b --- /dev/null +++ b/deps/include/v8-promise.h @@ -0,0 +1,174 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_PROMISE_H_ +#define INCLUDE_V8_PROMISE_H_ + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-object.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; + +#ifndef V8_PROMISE_INTERNAL_FIELD_COUNT +// The number of required internal fields can be defined by embedder. +#define V8_PROMISE_INTERNAL_FIELD_COUNT 0 +#endif + +/** + * An instance of the built-in Promise constructor (ES6 draft). + */ +class V8_EXPORT Promise : public Object { + public: + /** + * State of the promise. Each value corresponds to one of the possible values + * of the [[PromiseState]] field. + */ + enum PromiseState { kPending, kFulfilled, kRejected }; + + class V8_EXPORT Resolver : public Object { + public: + /** + * Create a new resolver, along with an associated promise in pending state. + */ + static V8_WARN_UNUSED_RESULT MaybeLocal New( + Local context); + + /** + * Extract the associated promise. + */ + Local GetPromise(); + + /** + * Resolve/reject the associated promise with a given value. + * Ignored if the promise is no longer pending. + */ + V8_WARN_UNUSED_RESULT Maybe Resolve(Local context, + Local value); + + V8_WARN_UNUSED_RESULT Maybe Reject(Local context, + Local value); + + V8_INLINE static Resolver* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + Resolver(); + static void CheckCast(Value* obj); + }; + + /** + * Register a resolution/rejection handler with a promise. + * The handler is given the respective resolution/rejection value as + * an argument. If the promise is already resolved/rejected, the handler is + * invoked at the end of turn. + */ + V8_WARN_UNUSED_RESULT MaybeLocal Catch(Local context, + Local handler); + + V8_WARN_UNUSED_RESULT MaybeLocal Then(Local context, + Local handler); + + V8_WARN_UNUSED_RESULT MaybeLocal Then(Local context, + Local on_fulfilled, + Local on_rejected); + + /** + * Returns true if the promise has at least one derived promise, and + * therefore resolve/reject handlers (including default handler). + */ + bool HasHandler() const; + + /** + * Returns the content of the [[PromiseResult]] field. The Promise must not + * be pending. + */ + Local Result(); + + /** + * Returns the value of the [[PromiseState]] field. + */ + PromiseState State(); + + /** + * Marks this promise as handled to avoid reporting unhandled rejections. + */ + void MarkAsHandled(); + + /** + * Marks this promise as silent to prevent pausing the debugger when the + * promise is rejected. + */ + void MarkAsSilent(); + + V8_INLINE static Promise* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + static const int kEmbedderFieldCount = V8_PROMISE_INTERNAL_FIELD_COUNT; + + private: + Promise(); + static void CheckCast(Value* obj); +}; + +/** + * PromiseHook with type kInit is called when a new promise is + * created. When a new promise is created as part of the chain in the + * case of Promise.then or in the intermediate promises created by + * Promise.{race, all}/AsyncFunctionAwait, we pass the parent promise + * otherwise we pass undefined. + * + * PromiseHook with type kResolve is called at the beginning of + * resolve or reject function defined by CreateResolvingFunctions. + * + * PromiseHook with type kBefore is called at the beginning of the + * PromiseReactionJob. + * + * PromiseHook with type kAfter is called right at the end of the + * PromiseReactionJob. + */ +enum class PromiseHookType { kInit, kResolve, kBefore, kAfter }; + +using PromiseHook = void (*)(PromiseHookType type, Local promise, + Local parent); + +// --- Promise Reject Callback --- +enum PromiseRejectEvent { + kPromiseRejectWithNoHandler = 0, + kPromiseHandlerAddedAfterReject = 1, + kPromiseRejectAfterResolved = 2, + kPromiseResolveAfterResolved = 3, +}; + +class PromiseRejectMessage { + public: + PromiseRejectMessage(Local promise, PromiseRejectEvent event, + Local value) + : promise_(promise), event_(event), value_(value) {} + + V8_INLINE Local GetPromise() const { return promise_; } + V8_INLINE PromiseRejectEvent GetEvent() const { return event_; } + V8_INLINE Local GetValue() const { return value_; } + + private: + Local promise_; + PromiseRejectEvent event_; + Local value_; +}; + +using PromiseRejectCallback = void (*)(PromiseRejectMessage message); + +} // namespace v8 + +#endif // INCLUDE_V8_PROMISE_H_ diff --git a/deps/include/v8-proxy.h b/deps/include/v8-proxy.h new file mode 100755 index 0000000..a08db88 --- /dev/null +++ b/deps/include/v8-proxy.h @@ -0,0 +1,50 @@ + +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_PROXY_H_ +#define INCLUDE_V8_PROXY_H_ + +#include "v8-context.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-object.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; + +/** + * An instance of the built-in Proxy constructor (ECMA-262, 6th Edition, + * 26.2.1). + */ +class V8_EXPORT Proxy : public Object { + public: + Local GetTarget(); + Local GetHandler(); + bool IsRevoked() const; + void Revoke(); + + /** + * Creates a new Proxy for the target object. + */ + static MaybeLocal New(Local context, + Local local_target, + Local local_handler); + + V8_INLINE static Proxy* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + Proxy(); + static void CheckCast(Value* obj); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_PROXY_H_ diff --git a/deps/include/v8-regexp.h b/deps/include/v8-regexp.h new file mode 100755 index 0000000..135977b --- /dev/null +++ b/deps/include/v8-regexp.h @@ -0,0 +1,106 @@ + +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_REGEXP_H_ +#define INCLUDE_V8_REGEXP_H_ + +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-object.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Context; + +/** + * An instance of the built-in RegExp constructor (ECMA-262, 15.10). + */ +class V8_EXPORT RegExp : public Object { + public: + /** + * Regular expression flag bits. They can be or'ed to enable a set + * of flags. + * The kLinear value ('l') is experimental and can only be used with + * --enable-experimental-regexp-engine. RegExps with kLinear flag are + * guaranteed to be executed in asymptotic linear time wrt. the length of + * the subject string. + */ + enum Flags { + kNone = 0, + kGlobal = 1 << 0, + kIgnoreCase = 1 << 1, + kMultiline = 1 << 2, + kSticky = 1 << 3, + kUnicode = 1 << 4, + kDotAll = 1 << 5, + kLinear = 1 << 6, + kHasIndices = 1 << 7, + kUnicodeSets = 1 << 8, + }; + + static constexpr int kFlagCount = 9; + + /** + * Creates a regular expression from the given pattern string and + * the flags bit field. May throw a JavaScript exception as + * described in ECMA-262, 15.10.4.1. + * + * For example, + * RegExp::New(v8::String::New("foo"), + * static_cast(kGlobal | kMultiline)) + * is equivalent to evaluating "/foo/gm". + */ + static V8_WARN_UNUSED_RESULT MaybeLocal New(Local context, + Local pattern, + Flags flags); + + /** + * Like New, but additionally specifies a backtrack limit. If the number of + * backtracks done in one Exec call hits the limit, a match failure is + * immediately returned. + */ + static V8_WARN_UNUSED_RESULT MaybeLocal NewWithBacktrackLimit( + Local context, Local pattern, Flags flags, + uint32_t backtrack_limit); + + /** + * Executes the current RegExp instance on the given subject string. + * Equivalent to RegExp.prototype.exec as described in + * + * https://tc39.es/ecma262/#sec-regexp.prototype.exec + * + * On success, an Array containing the matched strings is returned. On + * failure, returns Null. + * + * Note: modifies global context state, accessible e.g. through RegExp.input. + */ + V8_WARN_UNUSED_RESULT MaybeLocal Exec(Local context, + Local subject); + + /** + * Returns the value of the source property: a string representing + * the regular expression. + */ + Local GetSource() const; + + /** + * Returns the flags bit field. + */ + Flags GetFlags() const; + + V8_INLINE static RegExp* Cast(Value* value) { +#ifdef V8_ENABLE_CHECKS + CheckCast(value); +#endif + return static_cast(value); + } + + private: + static void CheckCast(Value* obj); +}; + +} // namespace v8 + +#endif // INCLUDE_V8_REGEXP_H_ diff --git a/deps/include/v8-script.h b/deps/include/v8-script.h new file mode 100755 index 0000000..e2ba845 --- /dev/null +++ b/deps/include/v8-script.h @@ -0,0 +1,803 @@ +// Copyright 2021 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef INCLUDE_V8_SCRIPT_H_ +#define INCLUDE_V8_SCRIPT_H_ + +#include +#include + +#include +#include + +#include "v8-data.h" // NOLINT(build/include_directory) +#include "v8-local-handle.h" // NOLINT(build/include_directory) +#include "v8-maybe.h" // NOLINT(build/include_directory) +#include "v8-message.h" // NOLINT(build/include_directory) +#include "v8config.h" // NOLINT(build/include_directory) + +namespace v8 { + +class Function; +class Message; +class Object; +class PrimitiveArray; +class Script; + +namespace internal { +class BackgroundDeserializeTask; +struct ScriptStreamingData; +} // namespace internal + +/** + * A container type that holds relevant metadata for module loading. + * + * This is passed back to the embedder as part of + * HostImportModuleDynamicallyCallback for module loading. + */ +class V8_EXPORT ScriptOrModule { + public: + /** + * The name that was passed by the embedder as ResourceName to the + * ScriptOrigin. This can be either a v8::String or v8::Undefined. + */ + Local GetResourceName(); + + /** + * The options that were passed by the embedder as HostDefinedOptions to + * the ScriptOrigin. + */ + Local HostDefinedOptions(); +}; + +/** + * A compiled JavaScript script, not yet tied to a Context. + */ +class V8_EXPORT UnboundScript { + public: + /** + * Binds the script to the currently entered context. + */ + Local