├── .gclient_entries ├── .gitignore ├── .gitmodules ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── build.py ├── build.rs └── src ├── binding.cc ├── binding.h ├── binding.rs ├── handler.rs ├── lib.rs └── worker.rs /.gclient_entries: -------------------------------------------------------------------------------- 1 | entries = { 2 | 'v8': 'https://chromium.googlesource.com/v8/v8.git', 3 | 'v8/base/trace_event/common': 'https://chromium.googlesource.com/chromium/src/base/trace_event/common.git@211b3ed9d0481b4caddbee1322321b86a483ca1f', 4 | 'v8/build': 'https://chromium.googlesource.com/chromium/src/build.git@a429f6047e2d7fa309b40fe45dc6831497959847', 5 | 'v8/buildtools': 'https://chromium.googlesource.com/chromium/buildtools.git@893eb86b02b2571894e328f05551112b96df1cce', 6 | 'v8/test/benchmarks/data': None, 7 | 'v8/test/mozilla/data': None, 8 | 'v8/test/test262/data': None, 9 | 'v8/test/test262/harness': None, 10 | 'v8/test/wasm-js': None, 11 | 'v8/testing/gmock': None, 12 | 'v8/third_party/depot_tools': 'https://chromium.googlesource.com/chromium/tools/depot_tools.git@cf4aced37e993525b9e21856c0d1acec682edab1', 13 | 'v8/third_party/googletest/src': 'https://chromium.googlesource.com/external/github.com/google/googletest.git@145d05750b15324899473340c8dd5af50d125d33', 14 | 'v8/third_party/icu': 'https://chromium.googlesource.com/chromium/deps/icu.git@f61e46dbee9d539a32551493e3bcc1dea92f83ec', 15 | 'v8/third_party/instrumented_libraries': None, 16 | 'v8/third_party/jinja2': 'https://chromium.googlesource.com/chromium/src/third_party/jinja2.git@45571de473282bd1d8b63a8dfcb1fd268d0635d2', 17 | 'v8/third_party/markupsafe': 'https://chromium.googlesource.com/chromium/src/third_party/markupsafe.git@8f45f5cfa0009d2a70589bcda0349b8cb2b72783', 18 | 'v8/tools/clang': 'https://chromium.googlesource.com/chromium/src/tools/clang.git@c893c7eec4706f8c7fc244ee254b1dadd8f8d158', 19 | 'v8/tools/gyp': None, 20 | 'v8/tools/luci-go': None, 21 | 'v8/tools/swarming_client': None, 22 | 'v8/buildtools/clang_format/script': 'https://chromium.googlesource.com/chromium/llvm-project/cfe/tools/clang-format.git@0653eee0c81ea04715c635dd0885e8096ff6ba6d', 23 | 'v8/buildtools/third_party/libc++/trunk': 'https://chromium.googlesource.com/chromium/llvm-project/libcxx.git@85a7702b4cc5d69402791fe685f151cf3076be71', 24 | 'v8/buildtools/third_party/libc++abi/trunk': 'https://chromium.googlesource.com/chromium/llvm-project/libcxxabi.git@05a73941f3fb177c4a891d4ff2a4ed5785e3b80c', 25 | 'v8/buildtools/third_party/libunwind/trunk': 'https://chromium.googlesource.com/external/llvm.org/libunwind.git@1e1c6b739595098ba5c466bfe9d58b993e646b48', 26 | } 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | v8.pc 3 | target 4 | .vscode/ 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "depot_tools"] 2 | path = depot_tools 3 | url = https://chromium.googlesource.com/chromium/tools/depot_tools.git 4 | [submodule "v8"] 5 | path = v8 6 | url = https://github.com/v8/v8.git 7 | branch = 6.6.164 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c++ 2 | before_cache: 3 | - ccache -s 4 | cache: ccache 5 | before_install: 6 | - sudo apt-get -qq update 7 | - curl https://sh.rustup.rs -sSf > rustup.sh 8 | - chmod +x rustup.sh 9 | - ./rustup.sh -y 10 | - source ~/.cargo/env 11 | install: 12 | - cargo build -vv 13 | script: 14 | - cargo test -- --nocapture 15 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "v8worker2" 3 | version = "0.1.0" 4 | authors = ["Matias Insaurralde ", "Ryan Dahl "] 5 | description = "Minimal Rust binding to V8, based on ry/v8worker2" 6 | repository = "https://github.com/matiasinsaurralde/rust-v8worker2" 7 | readme = "README.md" 8 | license = "MIT" 9 | build = "build.rs" 10 | categories = ["api-bindings"] 11 | 12 | 13 | [dependencies] 14 | libc = "0.2" 15 | bytes = "0.4" 16 | 17 | [build-dependencies] 18 | cc = "1" 19 | pkg-config = "0.3.11" 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2015-2018 Ryan Dahl . All rights reserved for parts of v8worker2 source code. 2 | Copyright 2018 Matias Insaurralde All rights reserved for the Rust code present in this project. 3 | 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to 7 | deal in the Software without restriction, including without limitation the 8 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | sell copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## rust-v8worker2 2 | 3 | [![Build Status](https://travis-ci.org/matiasinsaurralde/rust-v8worker2.svg?branch=master)](https://travis-ci.org/matiasinsaurralde/rust-v8worker2) 4 | 5 | This is a minimal binding between Rust (and V8 JavaScript. Basic concept is to only expose two methods to JavaScript: send and receive. 6 | 7 | Based on [ry/v8worker2](https://github.com/ry/v8worker2). 8 | 9 | ## Usage 10 | 11 | Add this to your `Config.toml`: 12 | ```toml 13 | [dependencies] 14 | v8worker2 = { git = "https://github.com/matiasinsaurralde/rust-v8worker2" } 15 | ``` 16 | 17 | **Note:** If you include this repository as a dependency, the `cargo build` command may take some time to fetch and build V8, on my box it takes around 20 minutes. There's a condition that prevents this build step to run if the library is already present. If detailed build logging is required consider using `cargo build -vv`. 18 | 19 | To use the crate: 20 | 21 | ```rust 22 | extern crate v8worker2; 23 | extern crate bytes; 24 | 25 | use v8worker2::worker::Worker as Worker; 26 | 27 | fn main() { 28 | // Initialize V8 (V8::InitializePlatform and V8::Initialize): 29 | let mut _handler = v8worker2::new_handler(); 30 | _handler.init(); 31 | 32 | // Setup a callback that receives bytes and returns boxed bytes: 33 | let cb = |incoming_data: bytes::Bytes| -> Box { 34 | println!("Getting data from V8, length is {}", incoming_data.len()); 35 | 36 | // Send some stuff to V8, this is not in use at the moment but we still require it: 37 | let data = Bytes::from(&b"reply"[..]); 38 | Box::new(data) 39 | }; 40 | 41 | // Initialize a worker with the callback: 42 | let mut worker = Worker::new(cb); 43 | 44 | // Send an empty ArrayBuffer (V8 -> Rust), the callback will print the length of it: 45 | worker.load("code.js", "V8Worker2.send(new ArrayBuffer(10))".to_string()); 46 | } 47 | ``` 48 | 49 | ## Setup 50 | 51 | If you want to see the detailed output of the V8 build, run: 52 | ``` 53 | $ ./build.py --enable-ccache 54 | ``` 55 | 56 | Otherwise you may run `cargo build` directly, the V8 build steps are part of `build.rs`. 57 | 58 | To run the tests, use `cargo test -- --nocapture`. 59 | 60 | ## Requirements 61 | 62 | - Python (required by the build script) 63 | - C/C++ compiler 64 | - pkg-config 65 | - ccache (OS X: `brew install ccache`) 66 | - Git 67 | - Rust 1.26 or newer 68 | 69 | ## Supported platforms 70 | 71 | My development box runs OS X, the CI environment runs Linux. These two platforms should be ok. 72 | 73 | ## License 74 | 75 | This project contains code written by [Ryan Dahl](https://github.com/ry), I'm using the same license for my additional Rust codebase: 76 | 77 | [MIT](LICENSE) 78 | -------------------------------------------------------------------------------- /build.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import shutil 4 | import stat 5 | import subprocess 6 | import sys 7 | import distutils.spawn 8 | import argparse 9 | import platform 10 | 11 | parser = argparse.ArgumentParser(description="v8worker2 build.py") 12 | parser.add_argument('--rebuild', dest='rebuild', action='store_true') 13 | parser.add_argument('--use_ccache', dest='use_ccache', action='store_true') 14 | parser.add_argument('--out_path', nargs=1, dest='out_path', type=str, action='store') 15 | parser.add_argument('--depot_tools_path', nargs=1, dest='depot_tools_path', type=str, action='store') 16 | parser.set_defaults(rebuild=False, use_ccache=False, out_path=None) 17 | args = parser.parse_args() 18 | 19 | root_path = os.path.dirname(os.path.realpath(__file__)) 20 | prebuilt_path = os.path.join(root_path, "prebuilt") 21 | v8_path = os.path.join(root_path, "v8") 22 | depot_tools = os.path.join(root_path, "depot_tools") 23 | out_path = os.path.join(root_path, "out/") 24 | if args.out_path: 25 | out_path = args.out_path[0] 26 | print("out_path %s" % args.out_path) 27 | v8build_path = os.path.join(out_path, "v8build") 28 | if args.depot_tools_path: 29 | depot_tools = args.depot_tools_path[0] 30 | 31 | # To get a list of args 32 | # cd v8 && ../depot_tools/gn args ../out/v8build/ --list 33 | GN_ARGS = """ 34 | is_component_build=false 35 | is_debug=false 36 | libcpp_is_static=false 37 | symbol_level=1 38 | treat_warnings_as_errors=false 39 | use_custom_libcxx=false 40 | use_sysroot=false 41 | v8_deprecation_warnings=false 42 | v8_embedder_string="-v8worker2" 43 | v8_enable_gdbjit=false 44 | v8_enable_i18n_support=false 45 | v8_enable_test_features=false 46 | v8_experimental_extra_library_files=[] 47 | v8_extra_library_files=[] 48 | v8_imminent_deprecation_warnings=false 49 | v8_monolithic=true 50 | v8_static_library=false 51 | v8_target_cpu="x64" 52 | v8_untrusted_code_mitigations=false 53 | v8_use_external_startup_data=false 54 | v8_use_snapshot=true 55 | """ 56 | 57 | GCLIENT_SOLUTION = [ 58 | { "name" : "v8", 59 | "url" : "https://chromium.googlesource.com/v8/v8.git", 60 | "deps_file" : "DEPS", 61 | "managed" : False, 62 | "custom_deps" : { 63 | # These deps are unnecessary for building. 64 | "v8/test/benchmarks/data" : None, 65 | "v8/testing/gmock" : None, 66 | "v8/test/mozilla/data" : None, 67 | "v8/test/test262/data" : None, 68 | "v8/test/test262/harness" : None, 69 | "v8/test/wasm-js" : None, 70 | "v8/third_party/android_tools" : None, 71 | "v8/third_party/catapult" : None, 72 | "v8/third_party/colorama/src" : None, 73 | "v8/third_party/instrumented_libraries" : None, 74 | "v8/tools/gyp" : None, 75 | "v8/tools/luci-go" : None, 76 | "v8/tools/swarming_client" : None, 77 | }, 78 | "custom_vars": { 79 | "build_for_node" : True, 80 | }, 81 | }, 82 | ] 83 | 84 | def main(): 85 | lib_fn = os.path.join(prebuilt_path, platform_name(), "libv8_monolith.a") 86 | if args.rebuild or not os.path.exists(lib_fn): 87 | print("Rebuilding V8") 88 | lib_fn = Rebuild() 89 | else: 90 | print("Using prebuilt V8 %s" % lib_fn) 91 | WriteProgramConifgFile(lib_fn) 92 | 93 | def platform_name(): 94 | u = platform.uname() 95 | return (u[0] + "-" + u[4]).lower() 96 | 97 | def Rebuild(): 98 | env = os.environ.copy() 99 | 100 | EnsureDeps(v8_path) 101 | 102 | gn_path = os.path.join(depot_tools, "gn") 103 | assert os.path.exists(gn_path) 104 | ninja_path = os.path.join(depot_tools, "ninja") 105 | assert os.path.exists(ninja_path) 106 | 107 | gn_args = GN_ARGS.replace('\n', ' ') 108 | 109 | if args.use_ccache: 110 | ccache_fn = distutils.spawn.find_executable("ccache") 111 | if ccache_fn: 112 | gn_args += " cc_wrapper=\"%s\"" % ccache_fn 113 | 114 | print("Running gn") 115 | subprocess.check_call([gn_path, "gen", v8build_path, "--args=" + gn_args], 116 | cwd=v8_path, 117 | env=env) 118 | print("Running ninja") 119 | subprocess.check_call([ninja_path, "-v", "-C", v8build_path, "v8_monolith"], 120 | cwd=v8_path, 121 | env=env) 122 | lib_fn = os.path.join(v8build_path, "obj/libv8_monolith.a") 123 | return lib_fn 124 | 125 | def WriteProgramConifgFile(lib_fn): 126 | assert os.path.exists(lib_fn) 127 | if not os.path.isdir(out_path): 128 | os.makedirs(out_path) 129 | 130 | pc_fn = os.path.join(root_path, "v8.pc") 131 | include_dir = os.path.join(v8_path, "include") 132 | with open(pc_fn, 'w+') as f: 133 | f.write("Name: v8\n") 134 | f.write("Description: v8\n") 135 | f.write("Version: xxx\n") 136 | f.write("Cflags: -I%s\n" % include_dir) 137 | f.write("Libs: %s\n" % lib_fn) 138 | print("Wrote %s" % pc_fn) 139 | 140 | def EnsureDeps(v8_path): 141 | # Now call gclient sync. 142 | spec = "solutions = %s" % GCLIENT_SOLUTION 143 | print("Fetching dependencies.") 144 | env = os.environ.copy() 145 | # gclient needs to have depot_tools in the PATH. 146 | env["PATH"] = depot_tools + os.pathsep + env["PATH"] 147 | subprocess.check_call(["gclient", "sync", "--spec", spec], 148 | cwd=root_path, 149 | env=env) 150 | 151 | if __name__ == "__main__": 152 | main() 153 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | extern crate cc; 2 | extern crate pkg_config; 3 | 4 | use std::process::Command; 5 | 6 | fn main() { 7 | let pkg_config_file = "v8.pc".to_string(); 8 | 9 | let mut lib = pkg_config::probe_library(&pkg_config_file); 10 | if lib.is_err() { 11 | // V8 build step: 12 | let status = Command::new("python") 13 | .args(&["build.py", "--use_ccache"]) 14 | .status() 15 | .expect("failed to build V8"); 16 | 17 | if !status.success() { 18 | panic!("Couldn't build V8"); 19 | } 20 | } 21 | println!("Successful V8 build, probe v8.pc"); 22 | 23 | // V8 library lookup: 24 | lib = pkg_config::probe_library(&pkg_config_file); 25 | if lib.is_err() { 26 | panic!("Couldn't find v8.pc via pkg-config"); 27 | } 28 | println!("v8.pc found"); 29 | let v8 = lib.unwrap(); 30 | let incl_path = v8.include_paths[0].to_str().unwrap(); 31 | let lib_path = str::replace(incl_path, "v8/include", "out/v8build/obj"); 32 | 33 | println!("Binding build"); 34 | // Build settings: 35 | let mut build = cc::Build::new(); 36 | build.cpp(true) 37 | .warnings_into_errors(true) 38 | .warnings(false) 39 | .include(incl_path) 40 | .flag("-std=c++11") 41 | .file("src/binding.cc") 42 | .compile("binding"); 43 | 44 | // Linker settings: 45 | println!("cargo:rustc-link-search=native={}", lib_path); 46 | println!("cargo:rustc-link-lib=static=v8_monolith"); 47 | } 48 | -------------------------------------------------------------------------------- /src/binding.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Ryan Dahl . All rights reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to 6 | deal in the Software without restriction, including without limitation the 7 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | IN THE SOFTWARE. 21 | */ 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "binding.h" 29 | #include "libplatform/libplatform.h" 30 | #include "v8.h" 31 | 32 | using namespace v8; 33 | 34 | struct worker_s { 35 | void* rust_callback; 36 | void* rust_object; 37 | Isolate* isolate; 38 | std::string last_exception; 39 | Persistent recv; 40 | Persistent context; 41 | }; 42 | 43 | // Extracts a C string from a V8 Utf8Value. 44 | const char* ToCString(const String::Utf8Value& value) { 45 | return *value ? *value : ""; 46 | } 47 | 48 | /* 49 | bool AbortOnUncaughtExceptionCallback(Isolate* isolate) { 50 | return true; 51 | } 52 | 53 | void MessageCallback2(Local message, Local data) { 54 | printf("MessageCallback2\n\n"); 55 | } 56 | 57 | void FatalErrorCallback2(const char* location, const char* message) { 58 | printf("FatalErrorCallback2\n"); 59 | } 60 | */ 61 | 62 | void ExitOnPromiseRejectCallback(PromiseRejectMessage promise_reject_message) { 63 | auto isolate = Isolate::GetCurrent(); 64 | worker* w = (worker*)isolate->GetData(0); 65 | assert(w->isolate == isolate); 66 | HandleScope handle_scope(w->isolate); 67 | auto context = w->context.Get(w->isolate); 68 | 69 | auto exception = promise_reject_message.GetValue(); 70 | 71 | auto message = Exception::CreateMessage(isolate, exception); 72 | auto onerrorStr = String::NewFromUtf8(w->isolate, "onerror"); 73 | auto onerror = context->Global()->Get(onerrorStr); 74 | 75 | if (onerror->IsFunction()) { 76 | Local func = Local::Cast(onerror); 77 | Local args[5]; 78 | auto origin = message->GetScriptOrigin(); 79 | args[0] = exception->ToString(); 80 | args[1] = message->GetScriptResourceName(); 81 | args[2] = origin.ResourceLineOffset(); 82 | args[3] = origin.ResourceColumnOffset(); 83 | args[4] = exception; 84 | func->Call(context->Global(), 5, args); 85 | /* message, source, lineno, colno, error */ 86 | } else { 87 | printf("Unhandled Promise\n"); 88 | message->PrintCurrentStackTrace(isolate, stdout); 89 | } 90 | 91 | exit(1); 92 | } 93 | 94 | // Exception details will be appended to the first argument. 95 | std::string ExceptionString(worker* w, TryCatch* try_catch) { 96 | std::string out; 97 | size_t scratchSize = 20; 98 | char scratch[scratchSize]; // just some scratch space for sprintf 99 | 100 | HandleScope handle_scope(w->isolate); 101 | Local context = w->context.Get(w->isolate); 102 | String::Utf8Value exception(try_catch->Exception()); 103 | const char* exception_string = ToCString(exception); 104 | 105 | Handle message = try_catch->Message(); 106 | 107 | if (message.IsEmpty()) { 108 | // V8 didn't provide any extra information about this error; just 109 | // print the exception. 110 | out.append(exception_string); 111 | out.append("\n"); 112 | } else { 113 | // Print (filename):(line number) 114 | String::Utf8Value filename(message->GetScriptOrigin().ResourceName()); 115 | const char* filename_string = ToCString(filename); 116 | int linenum = message->GetLineNumber(); 117 | 118 | snprintf(scratch, scratchSize, "%i", linenum); 119 | out.append(filename_string); 120 | out.append(":"); 121 | out.append(scratch); 122 | out.append("\n"); 123 | 124 | // Print line of source code. 125 | String::Utf8Value sourceline(message->GetSourceLine()); 126 | const char* sourceline_string = ToCString(sourceline); 127 | 128 | out.append(sourceline_string); 129 | out.append("\n"); 130 | 131 | // Print wavy underline (GetUnderline is deprecated). 132 | int start = message->GetStartColumn(context).FromJust(); 133 | for (int i = 0; i < start; i++) { 134 | out.append(" "); 135 | } 136 | int end = message->GetEndColumn(context).FromJust(); 137 | for (int i = start; i < end; i++) { 138 | out.append("^"); 139 | } 140 | out.append("\n"); 141 | String::Utf8Value stack_trace(try_catch->StackTrace()); 142 | if (stack_trace.length() > 0) { 143 | const char* stack_trace_string = ToCString(stack_trace); 144 | out.append(stack_trace_string); 145 | out.append("\n"); 146 | } else { 147 | out.append(exception_string); 148 | out.append("\n"); 149 | } 150 | } 151 | return out; 152 | } 153 | 154 | extern "C" { 155 | // #include "_cgo_export.h" 156 | 157 | const char* worker_version() { return V8::GetVersion(); } 158 | 159 | void worker_set_flags(int* argc, char** argv) { 160 | V8::SetFlagsFromCommandLine(argc, argv, true); 161 | } 162 | 163 | const char* worker_last_exception(worker* w) { 164 | return w->last_exception.c_str(); 165 | } 166 | 167 | int worker_load(worker* w, char* name_s, char* source_s) { 168 | Locker locker(w->isolate); 169 | Isolate::Scope isolate_scope(w->isolate); 170 | HandleScope handle_scope(w->isolate); 171 | 172 | Local context = Local::New(w->isolate, w->context); 173 | Context::Scope context_scope(context); 174 | 175 | TryCatch try_catch(w->isolate); 176 | 177 | Local name = String::NewFromUtf8(w->isolate, name_s); 178 | Local source = String::NewFromUtf8(w->isolate, source_s); 179 | 180 | ScriptOrigin origin(name); 181 | 182 | Local