From 8b803e24270d35edd22d05f364e5296f8d9ab989 Mon Sep 17 00:00:00 2001 From: Aleksandr Nahapetyan Date: Tue, 10 Mar 2026 22:13:25 -0400 Subject: [PATCH 1/7] Added 146 support --- .../bc343986bd4cb17e49e/chrome-sandbox.diff | 192 ++ patches/bc343986bd4cb17e49e/trace-apis.diff | 2022 +++++++++++++++++ patches/bc343986bd4cb17e49e/version.txt | 2 + 3 files changed, 2216 insertions(+) create mode 100644 patches/bc343986bd4cb17e49e/chrome-sandbox.diff create mode 100644 patches/bc343986bd4cb17e49e/trace-apis.diff create mode 100644 patches/bc343986bd4cb17e49e/version.txt diff --git a/patches/bc343986bd4cb17e49e/chrome-sandbox.diff b/patches/bc343986bd4cb17e49e/chrome-sandbox.diff new file mode 100644 index 0000000..a5c6a1c --- /dev/null +++ b/patches/bc343986bd4cb17e49e/chrome-sandbox.diff @@ -0,0 +1,192 @@ +From 7b959779fb4a9fd6939d03b6fef45bed1c6745b3 Mon Sep 17 00:00:00 2001 +From: Sohom +Date: Sun, 15 Feb 2026 01:56:41 -0500 +Subject: [PATCH] Add support for Chrome 145 + +--- + .../base/process_launcher/BindService.java | 2 +- + chrome/android/java/AndroidManifest.xml | 4 +- + .../java/gin_java_method_invocation_helper.cc | 2 + + .../gin_java_function_invocation_helper.cc | 43 +++++++++++++++++++ + content/renderer/renderer_main.cc | 3 +- + .../modules/remote_objects/remote_object.cc | 1 + + .../renderer/platform/bindings/v8_binding.h | 1 + + 7 files changed, 51 insertions(+), 5 deletions(-) + +diff --git a/base/android/java/src/org/chromium/base/process_launcher/BindService.java b/base/android/java/src/org/chromium/base/process_launcher/BindService.java +index f0b05f9da4de9..1fb127bf4f3cc 100644 +--- a/base/android/java/src/org/chromium/base/process_launcher/BindService.java ++++ b/base/android/java/src/org/chromium/base/process_launcher/BindService.java +@@ -41,7 +41,7 @@ public final class BindService { + + static boolean supportVariableConnections() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q +- && !BuildConfig.IS_INCREMENTAL_INSTALL; ++ && !true; // VisibleV8 android change required disabling renderer isolation. + } + + // Note that handler is not guaranteed to be used, and client still need to correctly handle +diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml +index 030736d46a43d..279cf5a5eb3d8 100644 +--- a/chrome/android/java/AndroidManifest.xml ++++ b/chrome/android/java/AndroidManifest.xml +@@ -1292,13 +1292,13 @@ by a child template that "extends" this file. + +diff --git a/content/browser/android/java/gin_java_method_invocation_helper.cc b/content/browser/android/java/gin_java_method_invocation_helper.cc +index 2d0c3dd4eb754..5c331d1bd1eb9 100644 +--- a/content/browser/android/java/gin_java_method_invocation_helper.cc ++++ b/content/browser/android/java/gin_java_method_invocation_helper.cc +@@ -12,6 +12,8 @@ + #include "base/android/event_log.h" + #include "base/android/jni_android.h" + #include "base/android/jni_string.h" ++#include "base/logging.h" ++#include "base/debug/stack_trace.h" + #include "content/browser/android/java/gin_java_script_to_java_types_coercion.h" + #include "content/browser/android/java/java_method.h" + #include "content/common/android/gin_java_bridge_value.h" +diff --git a/content/renderer/java/gin_java_function_invocation_helper.cc b/content/renderer/java/gin_java_function_invocation_helper.cc +index 0d126236bc172..94da580147e57 100644 +--- a/content/renderer/java/gin_java_function_invocation_helper.cc ++++ b/content/renderer/java/gin_java_function_invocation_helper.cc +@@ -14,6 +14,7 @@ + #include "content/renderer/java/gin_java_bridge_object.h" + #include "content/renderer/java/gin_java_bridge_value_converter.h" + #include "v8/include/v8-exception.h" ++#include "v8/include/v8-visiblev8.h" + + namespace content { + +@@ -79,6 +80,13 @@ v8::Local GinJavaFunctionInvocationHelper::Invoke( + } + } + ++ std::vector> visv8_args; ++ ++ v8::Local val; ++ while (args->GetNext(&val)) { ++ visv8_args.push_back(val); ++ } ++ + mojom::GinJavaBridgeError error = + mojom::GinJavaBridgeError::kGinJavaBridgeNoError; + +@@ -95,12 +103,24 @@ v8::Local GinJavaFunctionInvocationHelper::Invoke( + } + } + if (!result.get()) { ++ auto* functionCallbackInfo = args->GetFunctionCallbackInfo(); ++ v8::visv8_log_java_api_call( ++ args->isolate(), false, functionCallbackInfo->Data().As(), ++ functionCallbackInfo->This().As(), ++ v8::Undefined(args->isolate()), visv8_args, visv8_args.size()); + args->isolate()->ThrowException(v8::Exception::Error(gin::StringToV8( + args->isolate(), base::StrCat({"Error invoking ", method_name_, ": ", + GinJavaBridgeErrorToString(error)})))); + return v8::Undefined(args->isolate()); + } + if (!result->is_blob()) { ++ auto* functionCallbackInfo = args->GetFunctionCallbackInfo(); ++ v8::visv8_log_java_api_call( ++ args->isolate(), false, functionCallbackInfo->Data().As(), ++ functionCallbackInfo->This().As(), ++ converter_->ToV8Value(result.get(), ++ args->isolate()->GetCurrentContext()), ++ visv8_args, visv8_args.size()); + return converter_->ToV8Value(result.get(), + args->isolate()->GetCurrentContext()); + } +@@ -116,15 +136,38 @@ v8::Local GinJavaFunctionInvocationHelper::Invoke( + if (object_result) { + v8::Local controller; + if (!object_result->GetWrapper(args->isolate()).ToLocal(&controller)) { ++ auto* functionCallbackInfo = args->GetFunctionCallbackInfo(); ++ v8::visv8_log_java_api_call( ++ args->isolate(), false, ++ functionCallbackInfo->Data().As(), ++ functionCallbackInfo->This().As(), ++ v8::Undefined(args->isolate()), visv8_args, visv8_args.size()); + return v8::Undefined(args->isolate()); + } ++ auto* functionCallbackInfo = args->GetFunctionCallbackInfo(); ++ v8::visv8_log_java_api_call(args->isolate(), false, ++ functionCallbackInfo->Data().As(), ++ functionCallbackInfo->This().As(), ++ controller, visv8_args, visv8_args.size()); + return controller; + } + } else if (gin_value->IsType(GinJavaBridgeValue::TYPE_NONFINITE)) { + float float_value; + gin_value->GetAsNonFinite(&float_value); ++ auto* functionCallbackInfo = args->GetFunctionCallbackInfo(); ++ v8::visv8_log_java_api_call(args->isolate(), false, ++ functionCallbackInfo->Data().As(), ++ functionCallbackInfo->This().As(), ++ v8::Number::New(args->isolate(), float_value), ++ visv8_args, visv8_args.size()); + return v8::Number::New(args->isolate(), float_value); + } ++ ++ auto* functionCallbackInfo = args->GetFunctionCallbackInfo(); ++ v8::visv8_log_java_api_call( ++ args->isolate(), false, functionCallbackInfo->Data().As(), ++ functionCallbackInfo->This().As(), ++ v8::Undefined(args->isolate()), visv8_args, visv8_args.size()); + return v8::Undefined(args->isolate()); + } + +diff --git a/content/renderer/renderer_main.cc b/content/renderer/renderer_main.cc +index cc54c30df4042..2c2cfcc91ee24 100644 +--- a/content/renderer/renderer_main.cc ++++ b/content/renderer/renderer_main.cc +@@ -232,8 +232,7 @@ int RendererMain(MainFunctionParams parameters) { + { + content::ContentRendererClient* client = GetContentClient()->renderer(); + bool should_run_loop = true; +- bool need_sandbox = +- !command_line.HasSwitch(sandbox::policy::switches::kNoSandbox); ++ bool need_sandbox = false; // VisibleV8 disable sandbox for desktop + + if (!need_sandbox) { + // The post-sandbox actions still need to happen at some point. +diff --git a/third_party/blink/renderer/modules/remote_objects/remote_object.cc b/third_party/blink/renderer/modules/remote_objects/remote_object.cc +index c9c72eee69378..98ffd3c5de790 100644 +--- a/third_party/blink/renderer/modules/remote_objects/remote_object.cc ++++ b/third_party/blink/renderer/modules/remote_objects/remote_object.cc +@@ -6,6 +6,7 @@ + + #include + ++#include "base/logging.h" + #include "base/numerics/safe_conversions.h" + #include "gin/converter.h" + #include "third_party/blink/public/web/blink.h" +diff --git a/third_party/blink/renderer/platform/bindings/v8_binding.h b/third_party/blink/renderer/platform/bindings/v8_binding.h +index 6a0a0762ff359..df13bc22358e2 100644 +--- a/third_party/blink/renderer/platform/bindings/v8_binding.h ++++ b/third_party/blink/renderer/platform/bindings/v8_binding.h +@@ -49,6 +49,7 @@ + #include "v8/include/v8-maybe.h" + #include "v8/include/v8-persistent-handle.h" + #include "v8/include/v8-primitive.h" ++#include "v8/include/v8-visiblev8.h" + #include "v8/include/v8-value.h" + + namespace blink { +-- +2.43.0 + diff --git a/patches/bc343986bd4cb17e49e/trace-apis.diff b/patches/bc343986bd4cb17e49e/trace-apis.diff new file mode 100644 index 0000000..d2c639e --- /dev/null +++ b/patches/bc343986bd4cb17e49e/trace-apis.diff @@ -0,0 +1,2022 @@ +From 9cdcfbd0183f26de4c710452bfa88db63bb1e1d9 Mon Sep 17 00:00:00 2001 +From: Sohom +Date: Sat, 14 Feb 2026 20:54:54 -0500 +Subject: [PATCH] Add support for Chrome 145 + +--- + BUILD.bazel | 1 + + BUILD.gn | 13 +- + include/v8-visiblev8.h | 31 ++ + src/api/api.cc | 68 +++ + src/builtins/builtins-api.cc | 11 + + src/builtins/builtins-call-gen.cc | 16 + + src/builtins/builtins-function.cc | 12 + + src/builtins/builtins-global.cc | 10 + + src/builtins/builtins-reflect.cc | 7 + + src/builtins/reflect.tq | 9 + + src/compiler/js-call-reducer.cc | 217 +------- + src/ic/accessor-assembler.cc | 2 + + src/init/v8.cc | 2 + + src/interpreter/bytecode-generator.cc | 123 +++++ + src/objects/objects-inl.h | 8 + + src/objects/objects.cc | 83 +++ + src/objects/objects.h | 10 + + src/runtime/runtime-compiler.cc | 11 + + src/runtime/runtime-test.cc | 102 ++++ + src/runtime/runtime-utils.cc | 704 ++++++++++++++++++++++++++ + src/runtime/runtime-utils.h | 98 ++++ + src/runtime/runtime.cc | 25 + + src/runtime/runtime.h | 22 + + 23 files changed, 1368 insertions(+), 217 deletions(-) + create mode 100644 include/v8-visiblev8.h + create mode 100644 src/runtime/runtime-utils.cc + +diff --git a/BUILD.bazel b/BUILD.bazel +index ab41feeb1b8..6a51c19208a 100644 +--- a/BUILD.bazel ++++ b/BUILD.bazel +@@ -2476,6 +2476,7 @@ filegroup( + "src/runtime/runtime-test.cc", + "src/runtime/runtime-trace.cc", + "src/runtime/runtime-typedarray.cc", ++ "src/runtime/runtime-utils.cc", + "src/runtime/runtime-utils.h", + "src/runtime/runtime-weak-refs.cc", + "src/sandbox/bounded-size.h", +diff --git a/BUILD.gn b/BUILD.gn +index 6801927b0d0..d55c17bc261 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -291,8 +291,8 @@ declare_args() { + # specific hook). + v8_check_header_includes = false + +- # Enable lazy source positions by default. +- v8_enable_lazy_source_positions = true ++ # Enable lazy source positions by default. [disabling for VisibleV8] ++ v8_enable_lazy_source_positions = false + + # Disable write barriers when GCs are non-incremental and + # heap has single generation. +@@ -428,6 +428,9 @@ declare_args() { + # optimizations. + v8_jitless = v8_enable_lite_mode + ++ # VisibleV8: sets VV8_TRACE_PROPERTIES ++ vv8_trace_properties = true ++ + # Enable Sparkplug + # Sets -DV8_ENABLE_SPARKPLUG. + v8_enable_sparkplug = "" +@@ -1174,6 +1177,11 @@ config("cppgc_header_features") { + } else { + defines = enabled_external_cppgc_defines + } ++ ++ # VisibleV8: create define ++ if (vv8_trace_properties) { ++ defines += [ "VV8_TRACE_PROPERTIES" ] ++ } + } + + enabled_external_defines = +@@ -6016,6 +6024,7 @@ v8_source_set("v8_base_without_compiler") { + "src/runtime/runtime-test.cc", + "src/runtime/runtime-trace.cc", + "src/runtime/runtime-typedarray.cc", ++ "src/runtime/runtime-utils.cc", + "src/runtime/runtime-weak-refs.cc", + "src/runtime/runtime.cc", + "src/sandbox/bytecode-verifier.cc", +diff --git a/include/v8-visiblev8.h b/include/v8-visiblev8.h +new file mode 100644 +index 00000000000..fcc06c8fc91 +--- /dev/null ++++ b/include/v8-visiblev8.h +@@ -0,0 +1,31 @@ ++#ifndef INCLUDE_V8_VISIBLEV8_H_ ++#define INCLUDE_V8_VISIBLEV8_H_ ++ ++#include ++ ++#include "v8-isolate.h" ++#include "v8-local-handle.h" ++#include "v8-value.h" ++ ++ ++namespace v8 { ++ void visv8_log_java_api_call(Isolate *isolate, bool is_constructor, ++ v8::Local local_func, ++ v8::Local local_receiver, ++ v8::Local local_result, ++ const std::vector>& argv, int argc); ++ ++ void visv8_log_java_prop_set(Isolate *isolate, int call_site, ++ v8::Local local_obj, ++ v8::Local local_prop, ++ v8::Local local_value); ++ ++ void visv8_log_java_prop_get(Isolate *isolate, int call_site, ++ v8::Local local_obj, ++ v8::Local local_prop, ++ v8::Local local_value); ++ ++} ++ ++ ++#endif // INCLUDE_V8_VISIBLEV8_H_ +\ No newline at end of file +diff --git a/src/api/api.cc b/src/api/api.cc +index 0b3ea819fb2..4dec747f197 100644 +--- a/src/api/api.cc ++++ b/src/api/api.cc +@@ -12531,6 +12531,74 @@ std::string SourceLocation::ToString() const { + .str(); + } + ++v8::internal::Isolate* ConvertToInternalIsolate(v8::Isolate* public_isolate) { ++ return reinterpret_cast(public_isolate); ++} ++ ++std::vector>* ConvertArrayToTagged( ++ const std::vector>& argv, int argc) { ++ auto* tagged_values = ++ new std::vector>(); ++ tagged_values->reserve(argc); ++ ++ for (const auto& local_value : argv) { ++ internal::DirectHandle val = ++ Utils::OpenDirectHandle(*local_value); ++ tagged_values->push_back(*val); ++ } ++ ++ return tagged_values; ++} ++ ++void visv8_log_java_api_call(Isolate* isolate, bool is_constructor, ++ v8::Local local_func, ++ v8::Local local_receiver, ++ v8::Local local_result, ++ const std::vector>& argv, ++ int argc) { ++ v8::internal::Isolate* internal_isolate = ConvertToInternalIsolate(isolate); ++ std::vector>* args = ++ ConvertArrayToTagged(argv, argc); ++ internal::DirectHandle func = ++ Utils::OpenDirectHandle(*local_func); ++ internal::DirectHandle receiver = ++ Utils::OpenDirectHandle(*local_receiver); ++ internal::DirectHandle result = ++ Utils::OpenDirectHandle(*local_result); ++ internal::ext_visv8_log_java_api_call(internal_isolate, is_constructor, *func, ++ *receiver, *result, args, argc); ++} ++ ++void visv8_log_java_prop_set(Isolate* isolate, int call_site, ++ v8::Local local_obj, ++ v8::Local local_prop, ++ v8::Local local_value) { ++ v8::internal::Isolate* internal_isolate = ConvertToInternalIsolate(isolate); ++ internal::DirectHandle obj = ++ Utils::OpenDirectHandle(*local_obj); ++ internal::DirectHandle prop = ++ Utils::OpenDirectHandle(*local_prop); ++ internal::DirectHandle value = ++ Utils::OpenDirectHandle(*local_value); ++ internal::ext_visv8_log_java_prop_set(internal_isolate, call_site, *obj, ++ *prop, *value); ++} ++ ++void visv8_log_java_prop_get(Isolate* isolate, int call_site, ++ v8::Local local_obj, ++ v8::Local local_prop, ++ v8::Local local_value) { ++ v8::internal::Isolate* internal_isolate = ConvertToInternalIsolate(isolate); ++ internal::DirectHandle obj = ++ Utils::OpenDirectHandle(*local_obj); ++ internal::DirectHandle prop = ++ Utils::OpenDirectHandle(*local_prop); ++ internal::DirectHandle value = ++ Utils::OpenDirectHandle(*local_value); ++ internal::ext_visv8_log_java_prop_get(internal_isolate, call_site, *obj, ++ *prop, *value); ++} ++ + } // namespace v8 + + #ifdef ENABLE_SLOW_DCHECKS +diff --git a/src/builtins/builtins-api.cc b/src/builtins/builtins-api.cc +index 4a32a716207..8c189d34eb6 100644 +--- a/src/builtins/builtins-api.cc ++++ b/src/builtins/builtins-api.cc +@@ -18,6 +18,10 @@ + namespace v8 { + namespace internal { + ++// VisibleV8 ++extern void visv8_log_api_call(Isolate*, bool, Tagged, ++ Tagged, Address*, int); ++ + namespace { + + // Returns true if the function can legally be called with this receiver, +@@ -138,6 +142,13 @@ BUILTIN(HandleApiConstruct) { + DirectHandle fun_data( + args.target()->shared()->api_func_data(), isolate); + ++ // VisibleV8 ++ Handle function = args.target(); ++ v8::internal::visv8_log_api_call( ++ isolate, true, *function, *receiver, ++ args.address_of_first_argument(), ++ args.argc_without_receiver()); ++ + // TODO(ishell, http://crbug.com/326505377): avoid double-copying of the + // arguments on this path by porting this builtin to assembly and letting + // it create the required frame structure. +diff --git a/src/builtins/builtins-call-gen.cc b/src/builtins/builtins-call-gen.cc +index 17545d69dc7..44ec8d9fadf 100644 +--- a/src/builtins/builtins-call-gen.cc ++++ b/src/builtins/builtins-call-gen.cc +@@ -885,6 +885,19 @@ TF_BUILTIN(HandleApiCallOrConstruct, CallOrConstructBuiltinsAssembler) { + #else + auto dispatch_handle = InvalidDispatchHandleConstant(); + #endif ++ CodeStubArguments args(this, argc); ++ auto args_ptr = args.AtIndexPtr(IntPtrConstant(0)); ++ ++ // This splits the pointer in 16-bit Smi chunks, and passes the resulting ++ // array to the runtime no, I am not mad, take a look at how ++ // CodeStubAssembler::Print(... TNode) is implemented at ++ // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/codegen/code-stub-assembler.cc;l=16637;drc=f4a00cc248dd2dc8ec8759fb51620d47b5114090;bpv=1;bpt=1 ++ TNode chunks[4]; ++ for (int i = 0; i < 4; ++i) { ++ chunks[i] = SmiFromUint32(ReinterpretCast(Word32And( ++ TruncateIntPtrToInt32(ReinterpretCast(args_ptr)), 0xFFFF))); ++ args_ptr = ReinterpretCast(WordShr(args_ptr, IntPtrConstant(16))); ++ } + + Label if_call(this), if_construct(this); + Branch(IsUndefined(new_target), &if_call, &if_construct); +@@ -896,6 +909,9 @@ TF_BUILTIN(HandleApiCallOrConstruct, CallOrConstructBuiltinsAssembler) { + TNode function_template_info = + CAST(LoadSharedFunctionInfoUntrustedFunctionData(shared)); + ++ CallRuntime(Runtime::kVV8TraceFunctionCall, context, target, ++ SmiFromInt32(argc), chunks[3], chunks[2], chunks[1], chunks[0]); ++ + // The topmost script-having context is not guaranteed to be equal to + // current context at this point. For example, if target function was + // called via Function.prototype.call or other similar builtins, or if it +diff --git a/src/builtins/builtins-function.cc b/src/builtins/builtins-function.cc +index 7d61749e4af..d016a455c17 100644 +--- a/src/builtins/builtins-function.cc ++++ b/src/builtins/builtins-function.cc +@@ -17,6 +17,11 @@ + namespace v8 { + namespace internal { + ++// VisibleV8 ++extern void visv8_log_api_call(Isolate*, bool, Tagged, ++ Tagged, Address*, int); ++// VisibleV8 ++ + namespace { + + // ES6 section 19.2.1.1.1 CreateDynamicFunction +@@ -27,6 +32,13 @@ MaybeDirectHandle CreateDynamicFunction(Isolate* isolate, + DCHECK_LE(1, args.length()); + int const argc = args.length() - 1; + ++ // VisibleV8 ++ // passing undefined into the reciever since no reciever exists ++ visv8_log_api_call(isolate, false, *args.target(), ++ ReadOnlyRoots(isolate).undefined_value(), ++ args.address_of_first_argument(), argc); ++ // VisibleV8 ++ + DirectHandle target = args.target(); + DirectHandle target_global_proxy(target->global_proxy(), isolate); + +diff --git a/src/builtins/builtins-global.cc b/src/builtins/builtins-global.cc +index 89be05c0b3e..eeeb53f447a 100644 +--- a/src/builtins/builtins-global.cc ++++ b/src/builtins/builtins-global.cc +@@ -13,6 +13,11 @@ + namespace v8 { + namespace internal { + ++// VisibleV8 ++extern void visv8_log_api_call(Isolate*, bool, Tagged, ++ Tagged, Address*, int); ++// VisibleV8 ++ + // ES6 section 18.2.6.2 decodeURI (encodedURI) + BUILTIN(GlobalDecodeURI) { + HandleScope scope(isolate); +@@ -86,6 +91,11 @@ BUILTIN(GlobalEval) { + Handle x = args.atOrUndefined(isolate, 1); + DirectHandle target = args.target(); + DirectHandle target_global_proxy(target->global_proxy(), isolate); ++ // VisibleV8 ++ v8::internal::visv8_log_api_call(isolate, false, *target, *args.receiver(), ++ args.address_of_first_argument(), ++ args.length() - 1); ++ // VisibleV8 + if (!Builtins::AllowDynamicFunction(isolate, target, target_global_proxy)) { + isolate->CountUsage(v8::Isolate::kFunctionConstructorReturnedUndefined); + return ReadOnlyRoots(isolate).undefined_value(); +diff --git a/src/builtins/builtins-reflect.cc b/src/builtins/builtins-reflect.cc +index 4281d4e3957..bf227014c89 100644 +--- a/src/builtins/builtins-reflect.cc ++++ b/src/builtins/builtins-reflect.cc +@@ -90,6 +90,13 @@ BUILTIN(ReflectSet) { + ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name, + Object::ToName(isolate, key)); + ++#ifdef VV8_TRACE_PROPERTIES ++ // VisibleV8: log reflected property sets ++ extern void visv8_log_property_set(Isolate*, int, Tagged, ++ Tagged, Tagged); ++ visv8_log_property_set(isolate, -1, *target, *key, *value); ++#endif ++ + PropertyKey lookup_key(isolate, name); + LookupIterator it(isolate, receiver, lookup_key, target_recv); + Maybe result = Object::SetSuperProperty( +diff --git a/src/builtins/reflect.tq b/src/builtins/reflect.tq +index efd7568f4bf..e8134aaf8fb 100644 +--- a/src/builtins/reflect.tq ++++ b/src/builtins/reflect.tq +@@ -60,6 +60,10 @@ extern macro SmiConstant(constexpr OnNonExistent): Smi; + extern transitioning builtin GetPropertyWithReceiver( + implicit context: Context)(JSAny, Name, JSAny, Smi): JSAny; + ++// VisibleV8: defining external trace-property-load runtime function ++extern transitioning runtime TracePropertyLoad(implicit context: Context)(Smi, JSAny, JSAny): void; ++ ++ + // ES6 section 26.1.6 Reflect.get + transitioning javascript builtin ReflectGet( + js-implicit context: NativeContext)(...arguments): JSAny { +@@ -70,6 +74,11 @@ transitioning javascript builtin ReflectGet( + const name: AnyName = ToName(propertyKey); + const receiver: JSAny = + arguments.length > 2 ? arguments[2] : objectJSReceiver; ++ ++ ++ // VisibleV8: call-out to property-load tracer runtime function ++ TracePropertyLoad(-1, object, propertyKey); ++ + return GetPropertyWithReceiver( + objectJSReceiver, name, receiver, SmiConstant(kReturnUndefined)); + } +diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc +index ba774cd165f..1e2f3e1398b 100644 +--- a/src/compiler/js-call-reducer.cc ++++ b/src/compiler/js-call-reducer.cc +@@ -4033,221 +4033,8 @@ Reduction JSCallReducer::ReduceCallWasmFunction(Node* node, + + Reduction JSCallReducer::ReduceCallApiFunction(Node* node, + SharedFunctionInfoRef shared) { +- JSCallNode n(node); +- CallParameters const& p = n.Parameters(); +- int const argc = p.arity_without_implicit_args(); +- Node* target = n.target(); +- Node* global_proxy = jsgraph()->ConstantNoHole( +- native_context().global_proxy_object(broker()), broker()); +- Node* receiver = (p.convert_mode() == ConvertReceiverMode::kNullOrUndefined) +- ? global_proxy +- : n.receiver(); +- Node* context = n.context(); +- Effect effect = n.effect(); +- Control control = n.control(); +- FrameState frame_state = n.frame_state(); +- +- if (!shared.function_template_info(broker()).has_value()) { +- TRACE_BROKER_MISSING( +- broker(), "FunctionTemplateInfo for function with SFI " << shared); +- return NoChange(); +- } +- +- // See if we can optimize this API call to {shared}. +- FunctionTemplateInfoRef function_template_info( +- shared.function_template_info(broker()).value()); +- +- if (function_template_info.accept_any_receiver() && +- function_template_info.is_signature_undefined(broker())) { +- // We might be able to +- // optimize the API call depending on the {function_template_info}. +- // If the API function accepts any kind of {receiver}, we only need to +- // ensure that the {receiver} is actually a JSReceiver at this point, +- // and also pass that as the {holder}. There are two independent bits +- // here: +- // +- // a. When the "accept any receiver" bit is set, it means we don't +- // need to perform access checks, even if the {receiver}'s map +- // has the "needs access check" bit set. +- // b. When the {function_template_info} has no signature, we don't +- // need to do the compatible receiver check, since all receivers +- // are considered compatible at that point, and the {receiver} +- // will be pass as the {holder}. +- // +- receiver = effect = graph()->NewNode( +- simplified()->ConvertReceiver(p.convert_mode()), receiver, +- jsgraph()->ConstantNoHole(native_context(), broker()), global_proxy, +- effect, control); +- } else { +- // Try to infer the {receiver} maps from the graph. +- MapInference inference(broker(), receiver, effect); +- if (inference.HaveMaps()) { +- ZoneRefSet const& receiver_maps = inference.GetMaps(); +- MapRef first_receiver_map = receiver_maps[0]; +- +- // See if we can constant-fold the compatible receiver checks. +- HolderLookupResult api_holder = +- function_template_info.LookupHolderOfExpectedType(broker(), +- first_receiver_map); +- if (api_holder.lookup == CallOptimization::kHolderNotFound) { +- return inference.NoChange(); +- } +- +- // Check that all {receiver_maps} are actually JSReceiver maps and +- // that the {function_template_info} accepts them without access +- // checks (even if "access check needed" is set for {receiver}). +- // +- // Note that we don't need to know the concrete {receiver} maps here, +- // meaning it's fine if the {receiver_maps} are unreliable, and we also +- // don't need to install any stability dependencies, since the only +- // relevant information regarding the {receiver} is the Map::constructor +- // field on the root map (which is different from the JavaScript exposed +- // "constructor" property) and that field cannot change. +- // +- // So if we know that {receiver} had a certain constructor at some point +- // in the past (i.e. it had a certain map), then this constructor is going +- // to be the same later, since this information cannot change with map +- // transitions. +- // +- // The same is true for the instance type, e.g. we still know that the +- // instance type is JSObject even if that information is unreliable, and +- // the "access check needed" bit, which also cannot change later. +- CHECK(first_receiver_map.IsJSReceiverMap()); +- CHECK(!first_receiver_map.is_access_check_needed() || +- function_template_info.accept_any_receiver()); +- +- for (size_t i = 1; i < receiver_maps.size(); ++i) { +- MapRef receiver_map = receiver_maps[i]; +- HolderLookupResult holder_i = +- function_template_info.LookupHolderOfExpectedType(broker(), +- receiver_map); +- +- if (api_holder.lookup != holder_i.lookup) return inference.NoChange(); +- DCHECK(holder_i.lookup == CallOptimization::kHolderFound || +- holder_i.lookup == CallOptimization::kHolderIsReceiver); +- if (holder_i.lookup == CallOptimization::kHolderFound) { +- DCHECK(api_holder.holder.has_value() && holder_i.holder.has_value()); +- if (!api_holder.holder->equals(*holder_i.holder)) { +- return inference.NoChange(); +- } +- } +- +- CHECK(receiver_map.IsJSReceiverMap()); +- CHECK(!receiver_map.is_access_check_needed() || +- function_template_info.accept_any_receiver()); +- } +- +- if (p.speculation_mode() != SpeculationMode::kAllowSpeculation && +- !inference.RelyOnMapsViaStability(dependencies())) { +- // We were not able to make the receiver maps reliable without map +- // checks but doing map checks would lead to deopt loops, so give up. +- return inference.NoChange(); +- } +- +- // TODO(neis): The maps were used in a way that does not actually require +- // map checks or stability dependencies. +- inference.RelyOnMapsPreferStability(dependencies(), jsgraph(), &effect, +- control, p.feedback()); +- } else { +- // We don't have enough information to eliminate the access check +- // and/or the compatible receiver check, so use the generic builtin +- // that does those checks dynamically. This is still significantly +- // faster than the generic call sequence. +- Builtin builtin_name; +- if (function_template_info.accept_any_receiver()) { +- DCHECK(!function_template_info.is_signature_undefined(broker())); +- builtin_name = Builtin::kCallFunctionTemplate_CheckCompatibleReceiver; +- } else if (function_template_info.is_signature_undefined(broker())) { +- builtin_name = Builtin::kCallFunctionTemplate_CheckAccess; +- } else { +- builtin_name = +- Builtin::kCallFunctionTemplate_CheckAccessAndCompatibleReceiver; +- } +- +- // The CallFunctionTemplate builtin requires the {receiver} to be +- // an actual JSReceiver, so make sure we do the proper conversion +- // first if necessary. +- receiver = effect = graph()->NewNode( +- simplified()->ConvertReceiver(p.convert_mode()), receiver, +- jsgraph()->ConstantNoHole(native_context(), broker()), global_proxy, +- effect, control); +- +- Callable callable = Builtins::CallableFor(isolate(), builtin_name); +- auto call_descriptor = Linkage::GetStubCallDescriptor( +- graph()->zone(), callable.descriptor(), +- argc + 1 /* implicit receiver */, CallDescriptor::kNeedsFrameState); +- node->RemoveInput(n.FeedbackVectorIndex()); +- node->InsertInput(graph()->zone(), 0, +- jsgraph()->HeapConstantNoHole(callable.code())); +- node->ReplaceInput( +- 1, jsgraph()->ConstantNoHole(function_template_info, broker())); +- node->InsertInput(graph()->zone(), 2, +- jsgraph()->Int32Constant(JSParameterCount(argc))); +- node->ReplaceInput(3, receiver); // Update receiver input. +- node->ReplaceInput(6 + argc, effect); // Update effect input. +- NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); +- return Changed(node); +- } +- } +- +- // TODO(turbofan): Consider introducing a JSCallApiCallback operator for +- // this and lower it during JSGenericLowering, and unify this with the +- // JSNativeContextSpecialization::InlineApiCall method a bit. +- compiler::OptionalObjectRef maybe_callback_data = +- function_template_info.callback_data(broker()); +- // Check if the function has an associated C++ code to execute. +- if (!maybe_callback_data.has_value()) { +- // TODO(ishell): consider generating "return undefined" for empty function +- // instead of failing. +- TRACE_BROKER_MISSING(broker(), "call code for function template info " +- << function_template_info); +- return NoChange(); +- } +- +- // Handles overloaded functions. +- FastApiCallFunction c_function = fast_api_call::GetFastApiCallTarget( +- broker(), function_template_info, argc); +- +- if (c_function.address) { +- FastApiCallReducerAssembler a(this, node, function_template_info, +- c_function, receiver, shared, target, argc, +- effect); +- Node* fast_call_subgraph = a.ReduceFastApiCall(); +- +- return Replace(fast_call_subgraph); +- } +- +- // Slow call +- +- bool no_profiling = broker()->dependencies()->DependOnNoProfilingProtector(); +- Callable call_api_callback = Builtins::CallableFor( +- isolate(), no_profiling ? Builtin::kCallApiCallbackOptimizedNoProfiling +- : Builtin::kCallApiCallbackOptimized); +- CallInterfaceDescriptor cid = call_api_callback.descriptor(); +- auto call_descriptor = +- Linkage::GetStubCallDescriptor(graph()->zone(), cid, argc + 1 /* +- implicit receiver */, CallDescriptor::kNeedsFrameState); +- ApiFunction api_function(function_template_info.callback(broker())); +- ExternalReference function_reference = ExternalReference::Create( +- &api_function, ExternalReference::DIRECT_API_CALL); +- +- Node* continuation_frame_state = CreateInlinedApiFunctionFrameState( +- jsgraph(), shared, target, context, receiver, frame_state); +- +- node->RemoveInput(n.FeedbackVectorIndex()); +- node->InsertInput(graph()->zone(), 0, +- jsgraph()->HeapConstantNoHole(call_api_callback.code())); +- node->ReplaceInput(1, jsgraph()->ExternalConstant(function_reference)); +- node->InsertInput(graph()->zone(), 2, jsgraph()->ConstantNoHole(argc)); +- node->InsertInput( +- graph()->zone(), 3, +- jsgraph()->HeapConstantNoHole(function_template_info.object())); +- node->ReplaceInput(4, receiver); // Update receiver input. +- // 6 + argc is context input. +- node->ReplaceInput(5 + argc + 1, continuation_frame_state); +- node->ReplaceInput(5 + argc + 2, effect); +- NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); +- return Changed(node); ++ // VisibleV8 ++ return NoChange(); + } + + namespace { +diff --git a/src/ic/accessor-assembler.cc b/src/ic/accessor-assembler.cc +index 9d2889454b0..f41494681fa 100644 +--- a/src/ic/accessor-assembler.cc ++++ b/src/ic/accessor-assembler.cc +@@ -5617,6 +5617,8 @@ auto slot = Parameter(Descriptor::kSlot); + auto vector = Parameter(Descriptor::kVector); + auto context = Parameter(Descriptor::kContext); + ++ CallRuntime(Runtime::kVV8TraceKeyedHasIC, context, receiver, name); ++ +LoadICParameters p = + MakeLoadICParameters(context, receiver, name, slot, vector); + KeyedLoadIC(&p, LoadAccessMode::kHas); +diff --git a/src/init/v8.cc b/src/init/v8.cc +index 4e452b0a8ec..c20819061c2 100644 +--- a/src/init/v8.cc ++++ b/src/init/v8.cc +@@ -256,6 +256,8 @@ void V8::Initialize() { + ExternalReferenceTable::InitializeOncePerIsolateGroup( + IsolateGroup::current()->external_ref_table()); + AdvanceStartupState(V8StartupState::kV8Initialized); ++ extern void visv8_tls_init(); ++ visv8_tls_init(); + } + + void V8::Dispose() { +diff --git a/src/interpreter/bytecode-generator.cc b/src/interpreter/bytecode-generator.cc +index 4cb384f12e8..bd9fbab0fde 100644 +--- a/src/interpreter/bytecode-generator.cc ++++ b/src/interpreter/bytecode-generator.cc +@@ -5870,6 +5870,36 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) { + + VisitForAccumulatorValue(expr->value()); + ++#ifdef VV8_TRACE_PROPERTIES ++ // VisibleV8 (trace assignments to named/keyed properties only) ++ if ((lhs_data.assign_type() == NAMED_PROPERTY) || ++ (lhs_data.assign_type() == KEYED_PROPERTY)) { ++ // Save accumulator for later restoration ++ Register saved_acc = register_allocator()->NewRegister(); ++ builder()->StoreAccumulatorInRegister(saved_acc); ++ ++ // Trace object/property/new-value for this assignment ++ RegisterList trace_args = register_allocator()->NewRegisterList(4); ++ builder() ++ ->LoadLiteral(Smi::FromInt(expr->position())) ++ .StoreAccumulatorInRegister(trace_args[0]) ++ .MoveRegister(lhs_data.object(), trace_args[1]) ++ .MoveRegister(saved_acc, trace_args[3]); ++ if (lhs_data.assign_type() == NAMED_PROPERTY) { ++ builder() ++ ->LoadLiteral(lhs_data.name()) ++ .StoreAccumulatorInRegister(trace_args[2]); ++ } else { ++ builder()->MoveRegister(lhs_data.key(), trace_args[2]); ++ } ++ builder()->CallRuntime(Runtime::kTracePropertyStore, ++ trace_args); // args: (call-site, this, key, value) ++ ++ // Restore accumulator ++ builder()->LoadAccumulatorWithRegister(saved_acc); ++ } ++#endif ++ + builder()->SetExpressionPosition(expr); + BuildAssignment(lhs_data, expr->op(), expr->lookup_hoisting_mode()); + } +@@ -5965,6 +5995,36 @@ void BytecodeGenerator::VisitCompoundAssignment(CompoundAssignment* expr) { + VisitForAccumulatorValue(expr->value()); + builder()->BinaryOperation(binop->op(), old_value, feedback_index(slot)); + } ++#ifdef VV8_TRACE_PROPERTIES ++ // VisibleV8 (trace assignments to named/keyed properties only) ++ if ((lhs_data.assign_type() == NAMED_PROPERTY) || ++ (lhs_data.assign_type() == KEYED_PROPERTY)) { ++ // Save accumulator for later restoration ++ Register saved_acc = register_allocator()->NewRegister(); ++ builder()->StoreAccumulatorInRegister(saved_acc); ++ ++ // Trace object/property/new-value for this assignment ++ RegisterList trace_args = register_allocator()->NewRegisterList(4); ++ builder() ++ ->LoadLiteral(Smi::FromInt(expr->position())) ++ .StoreAccumulatorInRegister(trace_args[0]) ++ .MoveRegister(lhs_data.object(), trace_args[1]) ++ .MoveRegister(saved_acc, trace_args[3]); ++ if (lhs_data.assign_type() == NAMED_PROPERTY) { ++ builder() ++ ->LoadLiteral(lhs_data.name()) ++ .StoreAccumulatorInRegister(trace_args[2]); ++ } else { ++ builder()->MoveRegister(lhs_data.key(), trace_args[2]); ++ } ++ builder()->CallRuntime(Runtime::kTracePropertyStore, ++ trace_args); // args: (call-site, this, key, value) ++ ++ // Restore accumulator ++ builder()->LoadAccumulatorWithRegister(saved_acc); ++ } ++#endif ++ + builder()->SetExpressionPosition(expr); + + BuildAssignment(lhs_data, expr->op(), expr->lookup_hoisting_mode()); +@@ -6417,6 +6477,21 @@ void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* property) { + case NON_PROPERTY: + UNREACHABLE(); + case NAMED_PROPERTY: { ++#ifdef VV8_TRACE_PROPERTIES ++ // VisibleV8: generate code to trace named property loads ++ { ++ RegisterList trace_args = register_allocator()->NewRegisterList(3); ++ builder() ++ ->LoadLiteral(Smi::FromInt(property->position())) ++ .StoreAccumulatorInRegister(trace_args[0]) ++ .MoveRegister(obj, trace_args[1]) ++ .LoadLiteral(property->key()->AsLiteral()->AsRawPropertyName()) ++ .StoreAccumulatorInRegister(trace_args[2]) ++ .CallRuntime(Runtime::kTracePropertyLoad, ++ trace_args); // args: (call-site, this, key) ++ } ++#endif ++ + builder()->SetExpressionPosition(property); + const AstRawString* name = + property->key()->AsLiteral()->AsRawPropertyName(); +@@ -6424,7 +6499,28 @@ void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* property) { + break; + } + case KEYED_PROPERTY: { ++#ifdef VV8_TRACE_PROPERTIES ++ // RESHUFFLED for VisV8--evaluate property key value into a register, not ++ // the accumulator: ++ Register key_reg = VisitForRegisterValue(property->key()); ++ ++ // VisibleV8: generate code to trace keyed property loads ++ { ++ RegisterList trace_args = register_allocator()->NewRegisterList(3); ++ builder() ++ ->LoadLiteral(Smi::FromInt(property->position())) ++ .StoreAccumulatorInRegister(trace_args[0]) ++ .MoveRegister(obj, trace_args[1]) ++ .MoveRegister(key_reg, trace_args[2]) ++ .CallRuntime(Runtime::kTracePropertyLoad, ++ trace_args); // args: (call-site, this, key) ++ } ++ ++ // RESHUFFLED for VisV8--move the stashed key value into the accumulator ++ builder()->LoadAccumulatorWithRegister(key_reg); ++#else + VisitForAccumulatorValueAsPropertyKey(property->key()); ++#endif + builder()->SetExpressionPosition(property); + BuildLoadKeyedProperty(obj, feedback_spec()->AddKeyedLoadICSlot()); + break; +@@ -6739,6 +6835,7 @@ void BytecodeGenerator::VisitCall(Call* expr) { + Expression* callee_expr = expr->expression(); + Call::CallType call_type = expr->GetCallType(); + ++ builder()->CallRuntime(Runtime::kTraceFunctionCall); + if (call_type == Call::SUPER_CALL) { + return VisitCallSuper(expr); + } +@@ -7503,6 +7600,32 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) { + // Perform +1/-1 operation. + builder()->UnaryOperation(expr->op(), feedback_index(count_slot)); + ++#ifdef VV8_TRACE_PROPERTIES ++ // VisibleV8 (trace assignments to named/keyed properties only) ++ if ((assign_type == NAMED_PROPERTY) || (assign_type == KEYED_PROPERTY)) { ++ // Save accumulator for later restoration ++ Register saved_acc = register_allocator()->NewRegister(); ++ builder()->StoreAccumulatorInRegister(saved_acc); ++ ++ // Trace object/property/new-value for this assignment ++ RegisterList trace_args = register_allocator()->NewRegisterList(4); ++ builder() ++ ->LoadLiteral(Smi::FromInt(expr->position())) ++ .StoreAccumulatorInRegister(trace_args[0]) ++ .MoveRegister(object, trace_args[1]) ++ .MoveRegister(saved_acc, trace_args[3]); ++ if (assign_type == NAMED_PROPERTY) { ++ builder()->LoadLiteral(name).StoreAccumulatorInRegister(trace_args[2]); ++ } else { ++ builder()->MoveRegister(key, trace_args[2]); ++ } ++ builder()->CallRuntime(Runtime::kTracePropertyStore, ++ trace_args); // args: (call-site, this, key, value) ++ ++ // Restore accumulator ++ builder()->LoadAccumulatorWithRegister(saved_acc); ++ } ++#endif + // Store the value. + builder()->SetExpressionPosition(expr); + switch (assign_type) { +diff --git a/src/objects/objects-inl.h b/src/objects/objects-inl.h +index 1a2a59ccd71..a3e5ca0011a 100644 +--- a/src/objects/objects-inl.h ++++ b/src/objects/objects-inl.h +@@ -1947,6 +1947,14 @@ MaybeHandle Object::GetPropertyOrElement(Isolate* isolate, + return GetProperty(&it); + } + ++// VisibleV8 ++MaybeHandle Object::VV8GetPropertyOrElementWithNoSideEffects( ++ Isolate* isolate, DirectHandle object, DirectHandle name) { ++ PropertyKey key(isolate, name); ++ LookupIterator it(isolate, object, key); ++ return VV8GetPropertyNoSideEffects(&it); ++} ++ + MaybeDirectHandle Object::SetPropertyOrElement( + Isolate* isolate, DirectHandle object, DirectHandle name, + DirectHandle value, Maybe should_throw, +diff --git a/src/objects/objects.cc b/src/objects/objects.cc +index 4d49c679374..bbe63459194 100644 +--- a/src/objects/objects.cc ++++ b/src/objects/objects.cc +@@ -1286,6 +1286,89 @@ MaybeHandle Object::InstantiateIfLazyClosure( + return MaybeHandle(); + } + ++// VisibleV8 ++// static ++MaybeHandle Object::VV8GetPropertyNoSideEffects( ++ LookupIterator* it, bool is_global_reference) { ++ for (;; it->Next()) { ++ switch (it->state()) { ++ case LookupIterator::TRANSITION: ++ UNREACHABLE(); ++ case LookupIterator::JSPROXY: { ++ bool was_found; ++ DirectHandle receiver = it->GetReceiver(); ++ // In case of global IC, the receiver is the global object. Replace by ++ // the global proxy. ++ if (IsJSGlobalObject(*receiver)) { ++ receiver = handle(Cast(*receiver)->global_proxy(), ++ it->isolate()); ++ } ++ if (is_global_reference) { ++ Maybe maybe = JSProxy::HasProperty( ++ it->isolate(), it->GetHolder(), it->GetName()); ++ if (maybe.IsNothing()) return {}; ++ if (!maybe.FromJust()) { ++ it->NotFound(); ++ return it->isolate()->factory()->undefined_value(); ++ } ++ } ++ MaybeHandle result = ++ JSProxy::GetProperty(it->isolate(), it->GetHolder(), ++ it->GetName(), receiver, &was_found); ++ if (!was_found && !is_global_reference) it->NotFound(); ++ return result; ++ } ++ case LookupIterator::WASM_OBJECT: ++ return it->isolate()->factory()->undefined_value(); ++ case LookupIterator::INTERCEPTOR: { ++ bool done; ++ Handle result; ++ ASSIGN_RETURN_ON_EXCEPTION( ++ it->isolate(), result, ++ JSObject::GetPropertyWithInterceptor(it, &done)); ++ if (done) return result; ++ continue; ++ } ++ case LookupIterator::ACCESS_CHECK: ++ if (it->HasAccess()) continue; ++ return JSObject::GetPropertyWithFailedAccessCheck(it); ++ case LookupIterator::ACCESSOR: ++ return it->isolate()->factory()->undefined_value(); ++ case LookupIterator::TYPED_ARRAY_INDEX_NOT_FOUND: ++ return it->isolate()->factory()->undefined_value(); ++ case LookupIterator::DATA: ++ return it->GetDataValue(); ++ case LookupIterator::STRING_LOOKUP_START_OBJECT: ++ return it->GetStringPropertyValue(); ++ case LookupIterator::NOT_FOUND: ++ if (it->IsAnyPrivateName()) { ++ auto private_symbol = Cast(it->name()); ++ Handle name_string( ++ Cast(private_symbol->description()), it->isolate()); ++ if (private_symbol->is_private_brand()) { ++ Handle class_name = ++ (name_string->length() == 0) ++ ? it->isolate()->factory()->anonymous_string() ++ : name_string; ++ THROW_NEW_ERROR( ++ it->isolate(), ++ NewTypeError(MessageTemplate::kInvalidPrivateBrandInstance, ++ class_name)); ++ } ++ THROW_NEW_ERROR( ++ it->isolate(), ++ NewTypeError(MessageTemplate::kInvalidPrivateMemberRead, ++ name_string)); ++ } ++ ++ return it->isolate()->factory()->undefined_value(); ++ } ++ UNREACHABLE(); ++ } ++} ++ ++// end VisibleV8 ++ + // static + MaybeHandle Object::GetProperty(LookupIterator* it, + bool is_global_reference) { +diff --git a/src/objects/objects.h b/src/objects/objects.h +index f7ecc6533ab..db4a9ca004a 100644 +--- a/src/objects/objects.h ++++ b/src/objects/objects.h +@@ -357,6 +357,11 @@ class Object : public AllStatic { + V8_EXPORT_PRIVATE V8_WARN_UNUSED_RESULT static MaybeHandle + GetProperty(LookupIterator* it, bool is_global_reference = false); + ++ // VisibleV8 ++ V8_EXPORT_PRIVATE V8_WARN_UNUSED_RESULT static MaybeHandle ++ VV8GetPropertyNoSideEffects(LookupIterator* it, ++ bool is_global_reference = false); ++ + // ES6 [[Set]] (when passed kDontThrow) + // Invariants for this and related functions (unless stated otherwise): + // 1) When the result is Nothing, an exception is pending. +@@ -412,6 +417,11 @@ class Object : public AllStatic { + PropertyAttributes attributes, Maybe should_throw, + StoreOrigin store_origin); + ++ // VisibleV8 ++ V8_WARN_UNUSED_RESULT static inline MaybeHandle ++ VV8GetPropertyOrElementWithNoSideEffects(Isolate* isolate, ++ DirectHandle object, ++ DirectHandle name); + V8_WARN_UNUSED_RESULT static inline MaybeHandle GetPropertyOrElement( + Isolate* isolate, DirectHandle object, DirectHandle name); + V8_WARN_UNUSED_RESULT static inline MaybeHandle GetPropertyOrElement( +diff --git a/src/runtime/runtime-compiler.cc b/src/runtime/runtime-compiler.cc +index 01ed8f1256c..8b775cf51ac 100644 +--- a/src/runtime/runtime-compiler.cc ++++ b/src/runtime/runtime-compiler.cc +@@ -25,6 +25,10 @@ + #endif // V8_ENABLE_SPARKPLUG_PLUS + + namespace v8::internal { ++// VisibleV8 ++extern void visv8_log_api_call(Isolate*, bool, Tagged, ++ Tagged, Address*, int); ++// VisibleV8 + + namespace { + void LogExecution(Isolate* isolate, DirectHandle function) { +@@ -774,6 +778,13 @@ RUNTIME_FUNCTION(Runtime_ResolvePossiblyDirectEval) { + return *callee; + } + ++ // VisibleV8 ++ // passing undefined into the reciever since no reciever exists ++ visv8_log_api_call(isolate, false, *args.at(0), ++ ReadOnlyRoots(isolate).undefined_value(), ++ args.address_of_arg_at(1), 1); ++ // VisibleV8 ++ + DCHECK(is_valid_language_mode(args.smi_value_at(3))); + LanguageMode language_mode = static_cast(args.smi_value_at(3)); + DirectHandle outer_info(args.at(2)->shared(), +diff --git a/src/runtime/runtime-test.cc b/src/runtime/runtime-test.cc +index 13ea802bfd1..991578921a8 100644 +--- a/src/runtime/runtime-test.cc ++++ b/src/runtime/runtime-test.cc +@@ -4,17 +4,31 @@ + + #include "src/runtime/runtime.h" + ++#include ++#include + #include ++#include // Horrible VisV8 hack--forgive me... ++#include + ++#include ++#include ++#include + #include + #include ++#include ++#include ++#include ++#include ++#include + ++#include "build/build_config.h" + #include "include/v8-function.h" + #include "include/v8-profiler.h" + #include "src/api/api-inl.h" + #include "src/base/iterator.h" + #include "src/base/macros.h" + #include "src/base/numbers/double.h" ++#include "src/builtins/builtins-utils.h" + #include "src/codegen/compiler.h" + #include "src/codegen/pending-optimization-table.h" + #include "src/common/globals.h" +@@ -45,10 +59,19 @@ + #include "src/objects/js-atomics-synchronization-inl.h" + #include "src/objects/js-function-inl.h" + #include "src/objects/js-regexp-inl.h" ++#include "src/objects/keys.h" + #include "src/objects/smi.h" + #include "src/profiler/heap-snapshot-generator.h" + #include "src/regexp/regexp.h" ++#include "src/runtime/runtime-utils.h" + #include "src/snapshot/snapshot.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) ++ ++#if BUILDFLAG(IS_ANDROID) ++#include ++#endif + + #ifdef V8_ENABLE_MAGLEV + #include "src/maglev/maglev.h" +@@ -1709,6 +1732,85 @@ RUNTIME_FUNCTION(Runtime_TraceExit) { + return obj; // return TOS + } + ++RUNTIME_FUNCTION(Runtime_TracePropertyLoad) { ++ HandleScope hs(isolate); ++ DCHECK_EQ(3, args.length()); ++ ++ // CONVERT_ARG_CHECKED(Smi, call_site, 0); ++ // CONVERT_ARG_CHECKED(Object, obj, 1); ++ // CONVERT_ARG_CHECKED(Object, prop, 2); ++ Tagged call_site = args[0]; ++ Tagged obj = args[1]; ++ Tagged prop = args[2]; ++ ++ v8::internal::visv8_log_property_get(isolate, Smi::ToInt(call_site), obj, ++ prop); ++ ++ return ReadOnlyRoots(isolate).undefined_value(); ++} ++ ++RUNTIME_FUNCTION(Runtime_TracePropertyStore) { ++ HandleScope hs(isolate); ++ // CONVERT_ARG_CHECKED(Smi, call_site, 0); ++ // CONVERT_ARG_CHECKED(Object, obj, 1); ++ // CONVERT_ARG_CHECKED(Object, prop, 2); ++ // CONVERT_ARG_CHECKED(Object, value, 3); ++ ++ Tagged call_site = args[0]; ++ Tagged obj = args[1]; ++ Tagged prop = args[2]; ++ Tagged value = args[3]; ++ ++ v8::internal::visv8_log_property_set(isolate, Smi::ToInt(call_site), obj, ++ prop, value); ++ ++ return ReadOnlyRoots(isolate).undefined_value(); ++} ++ ++// Hack to log almost all scripts that have any kind of function call ++RUNTIME_FUNCTION(Runtime_TraceFunctionCall) { ++ return ReadOnlyRoots(isolate).undefined_value(); ++} ++ ++RUNTIME_FUNCTION(Runtime_VV8TraceKeyedHasIC) { ++ HandleScope shs(isolate); ++ Tagged obj = Cast(args[0]); ++ Tagged prop = Cast(args[1]); ++ ++ v8::internal::visv8_log_property_get(isolate, -1, obj, prop); ++ return ReadOnlyRoots(isolate).undefined_value(); ++} ++ ++RUNTIME_FUNCTION(Runtime_VV8TraceFunctionCall) { ++ HandleScope shs(isolate); ++ Tagged target = Cast(args[0]); ++ Tagged argc = Cast(args[1]); ++ ++ // This peice of code reassembles the mess of a pointer ++ // that we made in HandleApiCallOrConstruct in builtins-call-gen.cc ++ Address argv_ptr_val = 0; ++ for (int i = 2; i < 2 + 4; ++i) { ++ argv_ptr_val <<= 16; ++ CHECK(IsSmi(args[i])); ++ uint32_t chunk = Cast(args[i]).value(); ++ // We encode 16 bit per chunk only! ++ CHECK_EQ(chunk & 0xFFFF0000, 0); ++ argv_ptr_val |= chunk; ++ } ++ ++ int argc_integer = Smi::ToInt(argc) - 1; ++ Tagged reciever = ++ (Tagged)((Tagged*)argv_ptr_val)[-1]; ++ ++ v8::internal::visv8_log_api_call(isolate, false, target, reciever, ++ (Address*)argv_ptr_val, ++ argc_integer >= 0 ? argc_integer : 0); ++ return ReadOnlyRoots(isolate).undefined_value(); ++} ++ ++//------------------------------ ++// END VisibleV8 ++ + RUNTIME_FUNCTION(Runtime_HaveSameMap) { + SealHandleScope shs(isolate); + CHECK_UNLESS_FUZZING(args.length() == 2); +diff --git a/src/runtime/runtime-utils.cc b/src/runtime/runtime-utils.cc +new file mode 100644 +index 00000000000..aac5ab3f6e2 +--- /dev/null ++++ b/src/runtime/runtime-utils.cc +@@ -0,0 +1,704 @@ ++#include "src/runtime/runtime-utils.h" ++namespace v8::internal { ++ ++ // BEGIN VisibleV8 ++//------------------------------ ++// Fastpath replacement for "PrintUC16" that doesn't rely on snprintf ++static void myPrintUC16(Tagged str, std::ostream &out, int start = 0, ++ int end = -1) { ++ static char digits[] = "0123456789abcdef"; ++ char buff[4096]; ++ char *bp = buff; ++ char *bmax = buff + sizeof(buff) - 6; // max length char escape is 6 chars ++ ++ if (end < 0) ++ end = str->length(); ++ StringCharacterStream src(str, start); ++ for (int i = start; i < end && src.HasMore(); ++i) { ++ auto c = src.GetNext(); ++ if (c < ' ') { ++ // Unprintable ASCII ("\xEscaped") ++ *bp++ = '\\'; ++ *bp++ = 'x'; ++ *bp++ = digits[(c & 0xf0) >> 4]; ++ *bp++ = digits[(c & 0x0f)]; ++ } else if (c <= '~') { ++ // Printable ASCII ++ if (c == ':' || c == '\\') { // handle escapes for our output delimiter ++ *bp++ = '\\'; ++ } ++ *bp++ = (char)c; ++ } else { ++ // UC16 (\UEscaped) ++ *bp++ = '\\'; ++ *bp++ = 'u'; ++ *bp++ = digits[(c & 0xf000) >> 12]; ++ *bp++ = digits[(c & 0x0f00) >> 8]; ++ *bp++ = digits[(c & 0x00f0) >> 4]; ++ *bp++ = digits[(c & 0x000f)]; ++ } ++ ++ // Capacity flush ++ if (bp >= bmax) { ++ out.write(buff, bp - buff); ++ bp = buff; ++ } ++ } ++ ++ // Remainder flush ++ if (bp > buff) { ++ out.write(buff, bp - buff); ++ } ++} ++ ++// Fastpath stringify for something simple (Smi, String, ...) ++// (extracted from various 8-cylinder printing functions around V8, all too ++// general/too slow) ++void visv8_to_string(Isolate *isolate, std::ostream &out, Tagged obj, ++ bool quotes = true, int max_len = -1, ++ bool iter_obj = false) { ++ HandleScope scope(isolate); ++ ++ if (IsSmi(obj)) { ++ // Fine, print the stupid integer... ++ out << Cast(obj).value(); ++ } else { ++ // Determine type of HeapObject... ++ if (IsString(obj)) { ++ if (quotes) { ++ out << '"'; ++ } ++ myPrintUC16(Cast(obj), out, 0, max_len); ++ if (quotes) { ++ out << '"'; ++ } ++ } else if (IsNumber(obj)) { ++ out << Object::NumberValue(obj); ++ } else if (IsOddball(obj)) { ++ switch (Cast(obj)->kind()) { ++ case Oddball::kFalse: ++ out << "#F"; ++ break; ++ case Oddball::kTrue: ++ out << "#T"; ++ break; ++ case Oddball::kNull: ++ out << "#N"; ++ break; ++ case Oddball::kUndefined: ++ out << "#U"; ++ break; ++ default: ++ out << "#?"; ++ } ++ } else if (IsJSFunction(obj)) { ++ auto info = Cast(obj)->shared(); ++ if (!info->IsUserJavaScript()) { ++ out << '%'; ++ } ++ ++ auto name = info->Name(); ++ if (name->length()) { ++ myPrintUC16(name, out, 0, max_len); ++ } else { ++ out << ""; ++ } ++ } else if (IsJSRegExp(obj)) { ++ out << '/'; ++ myPrintUC16(Cast(obj)->source(), out, 0, max_len); ++ out << '/'; ++ } else if (IsJSReceiver(obj)) { ++ Handle rcvr = handle(Cast(obj), isolate); ++ DirectHandle ctor = JSReceiver::GetConstructorName(isolate, rcvr); ++ out << '{'; ++ out << rcvr->GetOrCreateIdentityHash(isolate).value(); ++ if (iter_obj && (strcmp(ctor->ToCString().get(), "Object") == 0 || ++ strcmp(ctor->ToCString().get(), "Array") == 0)) { ++ // We are encountering this object for the first time, iterate it! ++ Handle contents; ++ do { ++ if (!(KeyAccumulator::GetKeys( ++ isolate, rcvr, KeyCollectionMode::kOwnOnly, ++ ENUMERABLE_STRINGS, GetKeysConversion::kConvertToString)) ++ .ToHandle(&contents)) { ++ DCHECK((isolate)->has_exception()); ++ return; ++ } ++ } while (false); ++ ++ for (int i = 0; i < contents->length(); i++) { ++ out << ','; ++ Handle key(Cast(contents->get(i)), isolate); ++ Handle property; ++ // Add the key to the trace logs ++ myPrintUC16(*key, out, 0, max_len); ++ out << "\\:"; ++ do { ++ if (!(Object::VV8GetPropertyOrElementWithNoSideEffects(isolate, ++ rcvr, key)) ++ .ToHandle(&property)) { ++ DCHECK((isolate)->has_exception()); ++ return; ++ } ++ } while (false); ++ // Recurse with the option to not go deeper ++ visv8_to_string(isolate, out, *property, true, -1, false); ++ } ++ } else { ++ // We are inside a nested object, do not go deeper! ++ out << ','; ++ myPrintUC16(*ctor, out, 0, max_len); ++ } ++ out << '}'; ++ } else { ++ out << '?'; ++ } ++ } ++} ++ ++// TLS storage slot key for per-thread output streams for our trace logging ++static pthread_key_t visv8_out_key; ++ ++// Type used to aggregate all TLS data into one POD object ++struct VisV8TlsData { ++ // Since looking up window.origin can trigger recursion, we need to know when ++ // to ignore API calls ++ int rcount; ++ // std filestream used to log records to disk for this thread ++ std::ofstream log; ++ ++ // Context (last-encountered Isolate, and last SID within that Isolate) ++ Isolate *last_isolate; ++ int last_script_id; ++ bool isolate_changed; ++ ++ // Log file name generator pattern (for log rollover on large size) ++ int next_log; ++ char log_name_pattern[256]; ++ ++ // Small/simple "set" of seen Isolate/SID pairs (to avoid re-dumping script ++ // source/etc. within one log) ++ std::vector> seen_sids; ++ ++ // To track @origin (SOP), we need to look up the window.origin string; keep a ++ // cached copy (and a scratch buffer) ++ std::ostringstream last_origin_url; ++ std::ostringstream origin_url_scratch; ++ ++ // Dumb constructor ++ VisV8TlsData() ++ : rcount(0), last_isolate(nullptr), last_script_id(-1), ++ isolate_changed(true), next_log(0) { ++ // HACK: only direct pthread call can recover thread "name" [can't get ++ // current Thread object from V8?] ++ char thread_name[16] = ""; ++#if BUILDFLAG(IS_ANDROID) ++ if (prctl(PR_GET_NAME, thread_name, 0, 0, 0)) { ++ perror("prctl"); ++ } ++ char log_name[] = "/sdcard/Documents/vv8-%ld-%d-%d-%s.%%d.log"; ++#else ++ if (pthread_getname_np(pthread_self(), thread_name, sizeof(thread_name))) { ++ perror("pthread_getname_np"); ++ } ++ char log_name[] = "vv8-%ld-%d-%d-%s.%%d.log"; ++#endif ++ // Use thread name et al. to construct our log name pattern ++ snprintf(log_name_pattern, sizeof log_name_pattern, log_name, ++ (long)base::OS::TimeCurrentMillis(), ++ base::OS::GetCurrentProcessId(), base::OS::GetCurrentThreadId(), ++ thread_name); ++ ++ // And go ahead/open our next log file ++ open_next_log_file(); ++ ++ last_origin_url << std::ends; // Initialize this to the empty string to ++ // avoid sadness later ++ } ++ ++ void open_next_log_file() { ++ char log_name[256]; ++ ++ if (log.is_open()) ++ log.close(); ++ snprintf(log_name, sizeof log_name, log_name_pattern, next_log++); ++ log.open(log_name); ++ ++ if (!log) { ++ perror(log_name); ++ abort(); ++ } ++ } ++ ++ // Destructor: close and delete file stream object, reset all key fields to ++ // null/invalid state ++ ~VisV8TlsData() { ++ log.close(); ++ reset_isolate(nullptr); ++ } ++ ++ // Reset all context state for a new/different isolate ++ void reset_isolate(Isolate *isolate) { ++ last_isolate = isolate; ++ last_origin_url.clear(); ++ std::ostringstream().swap(last_origin_url); ++ last_script_id = -1; ++ isolate_changed = true; ++ } ++ ++ // Log the current "last_isolate" ++ void log_isolate() { ++ log << '~' << (void *)last_isolate << '\n'; ++ isolate_changed = false; ++ } ++ ++ // Predicate: have we logged a given isolate/SID pair yet? ++ bool check_sid(Isolate *isolate, int sid) { ++ return std::binary_search(std::begin(seen_sids), std::end(seen_sids), ++ std::make_pair(isolate, sid)); ++ } ++ ++ // Utility: insert an isolate/SID pair into our primitive set (no checks for ++ // duplicates) ++ void add_sid(Isolate *isolate, int sid) { ++ auto val = std::make_pair(isolate, sid); ++ seen_sids.insert( ++ std::upper_bound(std::begin(seen_sids), std::end(seen_sids), val), val); ++ } ++ ++ // Utility: log a '$' record for the given script object ++ void log_script(Isolate *isolate, Tagged