Commit ebd070ec authored by Alexey Kozyatinskiy's avatar Alexey Kozyatinskiy Committed by Commit Bot

reland: inspector: move injected script source to native

- introduced ValueMirror interface, this interface contains methods to generate
  different protocol entities,
- introduced DebugPropertyIterator, this iterator iterates through object properties
  in the following order: exotic indices, enumerable strings, all other properties,
- removed all injected script infra, e.g. closure compiler,

R=dgozman@chromium.org
TBR=yangguo@chromium.org

Bug: chromium:595206
Change-Id: I030fdb3a80074ca6edd4749f86b39b590776ae6f
Reviewed-on: https://chromium-review.googlesource.com/c/1310056Reviewed-by: 's avatarAleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57181}
parent 6dcbd4ff
......@@ -1937,6 +1937,8 @@ v8_source_set("v8_base") {
"src/debug/debug-frames.cc",
"src/debug/debug-frames.h",
"src/debug/debug-interface.h",
"src/debug/debug-property-iterator.cc",
"src/debug/debug-property-iterator.h",
"src/debug/debug-scope-iterator.cc",
"src/debug/debug-scope-iterator.h",
"src/debug/debug-scopes.cc",
......
......@@ -245,17 +245,6 @@ hooks = [
'-s', 'v8/test/wasm-spec-tests/tests.tar.gz.sha1',
],
},
{
'name': 'closure_compiler',
'pattern': '.',
'action': [ 'download_from_google_storage',
'--no_resume',
'--no_auth',
'-u',
'--bucket', 'chromium-v8-closure-compiler',
'-s', 'v8/src/inspector/build/closure-compiler.tar.gz.sha1',
],
},
{
'name': 'sysroot_arm',
'pattern': '.',
......
......@@ -9548,20 +9548,8 @@ Local<Function> debug::GetBuiltin(Isolate* v8_isolate, Builtin builtin) {
i::HandleScope handle_scope(isolate);
i::Builtins::Name builtin_id;
switch (builtin) {
case kObjectKeys:
builtin_id = i::Builtins::kObjectKeys;
break;
case kObjectGetPrototypeOf:
builtin_id = i::Builtins::kObjectGetPrototypeOf;
break;
case kObjectGetOwnPropertyDescriptor:
builtin_id = i::Builtins::kObjectGetOwnPropertyDescriptor;
break;
case kObjectGetOwnPropertyNames:
builtin_id = i::Builtins::kObjectGetOwnPropertyNames;
break;
case kObjectGetOwnPropertySymbols:
builtin_id = i::Builtins::kObjectGetOwnPropertySymbols;
case kStringToLowerCase:
builtin_id = i::Builtins::kStringPrototypeToLocaleLowerCase;
break;
default:
UNREACHABLE();
......@@ -9569,10 +9557,11 @@ Local<Function> debug::GetBuiltin(Isolate* v8_isolate, Builtin builtin) {
i::Handle<i::String> name = isolate->factory()->empty_string();
i::NewFunctionArgs args = i::NewFunctionArgs::ForBuiltinWithoutPrototype(
name, builtin_id, i::LanguageMode::kSloppy);
name, builtin_id, i::LanguageMode::kStrict);
i::Handle<i::JSFunction> fun = isolate->factory()->NewFunction(args);
fun->shared()->DontAdaptArguments();
fun->shared()->set_internal_formal_parameter_count(0);
fun->shared()->set_length(0);
return Utils::ToLocal(handle_scope.CloseAndEscape(fun));
}
......@@ -9697,41 +9686,6 @@ void debug::SetReturnValue(v8::Isolate* v8_isolate,
isolate->debug()->set_return_value(*Utils::OpenHandle(*value));
}
int debug::GetNativeAccessorDescriptor(v8::Local<v8::Context> context,
v8::Local<v8::Object> v8_object,
v8::Local<v8::Name> v8_name) {
i::Handle<i::JSReceiver> object = Utils::OpenHandle(*v8_object);
i::Handle<i::Name> name = Utils::OpenHandle(*v8_name);
uint32_t index;
if (name->AsArrayIndex(&index)) {
return static_cast<int>(debug::NativeAccessorType::None);
}
i::LookupIterator it = i::LookupIterator(object->GetIsolate(), object, name,
i::LookupIterator::OWN);
if (!it.IsFound()) return static_cast<int>(debug::NativeAccessorType::None);
if (it.state() != i::LookupIterator::ACCESSOR) {
return static_cast<int>(debug::NativeAccessorType::None);
}
i::Handle<i::Object> structure = it.GetAccessors();
if (!structure->IsAccessorInfo()) {
return static_cast<int>(debug::NativeAccessorType::None);
}
auto isolate = reinterpret_cast<i::Isolate*>(context->GetIsolate());
int result = 0;
#define IS_BUILTIN_ACESSOR(_, name, ...) \
if (*structure == *isolate->factory()->name##_accessor()) \
result |= static_cast<int>(debug::NativeAccessorType::IsBuiltin);
ACCESSOR_INFO_LIST_GENERATOR(IS_BUILTIN_ACESSOR, /* not used */)
#undef IS_BUILTIN_ACESSOR
i::Handle<i::AccessorInfo> accessor_info =
i::Handle<i::AccessorInfo>::cast(structure);
if (accessor_info->getter())
result |= static_cast<int>(debug::NativeAccessorType::HasGetter);
if (accessor_info->setter())
result |= static_cast<int>(debug::NativeAccessorType::HasSetter);
return result;
}
int64_t debug::GetNextRandomInt64(v8::Isolate* v8_isolate) {
return reinterpret_cast<i::Isolate*>(v8_isolate)
->random_number_generator()
......
......@@ -193,13 +193,7 @@ void ResetBlackboxedStateCache(Isolate* isolate,
int EstimatedValueSize(Isolate* isolate, v8::Local<v8::Value> value);
enum Builtin {
kObjectKeys,
kObjectGetPrototypeOf,
kObjectGetOwnPropertyDescriptor,
kObjectGetOwnPropertyNames,
kObjectGetOwnPropertySymbols,
};
enum Builtin { kStringToLowerCase };
Local<Function> GetBuiltin(Isolate* isolate, Builtin builtin);
......@@ -474,14 +468,9 @@ void SetReturnValue(v8::Isolate* isolate, v8::Local<v8::Value> value);
enum class NativeAccessorType {
None = 0,
HasGetter = 1 << 0,
HasSetter = 1 << 1,
IsBuiltin = 1 << 2
HasSetter = 1 << 1
};
int GetNativeAccessorDescriptor(v8::Local<v8::Context> context,
v8::Local<v8::Object> object,
v8::Local<v8::Name> name);
int64_t GetNextRandomInt64(v8::Isolate* isolate);
v8::MaybeLocal<v8::Value> EvaluateGlobal(v8::Isolate* isolate,
......@@ -518,6 +507,39 @@ class WeakMap : public v8::Object {
private:
WeakMap();
};
struct PropertyDescriptor {
bool enumerable : 1;
bool has_enumerable : 1;
bool configurable : 1;
bool has_configurable : 1;
bool writable : 1;
bool has_writable : 1;
v8::Local<v8::Value> value;
v8::Local<v8::Value> get;
v8::Local<v8::Value> set;
};
class PropertyIterator {
public:
static std::unique_ptr<PropertyIterator> Create(v8::Local<v8::Object> object);
virtual ~PropertyIterator() = default;
virtual bool Done() const = 0;
virtual void Advance() = 0;
virtual v8::Local<v8::Name> name() const = 0;
virtual bool is_native_accessor() = 0;
virtual bool has_native_getter() = 0;
virtual bool has_native_setter() = 0;
virtual Maybe<PropertyAttribute> attributes() = 0;
virtual Maybe<PropertyDescriptor> descriptor() = 0;
virtual bool is_own() = 0;
virtual bool is_array_index() = 0;
};
} // namespace debug
} // namespace v8
......
// 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.
#include "src/debug/debug-property-iterator.h"
#include "src/api-inl.h"
#include "src/base/flags.h"
#include "src/keys.h"
#include "src/objects/js-array-buffer-inl.h"
#include "src/property-descriptor.h"
#include "src/property-details.h"
namespace v8 {
std::unique_ptr<debug::PropertyIterator> debug::PropertyIterator::Create(
v8::Local<v8::Object> v8_object) {
internal::Isolate* isolate =
reinterpret_cast<internal::Isolate*>(v8_object->GetIsolate());
return std::unique_ptr<debug::PropertyIterator>(
new internal::DebugPropertyIterator(isolate,
Utils::OpenHandle(*v8_object)));
}
namespace internal {
DebugPropertyIterator::DebugPropertyIterator(Isolate* isolate,
Handle<JSReceiver> receiver)
: isolate_(isolate),
prototype_iterator_(isolate, receiver, kStartAtReceiver,
PrototypeIterator::END_AT_NULL) {
if (receiver->IsJSProxy()) {
is_own_ = false;
prototype_iterator_.AdvanceIgnoringProxies();
}
if (prototype_iterator_.IsAtEnd()) return;
FillKeysForCurrentPrototypeAndStage();
if (should_move_to_next_stage()) Advance();
}
bool DebugPropertyIterator::Done() const {
return prototype_iterator_.IsAtEnd();
}
void DebugPropertyIterator::Advance() {
++current_key_index_;
calculated_native_accessor_flags_ = false;
while (should_move_to_next_stage()) {
switch (stage_) {
case Stage::kExoticIndices:
stage_ = Stage::kEnumerableStrings;
break;
case Stage::kEnumerableStrings:
stage_ = Stage::kAllProperties;
break;
case Stage::kAllProperties:
stage_ = kExoticIndices;
is_own_ = false;
prototype_iterator_.AdvanceIgnoringProxies();
break;
}
FillKeysForCurrentPrototypeAndStage();
}
}
bool DebugPropertyIterator::is_native_accessor() {
if (stage_ == kExoticIndices) return false;
CalculateNativeAccessorFlags();
return native_accessor_flags_;
}
bool DebugPropertyIterator::has_native_getter() {
if (stage_ == kExoticIndices) return false;
CalculateNativeAccessorFlags();
return native_accessor_flags_ &
static_cast<int>(debug::NativeAccessorType::HasGetter);
}
bool DebugPropertyIterator::has_native_setter() {
if (stage_ == kExoticIndices) return false;
CalculateNativeAccessorFlags();
return native_accessor_flags_ &
static_cast<int>(debug::NativeAccessorType::HasSetter);
}
Handle<Name> DebugPropertyIterator::raw_name() const {
DCHECK(!Done());
if (stage_ == kExoticIndices) {
return isolate_->factory()->Uint32ToString(current_key_index_);
} else {
return Handle<Name>::cast(
FixedArray::get(*keys_, current_key_index_, isolate_));
}
}
v8::Local<v8::Name> DebugPropertyIterator::name() const {
return Utils::ToLocal(raw_name());
}
v8::Maybe<v8::PropertyAttribute> DebugPropertyIterator::attributes() {
Handle<JSReceiver> receiver =
PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
auto result = JSReceiver::GetPropertyAttributes(receiver, raw_name());
if (result.IsNothing()) return Nothing<v8::PropertyAttribute>();
DCHECK(result.FromJust() != ABSENT);
return Just(static_cast<v8::PropertyAttribute>(result.FromJust()));
}
v8::Maybe<v8::debug::PropertyDescriptor> DebugPropertyIterator::descriptor() {
Handle<JSReceiver> receiver =
PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
PropertyDescriptor descriptor;
Maybe<bool> did_get_descriptor = JSReceiver::GetOwnPropertyDescriptor(
isolate_, receiver, raw_name(), &descriptor);
if (did_get_descriptor.IsNothing()) {
return Nothing<v8::debug::PropertyDescriptor>();
}
DCHECK(did_get_descriptor.FromJust());
return Just(v8::debug::PropertyDescriptor{
descriptor.enumerable(), descriptor.has_enumerable(),
descriptor.configurable(), descriptor.has_configurable(),
descriptor.writable(), descriptor.has_writable(),
descriptor.has_value() ? Utils::ToLocal(descriptor.value())
: v8::Local<v8::Value>(),
descriptor.has_get() ? Utils::ToLocal(descriptor.get())
: v8::Local<v8::Value>(),
descriptor.has_set() ? Utils::ToLocal(descriptor.set())
: v8::Local<v8::Value>(),
});
}
bool DebugPropertyIterator::is_own() { return is_own_; }
bool DebugPropertyIterator::is_array_index() {
if (stage_ == kExoticIndices) return true;
uint32_t index = 0;
return raw_name()->AsArrayIndex(&index);
}
void DebugPropertyIterator::FillKeysForCurrentPrototypeAndStage() {
current_key_index_ = 0;
exotic_length_ = 0;
keys_ = Handle<FixedArray>::null();
if (prototype_iterator_.IsAtEnd()) return;
Handle<JSReceiver> receiver =
PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
bool has_exotic_indices = receiver->IsJSTypedArray();
if (stage_ == kExoticIndices) {
if (!has_exotic_indices) return;
exotic_length_ = static_cast<uint32_t>(
Handle<JSTypedArray>::cast(receiver)->length_value());
return;
}
bool skip_indices = has_exotic_indices;
PropertyFilter filter =
stage_ == kEnumerableStrings ? ENUMERABLE_STRINGS : ALL_PROPERTIES;
if (!KeyAccumulator::GetKeys(receiver, KeyCollectionMode::kOwnOnly, filter,
GetKeysConversion::kConvertToString, false,
skip_indices)
.ToHandle(&keys_)) {
keys_ = Handle<FixedArray>::null();
}
}
bool DebugPropertyIterator::should_move_to_next_stage() const {
if (prototype_iterator_.IsAtEnd()) return false;
if (stage_ == kExoticIndices) return current_key_index_ >= exotic_length_;
return keys_.is_null() ||
current_key_index_ >= static_cast<uint32_t>(keys_->length());
}
namespace {
base::Flags<debug::NativeAccessorType, int> GetNativeAccessorDescriptorInternal(
Handle<JSReceiver> object, Handle<Name> name) {
uint32_t index;
if (name->AsArrayIndex(&index)) return debug::NativeAccessorType::None;
LookupIterator it =
LookupIterator(object->GetIsolate(), object, name, LookupIterator::OWN);
if (!it.IsFound()) return debug::NativeAccessorType::None;
if (it.state() != LookupIterator::ACCESSOR) {
return debug::NativeAccessorType::None;
}
Handle<Object> structure = it.GetAccessors();
if (!structure->IsAccessorInfo()) return debug::NativeAccessorType::None;
auto isolate = object->GetIsolate();
base::Flags<debug::NativeAccessorType, int> result;
#define IS_BUILTIN_ACESSOR(_, name, ...) \
if (*structure == *isolate->factory()->name##_accessor()) \
return debug::NativeAccessorType::None;
ACCESSOR_INFO_LIST_GENERATOR(IS_BUILTIN_ACESSOR, /* not used */)
#undef IS_BUILTIN_ACESSOR
Handle<AccessorInfo> accessor_info = Handle<AccessorInfo>::cast(structure);
if (accessor_info->getter()) {
result |= debug::NativeAccessorType::HasGetter;
}
if (accessor_info->setter()) {
result |= debug::NativeAccessorType::HasSetter;
}
return result;
}
} // anonymous namespace
void DebugPropertyIterator::CalculateNativeAccessorFlags() {
if (calculated_native_accessor_flags_) return;
Handle<JSReceiver> receiver =
PrototypeIterator::GetCurrent<JSReceiver>(prototype_iterator_);
native_accessor_flags_ =
GetNativeAccessorDescriptorInternal(receiver, raw_name());
calculated_native_accessor_flags_ = true;
}
} // namespace internal
} // namespace v8
// 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 V8_DEBUG_DEBUG_PROPERTY_ITERATOR_H_
#define V8_DEBUG_DEBUG_PROPERTY_ITERATOR_H_
#include "src/debug/debug-interface.h"
#include "src/handles.h"
#include "src/isolate.h"
#include "src/prototype.h"
#include "include/v8.h"
namespace v8 {
namespace internal {
class JSReceiver;
class DebugPropertyIterator final : public debug::PropertyIterator {
public:
DebugPropertyIterator(Isolate* isolate, Handle<JSReceiver> receiver);
~DebugPropertyIterator() override = default;
bool Done() const override;
void Advance() override;
v8::Local<v8::Name> name() const override;
bool is_native_accessor() override;
bool has_native_getter() override;
bool has_native_setter() override;
v8::Maybe<v8::PropertyAttribute> attributes() override;
v8::Maybe<v8::debug::PropertyDescriptor> descriptor() override;
bool is_own() override;
bool is_array_index() override;
private:
void FillKeysForCurrentPrototypeAndStage();
bool should_move_to_next_stage() const;
void CalculateNativeAccessorFlags();
Handle<Name> raw_name() const;
Isolate* isolate_;
PrototypeIterator prototype_iterator_;
enum Stage { kExoticIndices = 0, kEnumerableStrings = 1, kAllProperties = 2 };
Stage stage_ = kExoticIndices;
uint32_t current_key_index_ = 0;
Handle<FixedArray> keys_;
uint32_t exotic_length_ = 0;
bool calculated_native_accessor_flags_ = false;
int native_accessor_flags_ = 0;
bool is_own_ = true;
DISALLOW_COPY_AND_ASSIGN(DebugPropertyIterator);
};
} // namespace internal
} // namespace v8
#endif // V8_DEBUG_DEBUG_PROPERTY_ITERATOR_H_
......@@ -61,22 +61,6 @@ inspector_protocol_generate("protocol_generated_sources") {
outputs = _protocol_generated
}
action("inspector_injected_script") {
visibility = [ ":*" ] # Only targets in this file can depend on this.
script = "build/xxd.py"
inputs = [
"injected-script-source.js",
]
outputs = [
"$target_gen_dir/injected-script-source.h",
]
args = [
"InjectedScriptSource_js",
rebase_path("injected-script-source.js", root_build_dir),
rebase_path("$target_gen_dir/injected-script-source.h", root_build_dir),
]
}
config("inspector_config") {
visibility = [ ":*" ] # Only targets in this file can depend on this.
if (is_component_build) {
......@@ -86,7 +70,6 @@ config("inspector_config") {
v8_source_set("inspector") {
deps = [
":inspector_injected_script",
":protocol_generated_sources",
]
configs = [ ":inspector_config" ]
......@@ -101,7 +84,6 @@ v8_source_set("inspector") {
"../../include/v8-inspector-protocol.h",
"../../include/v8-inspector.h",
]
sources += get_target_outputs(":inspector_injected_script")
sources += [
"custom-preview.cc",
"custom-preview.h",
......@@ -131,18 +113,12 @@ v8_source_set("inspector") {
"v8-debugger-script.h",
"v8-debugger.cc",
"v8-debugger.h",
"v8-function-call.cc",
"v8-function-call.h",
"v8-heap-profiler-agent-impl.cc",
"v8-heap-profiler-agent-impl.h",
"v8-injected-script-host.cc",
"v8-injected-script-host.h",
"v8-inspector-impl.cc",
"v8-inspector-impl.h",
"v8-inspector-session-impl.cc",
"v8-inspector-session-impl.h",
"v8-internal-value-type.cc",
"v8-internal-value-type.h",
"v8-profiler-agent-impl.cc",
"v8-profiler-agent-impl.h",
"v8-regex.cc",
......@@ -155,6 +131,8 @@ v8_source_set("inspector") {
"v8-stack-trace-impl.h",
"v8-value-utils.cc",
"v8-value-utils.h",
"value-mirror.cc",
"value-mirror.h",
"wasm-translation.cc",
"wasm-translation.h",
]
......
#!/usr/bin/env python
#
# 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.
"""v8_inspect presubmit script
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into gcl.
"""
compile_note = "Be sure to run your patch by the compile-scripts.py script prior to committing!"
def _CompileScripts(input_api, output_api):
local_paths = [f.LocalPath() for f in input_api.AffectedFiles()]
compilation_related_files = [
"js_protocol.json"
"compile-scripts.js",
"injected-script-source.js",
"injected_script_externs.js",
"check_injected_script_source.js"
]
for file in compilation_related_files:
if (any(file in path for path in local_paths)):
script_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
"build", "compile-scripts.py")
proc = input_api.subprocess.Popen(
[input_api.python_executable, script_path],
stdout=input_api.subprocess.PIPE,
stderr=input_api.subprocess.STDOUT)
out, _ = proc.communicate()
if "ERROR" in out or "WARNING" in out or proc.returncode:
return [output_api.PresubmitError(out)]
if "NOTE" in out:
return [output_api.PresubmitPromptWarning(out + compile_note)]
return []
return []
def CheckChangeOnUpload(input_api, output_api):
results = []
results.extend(_CompileScripts(input_api, output_api))
return results
def CheckChangeOnCommit(input_api, output_api):
results = []
results.extend(_CompileScripts(input_api, output_api))
return results
#!/usr/bin/env python
# Copyright (c) 2014 Google Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * 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.
# * Neither the name of Google Inc. 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
# OWNER 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.
#
# Copied from blink:
# WebKit/Source/devtools/scripts/check_injected_script_source.py
#
import re
import sys
import os
def validate_injected_script(fileName):
f = open(fileName, "r")
lines = f.readlines()
f.close()
proto_functions = "|".join([
# Array.prototype.*
"concat", "every", "filter", "forEach", "indexOf", "join", "lastIndexOf", "map", "pop",
"push", "reduce", "reduceRight", "reverse", "shift", "slice", "some", "sort", "splice", "toLocaleString", "toString", "unshift",
# Function.prototype.*
"apply", "bind", "call", "isGenerator", "toSource",
# Object.prototype.*
"toString",
])
global_functions = "|".join([
"eval", "uneval", "isFinite", "isNaN", "parseFloat", "parseInt", "decodeURI", "decodeURIComponent",
"encodeURI", "encodeURIComponent", "escape", "unescape", "Map", "Set"
])
# Black list:
# - instanceof, since e.g. "obj instanceof Error" may throw if Error is overridden and is not a function
# - Object.prototype.toString()
# - Array.prototype.*
# - Function.prototype.*
# - Math.*
# - Global functions
black_list_call_regex = re.compile(r"\sinstanceof\s+\w*|\bMath\.\w+\(|(?<!InjectedScriptHost)\.(" + proto_functions + r")\(|[^\.]\b(" + global_functions + r")\(")
errors_found = False
for i, line in enumerate(lines):
if line.find("suppressBlacklist") != -1:
continue
for match in re.finditer(black_list_call_regex, line):
errors_found = True
print "ERROR: Black listed expression in %s at line %02d column %02d: %s" % (os.path.basename(fileName), i + 1, match.start(), match.group(0))
if not errors_found:
print "OK"
def main(argv):
if len(argv) < 2:
print('ERROR: Usage: %s path/to/injected-script-source.js' % argv[0])
return 1
validate_injected_script(argv[1])
if __name__ == '__main__':
sys.exit(main(sys.argv))
69937d3c239ca63e4c9045718886ddd096ffc054
\ No newline at end of file
#!/usr/bin/env python
#
# 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.
import os
import os.path as path
import generate_protocol_externs
import re
import subprocess
import sys
if len(sys.argv) == 2 and sys.argv[1] == '--help':
print("Usage: %s" % path.basename(sys.argv[0]))
sys.exit(0)
java_required_major = 1
java_required_minor = 7
v8_inspector_path = path.dirname(path.dirname(path.abspath(__file__)))
protocol_externs_file = path.join(v8_inspector_path, 'protocol_externs.js')
injected_script_source_name = path.join(v8_inspector_path,
'injected-script-source.js')
injected_script_externs_file = path.join(v8_inspector_path,
'injected_script_externs.js')
generate_protocol_externs.generate_protocol_externs(protocol_externs_file,
path.join(v8_inspector_path, 'js_protocol.json'))
error_warning_regex = re.compile(r'WARNING|ERROR')
closure_compiler_jar = path.join(v8_inspector_path, 'build',
'closure-compiler', 'closure-compiler.jar')
common_closure_args = [
'--checks_only',
'--warning_level', 'VERBOSE'
]
# Error reporting and checking.
errors_found = False
def popen(arguments):
return subprocess.Popen(arguments, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
def error_excepthook(exctype, value, traceback):
print 'ERROR:'
sys.__excepthook__(exctype, value, traceback)
sys.excepthook = error_excepthook
def has_errors(output):
return re.search(error_warning_regex, output) != None
# Find java. Based on
# http://stackoverflow.com/questions/377017/test-if-executable-exists-in-python.
def which(program):
def is_exe(fpath):
return path.isfile(fpath) and os.access(fpath, os.X_OK)
fpath, fname = path.split(program)
if fpath:
if is_exe(program):
return program
else:
for part in os.environ['PATH'].split(os.pathsep):
part = part.strip('"')
exe_file = path.join(part, program)
if is_exe(exe_file):
return exe_file
return None
def find_java():
exec_command = None
has_server_jvm = True
java_path = which('java')
if not java_path:
java_path = which('java.exe')
if not java_path:
print 'NOTE: No Java executable found in $PATH.'
sys.exit(0)
is_ok = False
java_version_out, _ = popen([java_path, '-version']).communicate()
java_build_regex = re.compile(r'^\w+ version "(\d+)\.(\d+)')
# pylint: disable=E1103
match = re.search(java_build_regex, java_version_out)
if match:
major = int(match.group(1))
minor = int(match.group(2))
is_ok = major >= java_required_major and minor >= java_required_minor
if is_ok:
exec_command = [java_path, '-Xms1024m', '-server',
'-XX:+TieredCompilation']
check_server_proc = popen(exec_command + ['-version'])
check_server_proc.communicate()
if check_server_proc.returncode != 0:
# Not all Java installs have server JVMs.
exec_command = exec_command.remove('-server')
has_server_jvm = False
if not is_ok:
print 'NOTE: Java executable version %d.%d or above not found in $PATH.' % (java_required_major, java_required_minor)
sys.exit(0)
print 'Java executable: %s%s' % (java_path, '' if has_server_jvm else ' (no server JVM)')
return exec_command
java_exec = find_java()
spawned_compiler_command = java_exec + [
'-jar',
closure_compiler_jar
] + common_closure_args
print 'Compiling injected-script-source.js...'
command = spawned_compiler_command + [
'--externs', injected_script_externs_file,
'--externs', protocol_externs_file,
'--js', injected_script_source_name
]
injected_script_compile_proc = popen(command)
print 'Validating injected-script-source.js...'
injectedscript_check_script_path = path.join(v8_inspector_path, 'build',
'check_injected_script_source.py')
validate_injected_script_proc = popen([sys.executable,
injectedscript_check_script_path, injected_script_source_name])
print
(injected_script_compile_out, _) = injected_script_compile_proc.communicate()
print 'injected-script-source.js compilation output:%s' % os.linesep
print injected_script_compile_out
errors_found |= has_errors(injected_script_compile_out)
(validate_injected_script_out, _) = validate_injected_script_proc.communicate()
print 'Validate injected-script-source.js output:%s' % os.linesep
print validate_injected_script_out if validate_injected_script_out else '<empty>'
errors_found |= has_errors(validate_injected_script_out)
os.remove(protocol_externs_file)
if errors_found:
print 'ERRORS DETECTED'
sys.exit(1)
#!/usr/bin/env python
# Copyright (c) 2011 Google Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * 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.
# * Neither the name of Google Inc. 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
# OWNER 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.
import os
import re
import json
type_traits = {
"any": "*",
"string": "string",
"integer": "number",
"number": "number",
"boolean": "boolean",
"array": "!Array.<*>",
"object": "!Object",
}
promisified_domains = {
"Accessibility",
"Animation",
"CSS",
"Emulation",
"Profiler"
}
ref_types = {}
def full_qualified_type_id(domain_name, type_id):
if type_id.find(".") == -1:
return "%s.%s" % (domain_name, type_id)
return type_id
def fix_camel_case(name):
prefix = ""
if name[0] == "-":
prefix = "Negative"
name = name[1:]
refined = re.sub(r'-(\w)', lambda pat: pat.group(1).upper(), name)
refined = to_title_case(refined)
return prefix + re.sub(r'(?i)HTML|XML|WML|API', lambda pat: pat.group(0).upper(), refined)
def to_title_case(name):
return name[:1].upper() + name[1:]
def generate_enum(name, json):
enum_members = []
for member in json["enum"]:
enum_members.append(" %s: \"%s\"" % (fix_camel_case(member), member))
return "\n/** @enum {string} */\n%s = {\n%s\n};\n" % (name, (",\n".join(enum_members)))
def param_type(domain_name, param):
if "type" in param:
if param["type"] == "array":
items = param["items"]
return "!Array.<%s>" % param_type(domain_name, items)
else:
return type_traits[param["type"]]
if "$ref" in param:
type_id = full_qualified_type_id(domain_name, param["$ref"])
if type_id in ref_types:
return ref_types[type_id]
else:
print "Type not found: " + type_id
return "!! Type not found: " + type_id
def load_schema(file, domains):
input_file = open(file, "r")
json_string = input_file.read()
parsed_json = json.loads(json_string)
domains.extend(parsed_json["domains"])
def generate_protocol_externs(output_path, file1):
domains = []
load_schema(file1, domains)
output_file = open(output_path, "w")
output_file.write(
"""
var InspectorBackend = {}
var Protocol = {};
/** @typedef {string}*/
Protocol.Error;
""")
for domain in domains:
domain_name = domain["domain"]
if "types" in domain:
for type in domain["types"]:
type_id = full_qualified_type_id(domain_name, type["id"])
ref_types[type_id] = "%sAgent.%s" % (domain_name, type["id"])
for domain in domains:
domain_name = domain["domain"]
promisified = domain_name in promisified_domains
output_file.write("\n\n/**\n * @constructor\n*/\n")
output_file.write("Protocol.%sAgent = function(){};\n" % domain_name)
if "commands" in domain:
for command in domain["commands"]:
output_file.write("\n/**\n")
params = []
has_return_value = "returns" in command
explicit_parameters = promisified and has_return_value
if ("parameters" in command):
for in_param in command["parameters"]:
# All parameters are not optional in case of promisified domain with return value.
if (not explicit_parameters and "optional" in in_param):
params.append("opt_%s" % in_param["name"])
output_file.write(" * @param {%s=} opt_%s\n" % (param_type(domain_name, in_param), in_param["name"]))
else:
params.append(in_param["name"])
output_file.write(" * @param {%s} %s\n" % (param_type(domain_name, in_param), in_param["name"]))
returns = []
returns.append("?Protocol.Error")
if ("error" in command):
returns.append("%s=" % param_type(domain_name, command["error"]))
if (has_return_value):
for out_param in command["returns"]:
if ("optional" in out_param):
returns.append("%s=" % param_type(domain_name, out_param))
else:
returns.append("%s" % param_type(domain_name, out_param))
callback_return_type = "void="
if explicit_parameters:
callback_return_type = "T"
elif promisified:
callback_return_type = "T="
output_file.write(" * @param {function(%s):%s} opt_callback\n" % (", ".join(returns), callback_return_type))
if (promisified):
output_file.write(" * @return {!Promise.<T>}\n")
output_file.write(" * @template T\n")
params.append("opt_callback")
output_file.write(" */\n")
output_file.write("Protocol.%sAgent.prototype.%s = function(%s) {}\n" % (domain_name, command["name"], ", ".join(params)))
output_file.write("/** @param {function(%s):void=} opt_callback */\n" % ", ".join(returns))
output_file.write("Protocol.%sAgent.prototype.invoke_%s = function(obj, opt_callback) {}\n" % (domain_name, command["name"]))
output_file.write("\n\n\nvar %sAgent = function(){};\n" % domain_name)
if "types" in domain:
for type in domain["types"]:
if type["type"] == "object":
typedef_args = []
if "properties" in type:
for property in type["properties"]:
suffix = ""
if ("optional" in property):
suffix = "|undefined"
if "enum" in property:
enum_name = "%sAgent.%s%s" % (domain_name, type["id"], to_title_case(property["name"]))
output_file.write(generate_enum(enum_name, property))
typedef_args.append("%s:(%s%s)" % (property["name"], enum_name, suffix))
else:
typedef_args.append("%s:(%s%s)" % (property["name"], param_type(domain_name, property), suffix))
if (typedef_args):
output_file.write("\n/** @typedef {!{%s}} */\n%sAgent.%s;\n" % (", ".join(typedef_args), domain_name, type["id"]))
else:
output_file.write("\n/** @typedef {!Object} */\n%sAgent.%s;\n" % (domain_name, type["id"]))
elif type["type"] == "string" and "enum" in type:
output_file.write(generate_enum("%sAgent.%s" % (domain_name, type["id"]), type))
elif type["type"] == "array":
output_file.write("\n/** @typedef {!Array.<!%s>} */\n%sAgent.%s;\n" % (param_type(domain_name, type["items"]), domain_name, type["id"]))
else:
output_file.write("\n/** @typedef {%s} */\n%sAgent.%s;\n" % (type_traits[type["type"]], domain_name, type["id"]))
output_file.write("/** @interface */\n")
output_file.write("%sAgent.Dispatcher = function() {};\n" % domain_name)
if "events" in domain:
for event in domain["events"]:
params = []
if ("parameters" in event):
output_file.write("/**\n")
for param in event["parameters"]:
if ("optional" in param):
params.append("opt_%s" % param["name"])
output_file.write(" * @param {%s=} opt_%s\n" % (param_type(domain_name, param), param["name"]))
else:
params.append(param["name"])
output_file.write(" * @param {%s} %s\n" % (param_type(domain_name, param), param["name"]))
output_file.write(" */\n")
output_file.write("%sAgent.Dispatcher.prototype.%s = function(%s) {};\n" % (domain_name, event["name"], ", ".join(params)))
output_file.write("\n/** @constructor\n * @param {!Object.<string, !Object>} agentsMap\n */\n")
output_file.write("Protocol.Agents = function(agentsMap){this._agentsMap;};\n")
output_file.write("/**\n * @param {string} domain\n * @param {!Object} dispatcher\n */\n")
output_file.write("Protocol.Agents.prototype.registerDispatcher = function(domain, dispatcher){};\n")
for domain in domains:
domain_name = domain["domain"]
uppercase_length = 0
while uppercase_length < len(domain_name) and domain_name[uppercase_length].isupper():
uppercase_length += 1
output_file.write("/** @return {!Protocol.%sAgent}*/\n" % domain_name)
output_file.write("Protocol.Agents.prototype.%s = function(){};\n" % (domain_name[:uppercase_length].lower() + domain_name[uppercase_length:] + "Agent"))
output_file.write("/**\n * @param {!%sAgent.Dispatcher} dispatcher\n */\n" % domain_name)
output_file.write("Protocol.Agents.prototype.register%sDispatcher = function(dispatcher) {}\n" % domain_name)
output_file.close()
if __name__ == "__main__":
import sys
import os.path
program_name = os.path.basename(__file__)
if len(sys.argv) < 4 or sys.argv[1] != "-o":
sys.stderr.write("Usage: %s -o OUTPUT_FILE INPUT_FILE\n" % program_name)
exit(1)
output_path = sys.argv[2]
input_path = sys.argv[3]
generate_protocol_externs(output_path, input_path)
#!/usr/bin/env python
#
# Copyright 2011 - 2013
# Andr\xe9 Malo or his licensors, as applicable
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
r"""
=====================
Javascript Minifier
=====================
rJSmin is a javascript minifier written in python.
The minifier is based on the semantics of `jsmin.c by Douglas Crockford`_\.
The module is a re-implementation aiming for speed, so it can be used at
runtime (rather than during a preprocessing step). Usually it produces the
same results as the original ``jsmin.c``. It differs in the following ways:
- there is no error detection: unterminated string, regex and comment
literals are treated as regular javascript code and minified as such.
- Control characters inside string and regex literals are left untouched; they
are not converted to spaces (nor to \n)
- Newline characters are not allowed inside string and regex literals, except
for line continuations in string literals (ECMA-5).
- "return /regex/" is recognized correctly.
- "+ +" and "- -" sequences are not collapsed to '++' or '--'
- Newlines before ! operators are removed more sensibly
- rJSmin does not handle streams, but only complete strings. (However, the
module provides a "streamy" interface).
Since most parts of the logic are handled by the regex engine it's way
faster than the original python port of ``jsmin.c`` by Baruch Even. The speed
factor varies between about 6 and 55 depending on input and python version
(it gets faster the more compressed the input already is). Compared to the
speed-refactored python port by Dave St.Germain the performance gain is less
dramatic but still between 1.2 and 7. See the docs/BENCHMARKS file for
details.
rjsmin.c is a reimplementation of rjsmin.py in C and speeds it up even more.
Both python 2 and python 3 are supported.
.. _jsmin.c by Douglas Crockford:
http://www.crockford.com/javascript/jsmin.c
"""
__author__ = "Andr\xe9 Malo"
__author__ = getattr(__author__, 'decode', lambda x: __author__)('latin-1')
__docformat__ = "restructuredtext en"
__license__ = "Apache License, Version 2.0"
__version__ = '1.0.7'
__all__ = ['jsmin']
import re as _re
def _make_jsmin(python_only=False):
"""
Generate JS minifier based on `jsmin.c by Douglas Crockford`_
.. _jsmin.c by Douglas Crockford:
http://www.crockford.com/javascript/jsmin.c
:Parameters:
`python_only` : ``bool``
Use only the python variant. If true, the c extension is not even
tried to be loaded.
:Return: Minifier
:Rtype: ``callable``
"""
# pylint: disable = R0912, R0914, W0612
if not python_only:
try:
import _rjsmin
except ImportError:
pass
else:
return _rjsmin.jsmin
try:
xrange
except NameError:
xrange = range # pylint: disable = W0622
space_chars = r'[\000-\011\013\014\016-\040]'
line_comment = r'(?://[^\r\n]*)'
space_comment = r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)'
string1 = \
r'(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^\047\\\r\n]*)*\047)'
string2 = r'(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|\r)[^"\\\r\n]*)*")'
strings = r'(?:%s|%s)' % (string1, string2)
charclass = r'(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\])'
nospecial = r'[^/\\\[\r\n]'
regex = r'(?:/(?![\r\n/*])%s*(?:(?:\\[^\r\n]|%s)%s*)*/)' % (
nospecial, charclass, nospecial)
space = r'(?:%s|%s)' % (space_chars, space_comment)
newline = r'(?:%s?[\r\n])' % line_comment
def fix_charclass(result):
""" Fixup string of chars to fit into a regex char class """
pos = result.find('-')
if pos >= 0:
result = r'%s%s-' % (result[:pos], result[pos + 1:])
def sequentize(string):
"""
Notate consecutive characters as sequence
(1-4 instead of 1234)
"""
first, last, result = None, None, []
for char in map(ord, string):
if last is None:
first = last = char
elif last + 1 == char:
last = char
else:
result.append((first, last))
first = last = char
if last is not None:
result.append((first, last))
return ''.join(['%s%s%s' % (
chr(first),
last > first + 1 and '-' or '',
last != first and chr(last) or '') for first, last in result])
return _re.sub(r'([\000-\040\047])', # for better portability
lambda m: '\\%03o' % ord(m.group(1)), (sequentize(result)
.replace('\\', '\\\\')
.replace('[', '\\[')
.replace(']', '\\]')))
def id_literal_(what):
""" Make id_literal like char class """
match = _re.compile(what).match
result = ''.join([chr(c) for c in xrange(127) if not match(chr(c))])
return '[^%s]' % fix_charclass(result)
def not_id_literal_(keep):
""" Make negated id_literal like char class """
match = _re.compile(id_literal_(keep)).match
result = ''.join([chr(c) for c in xrange(127) if not match(chr(c))])
return r'[%s]' % fix_charclass(result)
not_id_literal = not_id_literal_(r'[a-zA-Z0-9_$]')
preregex1 = r'[(,=:\[!&|?{};\r\n]'
preregex2 = r'%(not_id_literal)sreturn' % locals()
id_literal = id_literal_(r'[a-zA-Z0-9_$]')
id_literal_open = id_literal_(r'[a-zA-Z0-9_${\[(!+-]')
id_literal_close = id_literal_(r'[a-zA-Z0-9_$}\])"\047+-]')
dull = r'[^\047"/\000-\040]'
space_sub = _re.compile((
r'(%(dull)s+)'
r'|(%(strings)s%(dull)s*)'
r'|(?<=%(preregex1)s)'
r'%(space)s*(?:%(newline)s%(space)s*)*'
r'(%(regex)s%(dull)s*)'
r'|(?<=%(preregex2)s)'
r'%(space)s*(?:%(newline)s%(space)s)*'
r'(%(regex)s%(dull)s*)'
r'|(?<=%(id_literal_close)s)'
r'%(space)s*(?:(%(newline)s)%(space)s*)+'
r'(?=%(id_literal_open)s)'
r'|(?<=%(id_literal)s)(%(space)s)+(?=%(id_literal)s)'
r'|(?<=\+)(%(space)s)+(?=\+)'
r'|(?<=-)(%(space)s)+(?=-)'
r'|%(space)s+'
r'|(?:%(newline)s%(space)s*)+') % locals()).sub
#print space_sub.__self__.pattern
def space_subber(match):
""" Substitution callback """
# pylint: disable = C0321, R0911
groups = match.groups()
if groups[0]:
return groups[0]
elif groups[1]:
return groups[1]
elif groups[2]:
return groups[2]
elif groups[3]:
return groups[3]
elif groups[4]:
return '\n'
elif groups[5] or groups[6] or groups[7]:
return ' '
else:
return ''
def jsmin(script): # pylint: disable = W0621
r"""
Minify javascript based on `jsmin.c by Douglas Crockford`_\.
Instead of parsing the stream char by char, it uses a regular
expression approach which minifies the whole script with one big
substitution regex.
.. _jsmin.c by Douglas Crockford:
http://www.crockford.com/javascript/jsmin.c
:Parameters:
`script` : ``str``
Script to minify
:Return: Minified script
:Rtype: ``str``
"""
return space_sub(space_subber, '\n%s\n' % script).strip()
return jsmin
jsmin = _make_jsmin()
def jsmin_for_posers(script):
r"""
Minify javascript based on `jsmin.c by Douglas Crockford`_\.
Instead of parsing the stream char by char, it uses a regular
expression approach which minifies the whole script with one big
substitution regex.
.. _jsmin.c by Douglas Crockford:
http://www.crockford.com/javascript/jsmin.c
:Warning: This function is the digest of a _make_jsmin() call. It just
utilizes the resulting regex. It's just for fun here and may
vanish any time. Use the `jsmin` function instead.
:Parameters:
`script` : ``str``
Script to minify
:Return: Minified script
:Rtype: ``str``
"""
def subber(match):
""" Substitution callback """
groups = match.groups()
return (
groups[0] or
groups[1] or
groups[2] or
groups[3] or
(groups[4] and '\n') or
(groups[5] and ' ') or
(groups[6] and ' ') or
(groups[7] and ' ') or
'')
return _re.sub(
r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]|\r?'
r'\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|'
r'\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|(?<=[(,=:\[!&|?{};\r\n])(?'
r':[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*'
r'(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*'
r'[^*]*\*+(?:[^/*][^*]*\*+)*/))*)*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:('
r'?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\['
r'\r\n]*)*/)[^\047"/\000-\040]*)|(?<=[\000-#%-,./:-@\[-^`{-~-]return'
r')(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/'
r'))*(?:(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:'
r'/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?'
r':(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/'
r'\\\[\r\n]*)*/)[^\047"/\000-\040]*)|(?<=[^\000-!#%&(*,./:-@\[\\^`{|'
r'~])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)'
r'*/))*(?:((?:(?://[^\r\n]*)?[\r\n]))(?:[\000-\011\013\014\016-\040]'
r'|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040"#%-\047)*,./'
r':-@\\-^`|-~])|(?<=[^\000-#%-,./:-@\[-^`{-~-])((?:[\000-\011\013\01'
r'4\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=[^\000-#%-,./:'
r'-@\[-^`{-~-])|(?<=\+)((?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*'
r'\*+(?:[^/*][^*]*\*+)*/)))+(?=\+)|(?<=-)((?:[\000-\011\013\014\016-'
r'\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=-)|(?:[\000-\011\013'
r'\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+|(?:(?:(?://[^'
r'\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^'
r'/*][^*]*\*+)*/))*)+', subber, '\n%s\n' % script).strip()
if __name__ == '__main__':
import sys as _sys
_sys.stdout.write(jsmin(_sys.stdin.read()))
# 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.
"""Represent a file as a C++ constant string.
Usage:
python xxd.py VAR SOURCE DEST
"""
import sys
import rjsmin
def main():
variable_name, input_filename, output_filename = sys.argv[1:]
with open(input_filename) as input_file:
input_text = input_file.read()
input_text = rjsmin.jsmin(input_text)
hex_values = ['0x{0:02x}'.format(ord(char)) for char in input_text]
const_declaration = 'const char %s[] = {\n%s\n};\n' % (
variable_name, ', '.join(hex_values))
with open(output_filename, 'w') as output_file:
output_file.write(const_declaration)
if __name__ == '__main__':
sys.exit(main())
......@@ -113,7 +113,7 @@ bool substituteObjectTags(int sessionId, const String16& groupName,
}
std::unique_ptr<protocol::Runtime::RemoteObject> wrapper;
protocol::Response response =
injectedScript->wrapObject(originValue, groupName, false, false,
injectedScript->wrapObject(originValue, groupName, WrapMode::kNoPreview,
configValue, maxDepth - 1, &wrapper);
if (!response.isSuccess() || !wrapper) {
reportError(context, tryCatch, "cannot wrap value");
......@@ -379,13 +379,8 @@ void generateCustomPreview(int sessionId, const String16& groupName,
reportError(context, tryCatch, "cannot find context with specified id");
return;
}
(*preview)->setBodyGetterId(String16::concat(
"{\"injectedScriptId\":",
String16::fromInteger(InspectedContext::contextId(context)),
",\"id\":",
String16::fromInteger(
injectedScript->bindObject(bodyFunction, groupName)),
"}"));
(*preview)->setBodyGetterId(
injectedScript->bindObject(bodyFunction, groupName));
}
return;
}
......
/*
* Copyright (C) 2007 Apple Inc. All rights reserved.
* Copyright (C) 2013 Google Inc. 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 Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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.
*/
"use strict";
/**
* @param {!InjectedScriptHostClass} InjectedScriptHost
* @param {!Window|!WorkerGlobalScope} inspectedGlobalObject
* @param {number} injectedScriptId
* @suppress {uselessCode}
*/
(function (InjectedScriptHost, inspectedGlobalObject, injectedScriptId) {
/**
* @param {!Array.<T>} array
* @param {...} var_args
* @template T
*/
function push(array, var_args)
{
for (var i = 1; i < arguments.length; ++i)
array[array.length] = arguments[i];
}
/**
* @param {*} obj
* @return {string}
* @suppress {uselessCode}
*/
function toString(obj)
{
// We don't use String(obj) because String could be overridden.
// Also the ("" + obj) expression may throw.
try {
return "" + obj;
} catch (e) {
var name = InjectedScriptHost.internalConstructorName(obj) || InjectedScriptHost.subtype(obj) || (typeof obj);
return "#<" + name + ">";
}
}
/**
* TODO(luoe): remove type-check suppression once bigint is supported by closure.
* @suppress {checkTypes}
* @param {*} obj
* @return {string}
*/
function toStringDescription(obj)
{
if (typeof obj === "number" && obj === 0 && 1 / obj < 0)
return "-0"; // Negative zero.
if (typeof obj === "bigint")
return toString(obj) + "n";
return toString(obj);
}
/**
* FireBug's array detection.
* @param {*} obj
* @return {boolean}
*/
function isArrayLike(obj)
{
if (typeof obj !== "object")
return false;
var splice = InjectedScriptHost.getProperty(obj, "splice");
if (typeof splice === "function") {
if (!InjectedScriptHost.objectHasOwnProperty(/** @type {!Object} */ (obj), "length"))
return false;
var len = InjectedScriptHost.getProperty(obj, "length");
// is len uint32?
return typeof len === "number" && len >>> 0 === len && (len > 0 || 1 / len > 0);
}
return false;
}
/**
* @param {number} a
* @param {number} b
* @return {number}
*/
function max(a, b)
{
return a > b ? a : b;
}
/**
* FIXME: Remove once ES6 is supported natively by JS compiler.
* @param {*} obj
* @return {boolean}
*/
function isSymbol(obj)
{
var type = typeof obj;
return (type === "symbol");
}
/**
* DOM Attributes which have observable side effect on getter, in the form of
* {interfaceName1: {attributeName1: true,
* attributeName2: true,
* ...},
* interfaceName2: {...},
* ...}
* @type {!Object<string, !Object<string, boolean>>}
* @const
*/
var domAttributesWithObservableSideEffectOnGet = {
Request: { body: true, __proto__: null },
Response: { body: true, __proto__: null },
__proto__: null
}
/**
* @param {!Object} object
* @param {string} attribute
* @return {boolean}
*/
function doesAttributeHaveObservableSideEffectOnGet(object, attribute)
{
for (var interfaceName in domAttributesWithObservableSideEffectOnGet) {
var interfaceFunction = inspectedGlobalObject[interfaceName];
// Call to instanceOf looks safe after typeof check.
var isInstance = typeof interfaceFunction === "function" && /* suppressBlacklist */ object instanceof interfaceFunction;
if (isInstance)
return attribute in domAttributesWithObservableSideEffectOnGet[interfaceName];
}
return false;
}
/**
* @constructor
*/
var InjectedScript = function()
{
}
InjectedScriptHost.nullifyPrototype(InjectedScript);
/**
* @type {!Object<string, boolean>}
* @const
*/
InjectedScript.primitiveTypes = {
"undefined": true,
"boolean": true,
"number": true,
"string": true,
"bigint": true,
__proto__: null
}
/**
* @type {!Object<string, string>}
* @const
*/
InjectedScript.closureTypes = {
"local": "Local",
"closure": "Closure",
"catch": "Catch",
"block": "Block",
"script": "Script",
"with": "With Block",
"global": "Global",
"eval": "Eval",
"module": "Module",
__proto__: null
};
InjectedScript.prototype = {
/**
* @param {*} object
* @return {boolean}
*/
isPrimitiveValue: function(object)
{
// FIXME(33716): typeof document.all is always 'undefined'.
return InjectedScript.primitiveTypes[typeof object] && !this._isHTMLAllCollection(object);
},
/**
* @param {*} object
* @return {boolean}
*/
_shouldPassByValue: function(object)
{
return typeof object === "object" && InjectedScriptHost.subtype(object) === "internal#location";
},
/**
* @param {*} object
* @param {string} groupName
* @param {boolean} forceValueType
* @param {boolean} generatePreview
* @return {!RuntimeAgent.RemoteObject}
*/
wrapObject: function(object, groupName, forceValueType, generatePreview)
{
return this._wrapObject(object, groupName, forceValueType, generatePreview);
},
/**
* @param {!Object} table
* @param {!Array.<string>|string|boolean} columns
* @return {!RuntimeAgent.RemoteObject}
*/
wrapTable: function(table, columns)
{
var columnNames = null;
if (typeof columns === "string")
columns = [columns];
if (InjectedScriptHost.subtype(columns) === "array") {
columnNames = [];
InjectedScriptHost.nullifyPrototype(columnNames);
for (var i = 0; i < columns.length; ++i)
columnNames[i] = toString(columns[i]);
}
return this._wrapObject(table, "console", false, true, columnNames, true);
},
/**
* This method cannot throw.
* @param {*} object
* @param {string=} objectGroupName
* @param {boolean=} forceValueType
* @param {boolean=} generatePreview
* @param {?Array.<string>=} columnNames
* @param {boolean=} isTable
* @param {boolean=} doNotBind
* @return {!RuntimeAgent.RemoteObject}
* @suppress {checkTypes}
*/
_wrapObject: function(object, objectGroupName, forceValueType, generatePreview, columnNames, isTable, doNotBind)
{
try {
return new InjectedScript.RemoteObject(object, objectGroupName, doNotBind, forceValueType, generatePreview, columnNames, isTable, undefined);
} catch (e) {
try {
var description = injectedScript._describe(e);
} catch (ex) {
var description = "<failed to convert exception to string>";
}
return new InjectedScript.RemoteObject(description);
}
},
/**
* @param {!Object|symbol} object
* @param {string=} objectGroupName
* @return {string}
*/
_bind: function(object, objectGroupName)
{
var id = InjectedScriptHost.bind(object, objectGroupName || "");
return "{\"injectedScriptId\":" + injectedScriptId + ",\"id\":" + id + "}";
},
/**
* @param {!Object} object
* @param {string} objectGroupName
* @param {boolean} ownProperties
* @param {boolean} accessorPropertiesOnly
* @param {boolean} generatePreview
* @return {!Array<!RuntimeAgent.PropertyDescriptor>|boolean}
*/
getProperties: function(object, objectGroupName, ownProperties, accessorPropertiesOnly, generatePreview)
{
var subtype = this._subtype(object);
if (subtype === "internal#scope") {
// Internally, scope contains object with scope variables and additional information like type,
// we use additional information for preview and would like to report variables as scope
// properties.
object = object.object;
}
// Go over properties, wrap object values.
var descriptors = this._propertyDescriptors(object, addPropertyIfNeeded, ownProperties, accessorPropertiesOnly);
for (var i = 0; i < descriptors.length; ++i) {
var descriptor = descriptors[i];
if ("get" in descriptor)
descriptor.get = this._wrapObject(descriptor.get, objectGroupName);
if ("set" in descriptor)
descriptor.set = this._wrapObject(descriptor.set, objectGroupName);
if ("value" in descriptor)
descriptor.value = this._wrapObject(descriptor.value, objectGroupName, false, generatePreview);
if (!("configurable" in descriptor))
descriptor.configurable = false;
if (!("enumerable" in descriptor))
descriptor.enumerable = false;
if ("symbol" in descriptor)
descriptor.symbol = this._wrapObject(descriptor.symbol, objectGroupName);
}
return descriptors;
/**
* @param {!Array<!Object>} descriptors
* @param {!Object} descriptor
* @return {boolean}
*/
function addPropertyIfNeeded(descriptors, descriptor) {
push(descriptors, descriptor);
return true;
}
},
/**
* @param {!Object} object
* @return {?Object}
*/
_objectPrototype: function(object)
{
if (InjectedScriptHost.subtype(object) === "proxy")
return null;
try {
return InjectedScriptHost.getPrototypeOf(object);
} catch (e) {
return null;
}
},
/**
* @param {!Object} object
* @param {!function(!Array<!Object>, !Object)} addPropertyIfNeeded
* @param {boolean=} ownProperties
* @param {boolean=} accessorPropertiesOnly
* @param {?Array<string>=} propertyNamesOnly
* @return {!Array<!Object>}
*/
_propertyDescriptors: function(object, addPropertyIfNeeded, ownProperties, accessorPropertiesOnly, propertyNamesOnly)
{
var descriptors = [];
InjectedScriptHost.nullifyPrototype(descriptors);
var propertyProcessed = { __proto__: null };
var subtype = InjectedScriptHost.subtype(object);
/**
* @param {!Object} o
* @param {!Array<string|number|symbol>=} properties
* @param {number=} objectLength
* @return {boolean}
*/
function process(o, properties, objectLength)
{
// When properties is not provided, iterate over the object's indices.
var length = properties ? properties.length : objectLength;
for (var i = 0; i < length; ++i) {
var property = properties ? properties[i] : ("" + i);
if (propertyProcessed[property])
continue;
propertyProcessed[property] = true;
var name;
if (isSymbol(property))
name = /** @type {string} */ (injectedScript._describe(property));
else
name = typeof property === "number" ? ("" + property) : /** @type {string} */(property);
if (subtype === "internal#scopeList" && name === "length")
continue;
var descriptor;
try {
var nativeAccessorDescriptor = InjectedScriptHost.nativeAccessorDescriptor(o, property);
if (nativeAccessorDescriptor && !nativeAccessorDescriptor.isBuiltin) {
descriptor = { __proto__: null };
if (nativeAccessorDescriptor.hasGetter)
descriptor.get = function nativeGetter() { return o[property]; };
if (nativeAccessorDescriptor.hasSetter)
descriptor.set = function nativeSetter(v) { o[property] = v; };
} else {
descriptor = InjectedScriptHost.getOwnPropertyDescriptor(o, property);
if (descriptor) {
InjectedScriptHost.nullifyPrototype(descriptor);
}
}
var isAccessorProperty = descriptor && ("get" in descriptor || "set" in descriptor);
if (accessorPropertiesOnly && !isAccessorProperty)
continue;
// Special case for Symbol.prototype.description where the receiver of the getter is not an actual object.
// Should only occur for nested previews.
var isSymbolDescription = isSymbol(object) && name === 'description';
if (isSymbolDescription || (descriptor && "get" in descriptor && "set" in descriptor && name !== "__proto__" &&
InjectedScriptHost.formatAccessorsAsProperties(object, descriptor.get) &&
!doesAttributeHaveObservableSideEffectOnGet(object, name))) {
descriptor.value = object[property];
descriptor.isOwn = true;
delete descriptor.get;
delete descriptor.set;
}
} catch (e) {
if (accessorPropertiesOnly)
continue;
descriptor = { value: e, wasThrown: true, __proto__: null };
}
// Not all bindings provide proper descriptors. Fall back to the non-configurable, non-enumerable,
// non-writable property.
if (!descriptor) {
try {
descriptor = { value: o[property], writable: false, __proto__: null };
} catch (e) {
// Silent catch.
continue;
}
}
descriptor.name = name;
if (o === object)
descriptor.isOwn = true;
if (isSymbol(property))
descriptor.symbol = property;
if (!addPropertyIfNeeded(descriptors, descriptor))
return false;
}
return true;
}
if (propertyNamesOnly) {
for (var i = 0; i < propertyNamesOnly.length; ++i) {
var name = propertyNamesOnly[i];
for (var o = object; this._isDefined(o); o = this._objectPrototype(/** @type {!Object} */ (o))) {
o = /** @type {!Object} */ (o);
if (InjectedScriptHost.objectHasOwnProperty(o, name)) {
if (!process(o, [name]))
return descriptors;
break;
}
if (ownProperties)
break;
}
}
return descriptors;
}
var skipGetOwnPropertyNames;
try {
skipGetOwnPropertyNames = subtype === "typedarray" && object.length > 500000;
} catch (e) {
}
for (var o = object; this._isDefined(o); o = this._objectPrototype(/** @type {!Object} */ (o))) {
o = /** @type {!Object} */ (o);
if (InjectedScriptHost.subtype(o) === "proxy")
continue;
var typedArrays = subtype === "arraybuffer" ? InjectedScriptHost.typedArrayProperties(o) || [] : [];
for (var i = 0; i < typedArrays.length; i += 2)
addPropertyIfNeeded(descriptors, { name: typedArrays[i], value: typedArrays[i + 1], isOwn: true, enumerable: false, configurable: false, __proto__: null });
try {
if (skipGetOwnPropertyNames && o === object) {
if (!process(o, undefined, o.length))
return descriptors;
} else {
// First call Object.keys() to enforce ordering of the property descriptors.
if (!process(o, InjectedScriptHost.keys(o)))
return descriptors;
if (!process(o, InjectedScriptHost.getOwnPropertyNames(o)))
return descriptors;
}
if (!process(o, InjectedScriptHost.getOwnPropertySymbols(o)))
return descriptors;
if (ownProperties) {
var proto = this._objectPrototype(o);
if (proto && !accessorPropertiesOnly) {
var descriptor = { name: "__proto__", value: proto, writable: true, configurable: true, enumerable: false, isOwn: true, __proto__: null };
if (!addPropertyIfNeeded(descriptors, descriptor))
return descriptors;
}
}
} catch (e) {
}
if (ownProperties)
break;
}
return descriptors;
},
/**
* @param {*} object
* @return {boolean}
*/
_isDefined: function(object)
{
return !!object || this._isHTMLAllCollection(object);
},
/**
* @param {*} object
* @return {boolean}
*/
_isHTMLAllCollection: function(object)
{
// document.all is reported as undefined, but we still want to process it.
return (typeof object === "undefined") && !!InjectedScriptHost.subtype(object);
},
/**
* @param {*} obj
* @return {?string}
*/
_subtype: function(obj)
{
if (obj === null)
return "null";
if (this.isPrimitiveValue(obj))
return null;
var subtype = InjectedScriptHost.subtype(obj);
if (subtype)
return subtype;
if (isArrayLike(obj))
return "array";
// If owning frame has navigated to somewhere else window properties will be undefined.
return null;
},
/**
* @param {*} obj
* @return {?string}
*/
_describe: function(obj)
{
if (this.isPrimitiveValue(obj))
return null;
var subtype = this._subtype(obj);
if (subtype === "regexp")
return toString(obj);
if (subtype === "date")
return toString(obj);
if (subtype === "node") {
// We should warmup blink dom binding before calling anything,
// see (crbug.com/827585) for details.
InjectedScriptHost.getOwnPropertyDescriptor(/** @type {!Object} */(obj), "nodeName");
var description = "";
var nodeName = InjectedScriptHost.getProperty(obj, "nodeName");
if (nodeName) {
description = nodeName.toLowerCase();
} else {
var constructor = InjectedScriptHost.getProperty(obj, "constructor");
if (constructor)
description = (InjectedScriptHost.getProperty(constructor, "name") || "").toLowerCase();
}
var nodeType = InjectedScriptHost.getProperty(obj, "nodeType");
switch (nodeType) {
case 1 /* Node.ELEMENT_NODE */:
var id = InjectedScriptHost.getProperty(obj, "id");
description += id ? "#" + id : "";
var className = InjectedScriptHost.getProperty(obj, "className");
description += (className && typeof className === "string") ? "." + className.trim().replace(/\s+/g, ".") : "";
break;
case 10 /*Node.DOCUMENT_TYPE_NODE */:
description = "<!DOCTYPE " + description + ">";
break;
}
return description;
}
if (subtype === "proxy")
return "Proxy";
var className = InjectedScriptHost.internalConstructorName(obj);
if (subtype === "array" || subtype === "typedarray") {
if (typeof obj.length === "number")
return className + "(" + obj.length + ")";
return className;
}
if (subtype === "map" || subtype === "set" || subtype === "blob") {
if (typeof obj.size === "number")
return className + "(" + obj.size + ")";
return className;
}
if (subtype === "arraybuffer" || subtype === "dataview") {
if (typeof obj.byteLength === "number")
return className + "(" + obj.byteLength + ")";
return className;
}
if (typeof obj === "function")
return toString(obj);
if (isSymbol(obj)) {
try {
// It isn't safe, because Symbol.prototype.toString can be overriden.
return /* suppressBlacklist */ obj.toString() || "Symbol";
} catch (e) {
return "Symbol";
}
}
if (InjectedScriptHost.subtype(obj) === "error") {
try {
const stack = obj.stack;
if (stack.substr(0, className.length) === className)
return stack;
const message = obj.message;
const index = /* suppressBlacklist */ stack.indexOf(message);
const messageWithStack = index !== -1 ? stack.substr(index) : message;
return className + ': ' + messageWithStack;
} catch(e) {
return className;
}
}
if (subtype === "internal#entry") {
if ("key" in obj)
return "{" + this._describeIncludingPrimitives(obj.key) + " => " + this._describeIncludingPrimitives(obj.value) + "}";
return this._describeIncludingPrimitives(obj.value);
}
if (subtype === "internal#scopeList")
return "Scopes[" + obj.length + "]";
if (subtype === "internal#scope")
return (InjectedScript.closureTypes[obj.type] || "Unknown") + (obj.name ? " (" + obj.name + ")" : "");
return className;
},
/**
* @param {*} value
* @return {string}
*/
_describeIncludingPrimitives: function(value)
{
if (typeof value === "string")
return "\"" + value.replace(/\n/g, "\u21B5") + "\"";
if (value === null)
return "" + value;
return this.isPrimitiveValue(value) ? toStringDescription(value) : (this._describe(value) || "");
}
}
/**
* @type {!InjectedScript}
* @const
*/
var injectedScript = new InjectedScript();
/**
* @constructor
* @param {*} object
* @param {string=} objectGroupName
* @param {boolean=} doNotBind
* @param {boolean=} forceValueType
* @param {boolean=} generatePreview
* @param {?Array.<string>=} columnNames
* @param {boolean=} isTable
* @param {boolean=} skipEntriesPreview
*/
InjectedScript.RemoteObject = function(object, objectGroupName, doNotBind, forceValueType, generatePreview, columnNames, isTable, skipEntriesPreview)
{
this.type = typeof object;
if (this.type === "undefined" && injectedScript._isHTMLAllCollection(object))
this.type = "object";
if (injectedScript.isPrimitiveValue(object) || object === null || forceValueType) {
// We don't send undefined values over JSON.
if (this.type !== "undefined")
this.value = object;
// Null object is object with 'null' subtype.
if (object === null)
this.subtype = "null";
// Provide user-friendly number values.
if (this.type === "number") {
this.description = toStringDescription(object);
switch (this.description) {
case "NaN":
case "Infinity":
case "-Infinity":
case "-0":
delete this.value;
this.unserializableValue = this.description;
break;
}
}
// The "n" suffix of bigint primitives are not JSON serializable.
if (this.type === "bigint") {
delete this.value;
this.description = toStringDescription(object);
this.unserializableValue = this.description;
}
return;
}
if (injectedScript._shouldPassByValue(object)) {
this.value = object;
this.subtype = injectedScript._subtype(object);
this.description = injectedScript._describeIncludingPrimitives(object);
return;
}
object = /** @type {!Object} */ (object);
if (!doNotBind)
this.objectId = injectedScript._bind(object, objectGroupName);
var subtype = injectedScript._subtype(object);
if (subtype)
this.subtype = subtype;
var className = InjectedScriptHost.internalConstructorName(object);
if (className)
this.className = className;
this.description = injectedScript._describe(object);
if (generatePreview && this.type === "object") {
if (this.subtype === "proxy")
this.preview = this._generatePreview(InjectedScriptHost.proxyTargetValue(object), undefined, columnNames, isTable, skipEntriesPreview);
else
this.preview = this._generatePreview(object, undefined, columnNames, isTable, skipEntriesPreview);
}
}
InjectedScript.RemoteObject.prototype = {
/**
* @return {!RuntimeAgent.ObjectPreview} preview
*/
_createEmptyPreview: function()
{
var preview = {
type: /** @type {!RuntimeAgent.ObjectPreviewType.<string>} */ (this.type),
description: this.description || toStringDescription(this.value),
overflow: false,
properties: [],
__proto__: null
};
InjectedScriptHost.nullifyPrototype(preview.properties);
if (this.subtype)
preview.subtype = /** @type {!RuntimeAgent.ObjectPreviewSubtype.<string>} */ (this.subtype);
return preview;
},
/**
* @param {!Object} object
* @param {?Array.<string>=} firstLevelKeys
* @param {?Array.<string>=} secondLevelKeys
* @param {boolean=} isTable
* @param {boolean=} skipEntriesPreview
* @return {!RuntimeAgent.ObjectPreview} preview
*/
_generatePreview: function(object, firstLevelKeys, secondLevelKeys, isTable, skipEntriesPreview)
{
var preview = this._createEmptyPreview();
var firstLevelKeysCount = firstLevelKeys ? firstLevelKeys.length : 0;
var propertiesThreshold = {
properties: isTable ? 1000 : max(5, firstLevelKeysCount),
indexes: isTable ? 1000 : max(100, firstLevelKeysCount),
__proto__: null
};
var subtype = this.subtype;
var primitiveString;
try {
var descriptors = [];
InjectedScriptHost.nullifyPrototype(descriptors);
// Add internal properties to preview.
var rawInternalProperties = InjectedScriptHost.getInternalProperties(object) || [];
var internalProperties = [];
InjectedScriptHost.nullifyPrototype(rawInternalProperties);
InjectedScriptHost.nullifyPrototype(internalProperties);
var entries = null;
for (var i = 0; i < rawInternalProperties.length; i += 2) {
if (rawInternalProperties[i] === "[[Entries]]") {
entries = /** @type {!Array<*>} */(rawInternalProperties[i + 1]);
continue;
}
if (rawInternalProperties[i] === "[[PrimitiveValue]]" && typeof rawInternalProperties[i + 1] === 'string')
primitiveString = rawInternalProperties[i + 1];
var internalPropertyDescriptor = {
name: rawInternalProperties[i],
value: rawInternalProperties[i + 1],
isOwn: true,
enumerable: true,
__proto__: null
};
push(descriptors, internalPropertyDescriptor);
}
var naturalDescriptors = injectedScript._propertyDescriptors(object, addPropertyIfNeeded, false /* ownProperties */, undefined /* accessorPropertiesOnly */, firstLevelKeys);
for (var i = 0; i < naturalDescriptors.length; i++)
push(descriptors, naturalDescriptors[i]);
this._appendPropertyPreviewDescriptors(preview, descriptors, secondLevelKeys, isTable);
if (subtype === "map" || subtype === "set" || subtype === "weakmap" || subtype === "weakset" || subtype === "iterator")
this._appendEntriesPreview(entries, preview, skipEntriesPreview);
} catch (e) {}
return preview;
/**
* @param {!Array<!Object>} descriptors
* @param {!Object} descriptor
* @return {boolean}
*/
function addPropertyIfNeeded(descriptors, descriptor) {
if (descriptor.wasThrown)
return true;
// Ignore __proto__ property.
if (descriptor.name === "__proto__")
return true;
// Ignore length property of array.
if ((subtype === "array" || subtype === "typedarray") && descriptor.name === "length")
return true;
// Ignore size property of map, set.
if ((subtype === "map" || subtype === "set") && descriptor.name === "size")
return true;
// Ignore ArrayBuffer previews
if (subtype === 'arraybuffer' && (descriptor.name === "[[Int8Array]]" || descriptor.name === "[[Uint8Array]]" || descriptor.name === "[[Int16Array]]" || descriptor.name === "[[Int32Array]]"))
return true;
// Never preview prototype properties.
if (!descriptor.isOwn)
return true;
// Ignore computed properties unless they have getters.
if (!("value" in descriptor) && !descriptor.get)
return true;
// Ignore index properties when there is a primitive string.
if (primitiveString && primitiveString[descriptor.name] === descriptor.value)
return true;
if (toString(descriptor.name >>> 0) === descriptor.name)
propertiesThreshold.indexes--;
else
propertiesThreshold.properties--;
var canContinue = propertiesThreshold.indexes >= 0 && propertiesThreshold.properties >= 0;
if (!canContinue) {
preview.overflow = true;
return false;
}
push(descriptors, descriptor);
return true;
}
},
/**
* @param {!RuntimeAgent.ObjectPreview} preview
* @param {!Array.<*>|!Iterable.<*>} descriptors
* @param {?Array.<string>=} secondLevelKeys
* @param {boolean=} isTable
*/
_appendPropertyPreviewDescriptors: function(preview, descriptors, secondLevelKeys, isTable)
{
for (var i = 0; i < descriptors.length; ++i) {
var descriptor = descriptors[i];
var name = descriptor.name;
var value = descriptor.value;
var type = typeof value;
// Special-case HTMLAll.
if (type === "undefined" && injectedScript._isHTMLAllCollection(value))
type = "object";
// Ignore computed properties unless they have getters.
if (descriptor.get && !("value" in descriptor)) {
push(preview.properties, { name: name, type: "accessor", __proto__: null });
continue;
}
// Render own properties.
if (value === null) {
push(preview.properties, { name: name, type: "object", subtype: "null", value: "null", __proto__: null });
continue;
}
var maxLength = 100;
if (InjectedScript.primitiveTypes[type]) {
var valueString = type === "string" ? value : toStringDescription(value);
if (valueString.length > maxLength)
valueString = this._abbreviateString(valueString, maxLength, true);
push(preview.properties, { name: name, type: type, value: valueString, __proto__: null });
continue;
}
var property = { name: name, type: type, __proto__: null };
var subtype = injectedScript._subtype(value);
if (subtype)
property.subtype = subtype;
if (secondLevelKeys === null || secondLevelKeys) {
var subPreview = this._generatePreview(value, secondLevelKeys || undefined, undefined, isTable);
property.valuePreview = subPreview;
if (subPreview.overflow)
preview.overflow = true;
} else {
var description = "";
if (type !== "function")
description = this._abbreviateString(/** @type {string} */ (injectedScript._describe(value)), maxLength, subtype === "regexp");
property.value = description;
}
push(preview.properties, property);
}
},
/**
* @param {?Array<*>} entries
* @param {!RuntimeAgent.ObjectPreview} preview
* @param {boolean=} skipEntriesPreview
*/
_appendEntriesPreview: function(entries, preview, skipEntriesPreview)
{
if (!entries)
return;
if (skipEntriesPreview) {
if (entries.length)
preview.overflow = true;
return;
}
preview.entries = [];
InjectedScriptHost.nullifyPrototype(preview.entries);
var entriesThreshold = 5;
for (var i = 0; i < entries.length; ++i) {
if (preview.entries.length >= entriesThreshold) {
preview.overflow = true;
break;
}
var entry = entries[i];
InjectedScriptHost.nullifyPrototype(entry);
var previewEntry = {
value: generateValuePreview(entry.value),
__proto__: null
};
if ("key" in entry)
previewEntry.key = generateValuePreview(entry.key);
push(preview.entries, previewEntry);
}
/**
* @param {*} value
* @return {!RuntimeAgent.ObjectPreview}
*/
function generateValuePreview(value)
{
var remoteObject = new InjectedScript.RemoteObject(value, undefined, true, undefined, true, undefined, undefined, true);
var valuePreview = remoteObject.preview || remoteObject._createEmptyPreview();
return valuePreview;
}
},
/**
* @param {string} string
* @param {number} maxLength
* @param {boolean=} middle
* @return {string}
*/
_abbreviateString: function(string, maxLength, middle)
{
if (string.length <= maxLength)
return string;
if (middle) {
var leftHalf = maxLength >> 1;
var rightHalf = maxLength - leftHalf - 1;
return string.substr(0, leftHalf) + "\u2026" + string.substr(string.length - rightHalf, rightHalf);
}
return string.substr(0, maxLength) + "\u2026";
},
__proto__: null
}
return injectedScript;
})
......@@ -30,26 +30,26 @@
#include "src/inspector/injected-script.h"
#include <cmath>
#include <unordered_set>
#include "src/inspector/custom-preview.h"
#include "src/inspector/injected-script-source.h"
#include "src/inspector/inspected-context.h"
#include "src/inspector/protocol/Protocol.h"
#include "src/inspector/remote-object-id.h"
#include "src/inspector/string-util.h"
#include "src/inspector/v8-console.h"
#include "src/inspector/v8-function-call.h"
#include "src/inspector/v8-injected-script-host.h"
#include "src/inspector/v8-inspector-impl.h"
#include "src/inspector/v8-inspector-session-impl.h"
#include "src/inspector/v8-stack-trace-impl.h"
#include "src/inspector/v8-value-utils.h"
#include "src/inspector/value-mirror.h"
#include "include/v8-inspector.h"
namespace v8_inspector {
namespace {
static const char privateKeyName[] = "v8-inspector#injectedScript";
static const char kGlobalHandleLabel[] = "DevTools console";
static bool isResolvableNumberLike(String16 query) {
return query == "Infinity" || query == "-Infinity" || query == "NaN";
......@@ -67,8 +67,7 @@ class InjectedScript::ProtocolPromiseHandler {
static bool add(V8InspectorSessionImpl* session,
v8::Local<v8::Context> context, v8::Local<v8::Value> value,
int executionContextId, const String16& objectGroup,
bool returnByValue, bool generatePreview,
EvaluateCallback* callback) {
WrapMode wrapMode, EvaluateCallback* callback) {
v8::Local<v8::Promise::Resolver> resolver;
if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) {
callback->sendFailure(Response::InternalError());
......@@ -81,9 +80,8 @@ class InjectedScript::ProtocolPromiseHandler {
v8::Local<v8::Promise> promise = resolver->GetPromise();
V8InspectorImpl* inspector = session->inspector();
ProtocolPromiseHandler* handler =
new ProtocolPromiseHandler(session, executionContextId, objectGroup,
returnByValue, generatePreview, callback);
ProtocolPromiseHandler* handler = new ProtocolPromiseHandler(
session, executionContextId, objectGroup, wrapMode, callback);
v8::Local<v8::Value> wrapper = handler->m_wrapper.Get(inspector->isolate());
v8::Local<v8::Function> thenCallbackFunction =
v8::Function::New(context, thenCallback, wrapper, 0,
......@@ -131,15 +129,13 @@ class InjectedScript::ProtocolPromiseHandler {
ProtocolPromiseHandler(V8InspectorSessionImpl* session,
int executionContextId, const String16& objectGroup,
bool returnByValue, bool generatePreview,
EvaluateCallback* callback)
WrapMode wrapMode, EvaluateCallback* callback)
: m_inspector(session->inspector()),
m_sessionId(session->sessionId()),
m_contextGroupId(session->contextGroupId()),
m_executionContextId(executionContextId),
m_objectGroup(objectGroup),
m_returnByValue(returnByValue),
m_generatePreview(generatePreview),
m_wrapMode(wrapMode),
m_callback(std::move(callback)),
m_wrapper(m_inspector->isolate(),
v8::External::New(m_inspector->isolate(), this)) {
......@@ -171,9 +167,8 @@ class InjectedScript::ProtocolPromiseHandler {
scope.injectedScript()->takeEvaluateCallback(m_callback);
if (!callback) return;
std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue;
response = scope.injectedScript()->wrapObject(
result, m_objectGroup, m_returnByValue, m_generatePreview,
&wrappedValue);
response = scope.injectedScript()->wrapObject(result, m_objectGroup,
m_wrapMode, &wrappedValue);
if (!response.isSuccess()) {
callback->sendFailure(response);
return;
......@@ -193,9 +188,8 @@ class InjectedScript::ProtocolPromiseHandler {
scope.injectedScript()->takeEvaluateCallback(m_callback);
if (!callback) return;
std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue;
response = scope.injectedScript()->wrapObject(
result, m_objectGroup, m_returnByValue, m_generatePreview,
&wrappedValue);
response = scope.injectedScript()->wrapObject(result, m_objectGroup,
m_wrapMode, &wrappedValue);
if (!response.isSuccess()) {
callback->sendFailure(response);
return;
......@@ -253,116 +247,142 @@ class InjectedScript::ProtocolPromiseHandler {
int m_contextGroupId;
int m_executionContextId;
String16 m_objectGroup;
bool m_returnByValue;
bool m_generatePreview;
WrapMode m_wrapMode;
EvaluateCallback* m_callback;
v8::Global<v8::External> m_wrapper;
};
std::unique_ptr<InjectedScript> InjectedScript::create(
InspectedContext* inspectedContext, int sessionId) {
v8::Isolate* isolate = inspectedContext->isolate();
v8::HandleScope handles(isolate);
v8::TryCatch tryCatch(isolate);
v8::Local<v8::Context> context = inspectedContext->context();
v8::debug::PostponeInterruptsScope postponeInterrupts(isolate);
v8::Context::Scope scope(context);
v8::MicrotasksScope microtasksScope(isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
// Inject javascript into the context. The compiled script is supposed to
// evaluate into
// a single anonymous function(it's anonymous to avoid cluttering the global
// object with
// inspector's stuff) the function is called a few lines below with
// InjectedScriptHost wrapper,
// injected script id and explicit reference to the inspected global object.
// The function is expected
// to create and configure InjectedScript instance that is going to be used by
// the inspector.
StringView injectedScriptSource(
reinterpret_cast<const uint8_t*>(InjectedScriptSource_js),
sizeof(InjectedScriptSource_js));
v8::Local<v8::Value> value;
if (!inspectedContext->inspector()
->compileAndRunInternalScript(
context, toV8String(isolate, injectedScriptSource))
.ToLocal(&value)) {
return nullptr;
}
DCHECK(value->IsFunction());
v8::Local<v8::Object> scriptHostWrapper =
V8InjectedScriptHost::create(context, inspectedContext->inspector());
v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(value);
v8::Local<v8::Object> windowGlobal = context->Global();
v8::Local<v8::Value> info[] = {
scriptHostWrapper, windowGlobal,
v8::Number::New(isolate, inspectedContext->contextId())};
int contextGroupId = inspectedContext->contextGroupId();
int contextId = inspectedContext->contextId();
V8InspectorImpl* inspector = inspectedContext->inspector();
v8::Local<v8::Value> injectedScriptValue;
if (!function->Call(context, windowGlobal, arraysize(info), info)
.ToLocal(&injectedScriptValue))
return nullptr;
if (inspector->getContext(contextGroupId, contextId) != inspectedContext)
return nullptr;
if (!injectedScriptValue->IsObject()) return nullptr;
std::unique_ptr<InjectedScript> injectedScript(new InjectedScript(
inspectedContext, injectedScriptValue.As<v8::Object>(), sessionId));
v8::Local<v8::Private> privateKey = v8::Private::ForApi(
isolate, v8::String::NewFromUtf8(isolate, privateKeyName,
v8::NewStringType::kInternalized)
.ToLocalChecked());
scriptHostWrapper->SetPrivate(
context, privateKey, v8::External::New(isolate, injectedScript.get()));
return injectedScript;
}
InjectedScript::InjectedScript(InspectedContext* context,
v8::Local<v8::Object> object, int sessionId)
: m_context(context),
m_value(context->isolate(), object),
m_sessionId(sessionId) {}
InjectedScript::InjectedScript(InspectedContext* context, int sessionId)
: m_context(context), m_sessionId(sessionId) {}
InjectedScript::~InjectedScript() { discardEvaluateCallbacks(); }
namespace {
class PropertyAccumulator : public ValueMirror::PropertyAccumulator {
public:
explicit PropertyAccumulator(std::vector<PropertyMirror>* mirrors)
: m_mirrors(mirrors) {}
bool Add(PropertyMirror mirror) override {
m_mirrors->push_back(std::move(mirror));
return true;
}
private:
std::vector<PropertyMirror>* m_mirrors;
};
} // anonymous namespace
Response InjectedScript::getProperties(
v8::Local<v8::Object> object, const String16& groupName, bool ownProperties,
bool accessorPropertiesOnly, bool generatePreview,
bool accessorPropertiesOnly, WrapMode wrapMode,
std::unique_ptr<Array<PropertyDescriptor>>* properties,
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
v8::HandleScope handles(m_context->isolate());
v8::Local<v8::Context> context = m_context->context();
V8FunctionCall function(m_context->inspector(), m_context->context(),
v8Value(), "getProperties");
function.appendArgument(object);
function.appendArgument(groupName);
function.appendArgument(ownProperties);
function.appendArgument(accessorPropertiesOnly);
function.appendArgument(generatePreview);
v8::TryCatch tryCatch(m_context->isolate());
v8::Local<v8::Value> resultValue = function.callWithoutExceptionHandling();
if (tryCatch.HasCaught()) {
Response response = createExceptionDetails(
tryCatch, groupName, generatePreview, exceptionDetails);
v8::Isolate* isolate = m_context->isolate();
int sessionId = m_sessionId;
v8::TryCatch tryCatch(isolate);
*properties = Array<PropertyDescriptor>::create();
std::vector<PropertyMirror> mirrors;
PropertyAccumulator accumulator(&mirrors);
if (!ValueMirror::getProperties(context, object, ownProperties,
accessorPropertiesOnly, &accumulator)) {
return createExceptionDetails(tryCatch, groupName, wrapMode,
exceptionDetails);
}
for (const PropertyMirror& mirror : mirrors) {
std::unique_ptr<PropertyDescriptor> descriptor =
PropertyDescriptor::create()
.setName(mirror.name)
.setConfigurable(mirror.configurable)
.setEnumerable(mirror.enumerable)
.setIsOwn(mirror.isOwn)
.build();
Response response;
std::unique_ptr<RemoteObject> remoteObject;
if (mirror.value) {
response =
mirror.value->buildRemoteObject(context, wrapMode, &remoteObject);
if (!response.isSuccess()) return response;
response =
bindRemoteObjectIfNeeded(sessionId, context, mirror.value->v8Value(),
groupName, remoteObject.get());
if (!response.isSuccess()) return response;
descriptor->setValue(std::move(remoteObject));
descriptor->setWritable(mirror.writable);
}
if (mirror.getter) {
response =
mirror.getter->buildRemoteObject(context, wrapMode, &remoteObject);
if (!response.isSuccess()) return response;
response =
bindRemoteObjectIfNeeded(sessionId, context, mirror.getter->v8Value(),
groupName, remoteObject.get());
if (!response.isSuccess()) return response;
descriptor->setGet(std::move(remoteObject));
}
if (mirror.setter) {
response =
mirror.setter->buildRemoteObject(context, wrapMode, &remoteObject);
if (!response.isSuccess()) return response;
response =
bindRemoteObjectIfNeeded(sessionId, context, mirror.setter->v8Value(),
groupName, remoteObject.get());
if (!response.isSuccess()) return response;
descriptor->setSet(std::move(remoteObject));
}
if (mirror.symbol) {
response =
mirror.symbol->buildRemoteObject(context, wrapMode, &remoteObject);
if (!response.isSuccess()) return response;
response =
bindRemoteObjectIfNeeded(sessionId, context, mirror.symbol->v8Value(),
groupName, remoteObject.get());
if (!response.isSuccess()) return response;
descriptor->setSymbol(std::move(remoteObject));
}
if (mirror.exception) {
response =
mirror.exception->buildRemoteObject(context, wrapMode, &remoteObject);
if (!response.isSuccess()) return response;
response = bindRemoteObjectIfNeeded(sessionId, context,
mirror.exception->v8Value(),
groupName, remoteObject.get());
if (!response.isSuccess()) return response;
descriptor->setValue(std::move(remoteObject));
descriptor->setWasThrown(true);
}
(*properties)->addItem(std::move(descriptor));
}
return Response::OK();
}
Response InjectedScript::getInternalProperties(
v8::Local<v8::Value> value, const String16& groupName,
std::unique_ptr<protocol::Array<InternalPropertyDescriptor>>* result) {
*result = protocol::Array<InternalPropertyDescriptor>::create();
v8::Local<v8::Context> context = m_context->context();
int sessionId = m_sessionId;
std::vector<InternalPropertyMirror> wrappers;
if (value->IsObject()) {
ValueMirror::getInternalProperties(m_context->context(),
value.As<v8::Object>(), &wrappers);
}
for (size_t i = 0; i < wrappers.size(); ++i) {
std::unique_ptr<RemoteObject> remoteObject;
Response response = wrappers[i].value->buildRemoteObject(
m_context->context(), WrapMode::kNoPreview, &remoteObject);
if (!response.isSuccess()) return response;
// FIXME: make properties optional
*properties = Array<PropertyDescriptor>::create();
return Response::OK();
response = bindRemoteObjectIfNeeded(sessionId, context,
wrappers[i].value->v8Value(), groupName,
remoteObject.get());
if (!response.isSuccess()) return response;
(*result)->addItem(InternalPropertyDescriptor::create()
.setName(wrappers[i].name)
.setValue(std::move(remoteObject))
.build());
}
if (resultValue.IsEmpty()) return Response::InternalError();
std::unique_ptr<protocol::Value> protocolValue;
Response response = toProtocolValue(context, resultValue, &protocolValue);
if (!response.isSuccess()) return response;
protocol::ErrorSupport errors;
std::unique_ptr<Array<PropertyDescriptor>> result =
Array<PropertyDescriptor>::fromValue(protocolValue.get(), &errors);
if (errors.hasErrors()) return Response::Error(errors.errors());
*properties = std::move(result);
return Response::OK();
}
......@@ -379,37 +399,27 @@ void InjectedScript::releaseObject(const String16& objectId) {
}
Response InjectedScript::wrapObject(
v8::Local<v8::Value> value, const String16& groupName, bool forceValueType,
bool generatePreview,
std::unique_ptr<protocol::Runtime::RemoteObject>* result) const {
return wrapObject(value, groupName, forceValueType, generatePreview,
v8::MaybeLocal<v8::Value>(), kMaxCustomPreviewDepth,
result);
v8::Local<v8::Value> value, const String16& groupName, WrapMode wrapMode,
std::unique_ptr<protocol::Runtime::RemoteObject>* result) {
return wrapObject(value, groupName, wrapMode, v8::MaybeLocal<v8::Value>(),
kMaxCustomPreviewDepth, result);
}
Response InjectedScript::wrapObject(
v8::Local<v8::Value> value, const String16& groupName, bool forceValueType,
bool generatePreview, v8::MaybeLocal<v8::Value> customPreviewConfig,
int maxCustomPreviewDepth,
std::unique_ptr<protocol::Runtime::RemoteObject>* result) const {
v8::HandleScope handles(m_context->isolate());
v8::Local<v8::Value> wrappedObject;
v8::Local<v8::Value> value, const String16& groupName, WrapMode wrapMode,
v8::MaybeLocal<v8::Value> customPreviewConfig, int maxCustomPreviewDepth,
std::unique_ptr<protocol::Runtime::RemoteObject>* result) {
v8::Local<v8::Context> context = m_context->context();
v8::Context::Scope contextScope(context);
int customPreviewEnabled = m_customPreviewEnabled;
int sessionId = m_sessionId;
Response response = wrapValue(value, groupName, forceValueType,
generatePreview, &wrappedObject);
auto obj = ValueMirror::create(m_context->context(), value);
if (!obj) return Response::InternalError();
Response response = obj->buildRemoteObject(context, wrapMode, result);
if (!response.isSuccess()) return response;
protocol::ErrorSupport errors;
std::unique_ptr<protocol::Value> protocolValue;
response = toProtocolValue(context, wrappedObject, &protocolValue);
response = bindRemoteObjectIfNeeded(sessionId, context, value, groupName,
result->get());
if (!response.isSuccess()) return response;
*result =
protocol::Runtime::RemoteObject::fromValue(protocolValue.get(), &errors);
if (!result->get()) return Response::Error(errors.errors());
if (customPreviewEnabled && value->IsObject()) {
std::unique_ptr<protocol::Runtime::CustomPreview> customPreview;
generateCustomPreview(sessionId, groupName, context, value.As<v8::Object>(),
......@@ -420,47 +430,65 @@ Response InjectedScript::wrapObject(
return Response::OK();
}
Response InjectedScript::wrapValue(v8::Local<v8::Value> value,
const String16& groupName,
bool forceValueType, bool generatePreview,
v8::Local<v8::Value>* result) const {
V8FunctionCall function(m_context->inspector(), m_context->context(),
v8Value(), "wrapObject");
function.appendArgument(value);
function.appendArgument(groupName);
function.appendArgument(forceValueType);
function.appendArgument(generatePreview);
bool hadException = false;
*result = function.call(hadException);
if (hadException || result->IsEmpty()) return Response::InternalError();
return Response::OK();
}
std::unique_ptr<protocol::Runtime::RemoteObject> InjectedScript::wrapTable(
v8::Local<v8::Value> table, v8::Local<v8::Value> columns) const {
v8::HandleScope handles(m_context->isolate());
v8::Local<v8::Object> table, v8::MaybeLocal<v8::Array> maybeColumns) {
using protocol::Runtime::RemoteObject;
using protocol::Runtime::ObjectPreview;
using protocol::Runtime::PropertyPreview;
using protocol::Array;
v8::Isolate* isolate = m_context->isolate();
v8::HandleScope handles(isolate);
v8::Local<v8::Context> context = m_context->context();
V8FunctionCall function(m_context->inspector(), context, v8Value(),
"wrapTable");
function.appendArgument(table);
if (columns.IsEmpty())
function.appendArgument(false);
else
function.appendArgument(columns);
bool hadException = false;
v8::Local<v8::Value> r = function.call(hadException);
if (hadException || r.IsEmpty()) return nullptr;
std::unique_ptr<protocol::Value> protocolValue;
Response response = toProtocolValue(context, r, &protocolValue);
if (!response.isSuccess()) return nullptr;
protocol::ErrorSupport errors;
return protocol::Runtime::RemoteObject::fromValue(protocolValue.get(),
&errors);
std::unique_ptr<RemoteObject> remoteObject;
Response response =
wrapObject(table, "console", WrapMode::kNoPreview, &remoteObject);
if (!remoteObject || !response.isSuccess()) return nullptr;
auto mirror = ValueMirror::create(context, table);
std::unique_ptr<ObjectPreview> preview;
int limit = 100;
mirror->buildObjectPreview(context, true /* generatePreviewForProperties */,
&limit, &limit, &preview);
Array<PropertyPreview>* columns = preview->getProperties();
std::unordered_set<String16> selectedColumns;
v8::Local<v8::Array> v8Columns;
if (maybeColumns.ToLocal(&v8Columns)) {
for (uint32_t i = 0; i < v8Columns->Length(); ++i) {
v8::Local<v8::Value> column;
if (v8Columns->Get(context, i).ToLocal(&column) && column->IsString()) {
selectedColumns.insert(
toProtocolString(isolate, column.As<v8::String>()));
}
}
}
if (!selectedColumns.empty()) {
for (size_t i = 0; i < columns->length(); ++i) {
ObjectPreview* columnPreview = columns->get(i)->getValuePreview(nullptr);
if (!columnPreview) continue;
std::unique_ptr<Array<PropertyPreview>> filtered =
Array<PropertyPreview>::create();
Array<PropertyPreview>* columns = columnPreview->getProperties();
for (size_t j = 0; j < columns->length(); ++j) {
PropertyPreview* property = columns->get(j);
if (selectedColumns.find(property->getName()) !=
selectedColumns.end()) {
filtered->addItem(property->clone());
}
}
columnPreview->setProperties(std::move(filtered));
}
}
remoteObject->setPreview(std::move(preview));
return remoteObject;
}
void InjectedScript::addPromiseCallback(
V8InspectorSessionImpl* session, v8::MaybeLocal<v8::Value> value,
const String16& objectGroup, bool returnByValue, bool generatePreview,
const String16& objectGroup, WrapMode wrapMode,
std::unique_ptr<EvaluateCallback> callback) {
if (value.IsEmpty()) {
callback->sendFailure(Response::InternalError());
......@@ -470,8 +498,7 @@ void InjectedScript::addPromiseCallback(
v8::MicrotasksScope::kRunMicrotasks);
if (ProtocolPromiseHandler::add(
session, m_context->context(), value.ToLocalChecked(),
m_context->contextId(), objectGroup, returnByValue, generatePreview,
callback.get())) {
m_context->contextId(), objectGroup, wrapMode, callback.get())) {
m_evaluateCallbacks.insert(callback.release());
}
}
......@@ -521,10 +548,6 @@ void InjectedScript::setCustomObjectFormatterEnabled(bool enabled) {
m_customPreviewEnabled = enabled;
}
v8::Local<v8::Value> InjectedScript::v8Value() const {
return m_value.Get(m_context->isolate());
}
v8::Local<v8::Value> InjectedScript::lastEvaluationResult() const {
if (m_lastEvaluationResult.IsEmpty())
return v8::Undefined(m_context->isolate());
......@@ -576,7 +599,7 @@ Response InjectedScript::resolveCallArgument(
Response InjectedScript::createExceptionDetails(
const v8::TryCatch& tryCatch, const String16& objectGroup,
bool generatePreview, Maybe<protocol::Runtime::ExceptionDetails>* result) {
WrapMode wrapMode, Maybe<protocol::Runtime::ExceptionDetails>* result) {
if (!tryCatch.HasCaught()) return Response::InternalError();
v8::Local<v8::Message> message = tryCatch.Message();
v8::Local<v8::Value> exception = tryCatch.Exception();
......@@ -612,8 +635,10 @@ Response InjectedScript::createExceptionDetails(
if (!exception.IsEmpty()) {
std::unique_ptr<protocol::Runtime::RemoteObject> wrapped;
Response response =
wrapObject(exception, objectGroup, false /* forceValueType */,
generatePreview && !exception->IsNativeError(), &wrapped);
wrapObject(exception, objectGroup,
exception->IsNativeError() ? WrapMode::kNoPreview
: WrapMode::kWithPreview,
&wrapped);
if (!response.isSuccess()) return response;
exceptionDetails->setException(std::move(wrapped));
}
......@@ -623,15 +648,14 @@ Response InjectedScript::createExceptionDetails(
Response InjectedScript::wrapEvaluateResult(
v8::MaybeLocal<v8::Value> maybeResultValue, const v8::TryCatch& tryCatch,
const String16& objectGroup, bool returnByValue, bool generatePreview,
const String16& objectGroup, WrapMode wrapMode,
std::unique_ptr<protocol::Runtime::RemoteObject>* result,
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
v8::Local<v8::Value> resultValue;
if (!tryCatch.HasCaught()) {
if (!maybeResultValue.ToLocal(&resultValue))
return Response::InternalError();
Response response = wrapObject(resultValue, objectGroup, returnByValue,
generatePreview, result);
Response response = wrapObject(resultValue, objectGroup, wrapMode, result);
if (!response.isSuccess()) return response;
if (objectGroup == "console") {
m_lastEvaluationResult.Reset(m_context->isolate(), resultValue);
......@@ -643,12 +667,14 @@ Response InjectedScript::wrapEvaluateResult(
}
v8::Local<v8::Value> exception = tryCatch.Exception();
Response response =
wrapObject(exception, objectGroup, false,
generatePreview && !exception->IsNativeError(), result);
wrapObject(exception, objectGroup,
exception->IsNativeError() ? WrapMode::kNoPreview
: WrapMode::kWithPreview,
result);
if (!response.isSuccess()) return response;
// We send exception in result for compatibility reasons, even though it's
// accessible through exceptionDetails.exception.
response = createExceptionDetails(tryCatch, objectGroup, generatePreview,
response = createExceptionDetails(tryCatch, objectGroup, wrapMode,
exceptionDetails);
if (!response.isSuccess()) return response;
}
......@@ -797,33 +823,44 @@ Response InjectedScript::CallFrameScope::findInjectedScript(
return session->findInjectedScript(remoteId.get(), m_injectedScript);
}
InjectedScript* InjectedScript::fromInjectedScriptHost(
v8::Isolate* isolate, v8::Local<v8::Object> injectedScriptObject) {
v8::HandleScope handleScope(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Private> privateKey = v8::Private::ForApi(
isolate, v8::String::NewFromUtf8(isolate, privateKeyName,
v8::NewStringType::kInternalized)
.ToLocalChecked());
v8::Local<v8::Value> value =
injectedScriptObject->GetPrivate(context, privateKey).ToLocalChecked();
DCHECK(value->IsExternal());
v8::Local<v8::External> external = value.As<v8::External>();
return static_cast<InjectedScript*>(external->Value());
}
int InjectedScript::bindObject(v8::Local<v8::Value> value,
const String16& groupName) {
String16 InjectedScript::bindObject(v8::Local<v8::Value> value,
const String16& groupName) {
if (m_lastBoundObjectId <= 0) m_lastBoundObjectId = 1;
int id = m_lastBoundObjectId++;
m_idToWrappedObject[id].Reset(m_context->isolate(), value);
m_idToWrappedObject[id].AnnotateStrongRetainer(kGlobalHandleLabel);
if (!groupName.isEmpty() && id > 0) {
m_idToObjectGroupName[id] = groupName;
m_nameToObjectGroup[groupName].push_back(id);
}
return id;
// TODO(dgozman): get rid of "injectedScript" notion.
return String16::concat(
"{\"injectedScriptId\":", String16::fromInteger(m_context->contextId()),
",\"id\":", String16::fromInteger(id), "}");
}
// static
Response InjectedScript::bindRemoteObjectIfNeeded(
int sessionId, v8::Local<v8::Context> context, v8::Local<v8::Value> value,
const String16& groupName, protocol::Runtime::RemoteObject* remoteObject) {
if (!remoteObject) return Response::OK();
if (remoteObject->hasValue()) return Response::OK();
if (remoteObject->hasUnserializableValue()) return Response::OK();
if (remoteObject->getType() != RemoteObject::TypeEnum::Undefined) {
v8::Isolate* isolate = context->GetIsolate();
V8InspectorImpl* inspector =
static_cast<V8InspectorImpl*>(v8::debug::GetInspector(isolate));
InspectedContext* inspectedContext =
inspector->getContext(InspectedContext::contextId(context));
InjectedScript* injectedScript =
inspectedContext ? inspectedContext->getInjectedScript(sessionId)
: nullptr;
if (!injectedScript) {
return Response::Error("Cannot find context with specified id");
}
remoteObject->setObjectId(injectedScript->bindObject(value, groupName));
}
return Response::OK();
}
void InjectedScript::unbindObject(int id) {
......
......@@ -46,9 +46,9 @@
namespace v8_inspector {
class RemoteObjectId;
class V8FunctionCall;
class V8InspectorImpl;
class V8InspectorSessionImpl;
enum class WrapMode;
using protocol::Maybe;
using protocol::Response;
......@@ -65,38 +65,40 @@ class EvaluateCallback {
class InjectedScript final {
public:
static std::unique_ptr<InjectedScript> create(InspectedContext*,
int sessionId);
InjectedScript(InspectedContext*, int sessionId);
~InjectedScript();
static InjectedScript* fromInjectedScriptHost(v8::Isolate* isolate,
v8::Local<v8::Object>);
InspectedContext* context() const { return m_context; }
Response getProperties(
v8::Local<v8::Object>, const String16& groupName, bool ownProperties,
bool accessorPropertiesOnly, bool generatePreview,
bool accessorPropertiesOnly, WrapMode wrapMode,
std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>>*
result,
Maybe<protocol::Runtime::ExceptionDetails>*);
Response getInternalProperties(
v8::Local<v8::Value>, const String16& groupName,
std::unique_ptr<
protocol::Array<protocol::Runtime::InternalPropertyDescriptor>>*
result);
void releaseObject(const String16& objectId);
Response wrapObject(
v8::Local<v8::Value>, const String16& groupName, bool forceValueType,
bool generatePreview,
std::unique_ptr<protocol::Runtime::RemoteObject>* result) const;
Response wrapObject(
v8::Local<v8::Value>, const String16& groupName, bool forceValueType,
bool generatePreview, v8::MaybeLocal<v8::Value> customPreviewConfig,
int maxCustomPreviewDepth,
std::unique_ptr<protocol::Runtime::RemoteObject>* result) const;
Response wrapObject(v8::Local<v8::Value>, const String16& groupName,
WrapMode wrapMode,
std::unique_ptr<protocol::Runtime::RemoteObject>* result);
Response wrapObject(v8::Local<v8::Value>, const String16& groupName,
WrapMode wrapMode,
v8::MaybeLocal<v8::Value> customPreviewConfig,
int maxCustomPreviewDepth,
std::unique_ptr<protocol::Runtime::RemoteObject>* result);
std::unique_ptr<protocol::Runtime::RemoteObject> wrapTable(
v8::Local<v8::Value> table, v8::Local<v8::Value> columns) const;
v8::Local<v8::Object> table, v8::MaybeLocal<v8::Array> columns);
void addPromiseCallback(V8InspectorSessionImpl* session,
v8::MaybeLocal<v8::Value> value,
const String16& objectGroup, bool returnByValue,
bool generatePreview,
const String16& objectGroup, WrapMode wrapMode,
std::unique_ptr<EvaluateCallback> callback);
Response findObject(const RemoteObjectId&, v8::Local<v8::Value>*) const;
......@@ -107,18 +109,16 @@ class InjectedScript final {
v8::Local<v8::Value>* result);
Response createExceptionDetails(
const v8::TryCatch&, const String16& groupName, bool generatePreview,
const v8::TryCatch&, const String16& groupName, WrapMode wrapMode,
Maybe<protocol::Runtime::ExceptionDetails>* result);
Response wrapEvaluateResult(
v8::MaybeLocal<v8::Value> maybeResultValue, const v8::TryCatch&,
const String16& objectGroup, bool returnByValue, bool generatePreview,
const String16& objectGroup, WrapMode wrapMode,
std::unique_ptr<protocol::Runtime::RemoteObject>* result,
Maybe<protocol::Runtime::ExceptionDetails>*);
v8::Local<v8::Value> lastEvaluationResult() const;
void setLastEvaluationResult(v8::Local<v8::Value> result);
int bindObject(v8::Local<v8::Value>, const String16& groupName);
class Scope {
public:
Response initialize();
......@@ -196,23 +196,22 @@ class InjectedScript final {
DISALLOW_COPY_AND_ASSIGN(CallFrameScope);
};
String16 bindObject(v8::Local<v8::Value>, const String16& groupName);
private:
InjectedScript(InspectedContext*, v8::Local<v8::Object>, int sessionId);
v8::Local<v8::Value> v8Value() const;
Response wrapValue(v8::Local<v8::Value>, const String16& groupName,
bool forceValueType, bool generatePreview,
v8::Local<v8::Value>* result) const;
v8::Local<v8::Object> commandLineAPI();
void unbindObject(int id);
static Response bindRemoteObjectIfNeeded(
int sessionId, v8::Local<v8::Context> context, v8::Local<v8::Value>,
const String16& groupName, protocol::Runtime::RemoteObject* remoteObject);
class ProtocolPromiseHandler;
void discardEvaluateCallbacks();
std::unique_ptr<EvaluateCallback> takeEvaluateCallback(
EvaluateCallback* callback);
InspectedContext* m_context;
v8::Global<v8::Value> m_value;
int m_sessionId;
v8::Global<v8::Value> m_lastEvaluationResult;
v8::Global<v8::Object> m_commandLineAPI;
......
// Copyright 2015 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.
/** @interface */
function InjectedScriptHostClass()
{
}
/**
* @param {*} obj
*/
InjectedScriptHostClass.prototype.nullifyPrototype = function(obj) {}
/**
* @param {*} obj
* @param {string} name
* @return {*}
*/
InjectedScriptHostClass.prototype.getProperty = function(obj, name) {}
/**
* @param {*} obj
* @return {string}
*/
InjectedScriptHostClass.prototype.internalConstructorName = function(obj) {}
/**
* @param {*} obj
* @param {function()|undefined} func
* @return {boolean}
*/
InjectedScriptHostClass.prototype.formatAccessorsAsProperties = function(obj, func) {}
/**
* @param {*} obj
* @return {string}
*/
InjectedScriptHostClass.prototype.subtype = function(obj) {}
/**
* @param {*} obj
* @return {boolean}
*/
InjectedScriptHostClass.prototype.isTypedArray = function(obj) {}
/**
* @param {*} obj
* @return {!Array.<*>}
*/
InjectedScriptHostClass.prototype.getInternalProperties = function(obj) {}
/**
* @param {!Object} object
* @param {string} propertyName
* @return {boolean}
*/
InjectedScriptHostClass.prototype.objectHasOwnProperty = function(object, propertyName) {}
/**
* @param {*} value
* @param {string} groupName
* @return {number}
*/
InjectedScriptHostClass.prototype.bind = function(value, groupName) {}
/**
* @param {!Object} object
* @return {!Object}
*/
InjectedScriptHostClass.prototype.proxyTargetValue = function(object) {}
/**
* @param {!Object} obj
* @return {!Array<string>}
*/
InjectedScriptHostClass.prototype.keys = function(obj) {}
/**
* @param {!Object} obj
* @return {Object}
*/
InjectedScriptHostClass.prototype.getPrototypeOf = function(obj) {}
/**
* @param {!Object} obj
* @param {string} prop
* @return {Object}
*/
InjectedScriptHostClass.prototype.getOwnPropertyDescriptor = function(obj, prop) {}
/**
* @param {!Object} obj
* @return {!Array<string>}
*/
InjectedScriptHostClass.prototype.getOwnPropertyNames = function(obj) {}
/**
* @param {!Object} obj
* @return {!Array<symbol>}
*/
InjectedScriptHostClass.prototype.getOwnPropertySymbols = function(obj) {}
/**
* @param {!Object} obj
* @param {string|symbol} name
* @return {{isBuiltin:boolean, hasGetter:boolean, hasSetter:boolean}|undefined}
*/
InjectedScriptHostClass.prototype.nativeAccessorDescriptor = function(obj, name) {}
/**
* @param {!Object} arrayBuffer
* @return {Array<Object>|undefined}
*/
InjectedScriptHostClass.prototype.typedArrayProperties = function(arrayBuffer) {}
/** @type {!InjectedScriptHostClass} */
var InjectedScriptHost;
/** @type {!Window} */
var inspectedGlobalObject;
/** @type {number} */
var injectedScriptId;
......@@ -109,14 +109,12 @@ InjectedScript* InspectedContext::getInjectedScript(int sessionId) {
return it == m_injectedScripts.end() ? nullptr : it->second.get();
}
bool InspectedContext::createInjectedScript(int sessionId) {
InjectedScript* InspectedContext::createInjectedScript(int sessionId) {
std::unique_ptr<InjectedScript> injectedScript =
InjectedScript::create(this, sessionId);
// InjectedScript::create can destroy |this|.
if (!injectedScript) return false;
v8::base::make_unique<InjectedScript>(this, sessionId);
CHECK(m_injectedScripts.find(sessionId) == m_injectedScripts.end());
m_injectedScripts[sessionId] = std::move(injectedScript);
return true;
return getInjectedScript(sessionId);
}
void InspectedContext::discardInjectedScript(int sessionId) {
......
......@@ -40,7 +40,7 @@ class InspectedContext {
V8InspectorImpl* inspector() const { return m_inspector; }
InjectedScript* getInjectedScript(int sessionId);
bool createInjectedScript(int sessionId);
InjectedScript* createInjectedScript(int sessionId);
void discardInjectedScript(int sessionId);
private:
......
......@@ -1890,7 +1890,9 @@
"error",
"proxy",
"promise",
"typedarray"
"typedarray",
"arraybuffer",
"dataview"
]
},
{
......
......@@ -887,6 +887,8 @@ domain Runtime
proxy
promise
typedarray
arraybuffer
dataview
# 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).
......
......@@ -259,19 +259,33 @@ V8ConsoleMessage::wrapArguments(V8InspectorSessionImpl* session,
std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> args =
protocol::Array<protocol::Runtime::RemoteObject>::create();
if (m_type == ConsoleAPIType::kTable && generatePreview) {
v8::Local<v8::Value> table = m_arguments[0]->Get(isolate);
v8::Local<v8::Value> columns = m_arguments.size() > 1
? m_arguments[1]->Get(isolate)
: v8::Local<v8::Value>();
v8::Local<v8::Value> value = m_arguments[0]->Get(isolate);
if (value->IsObject() && m_type == ConsoleAPIType::kTable &&
generatePreview) {
v8::MaybeLocal<v8::Array> columns;
if (m_arguments.size() > 1) {
v8::Local<v8::Value> secondArgument = m_arguments[1]->Get(isolate);
if (secondArgument->IsArray()) {
columns = v8::Local<v8::Array>::Cast(secondArgument);
} else if (secondArgument->IsString()) {
v8::TryCatch tryCatch(isolate);
v8::Local<v8::Array> array = v8::Array::New(isolate);
if (array->Set(context, 0, secondArgument).IsJust()) {
columns = array;
}
}
}
std::unique_ptr<protocol::Runtime::RemoteObject> wrapped =
session->wrapTable(context, table, columns);
session->wrapTable(context, v8::Local<v8::Object>::Cast(value),
columns);
inspectedContext = inspector->getContext(contextGroupId, contextId);
if (!inspectedContext) return nullptr;
if (wrapped)
if (wrapped) {
args->addItem(std::move(wrapped));
else
} else {
args = nullptr;
}
} else {
for (size_t i = 0; i < m_arguments.size(); ++i) {
std::unique_ptr<protocol::Runtime::RemoteObject> wrapped =
......
......@@ -600,9 +600,8 @@ static void inspectImpl(const v8::FunctionCallbackInfo<v8::Value>& info,
InjectedScript* injectedScript = helper.injectedScript(sessionId);
if (!injectedScript) return;
std::unique_ptr<protocol::Runtime::RemoteObject> wrappedObject;
protocol::Response response =
injectedScript->wrapObject(value, "", false /** forceValueType */,
false /** generatePreview */, &wrappedObject);
protocol::Response response = injectedScript->wrapObject(
value, "", WrapMode::kNoPreview, &wrappedObject);
if (!response.isSuccess()) return;
std::unique_ptr<protocol::DictionaryValue> hints =
......
......@@ -260,8 +260,9 @@ Response buildScopes(v8::Isolate* isolate, v8::debug::ScopeIterator* iterator,
for (; !iterator->Done(); iterator->Advance()) {
std::unique_ptr<RemoteObject> object;
Response result = injectedScript->wrapObject(
iterator->GetObject(), kBacktraceObjectGroup, false, false, &object);
Response result =
injectedScript->wrapObject(iterator->GetObject(), kBacktraceObjectGroup,
WrapMode::kNoPreview, &object);
if (!result.isSuccess()) return result;
auto scope = Scope::create()
......@@ -1085,10 +1086,12 @@ Response V8DebuggerAgentImpl::evaluateOnCallFrame(
// context or session.
response = scope.initialize();
if (!response.isSuccess()) return response;
WrapMode mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
: WrapMode::kNoPreview;
if (returnByValue.fromMaybe(false)) mode = WrapMode::kForceValue;
return scope.injectedScript()->wrapEvaluateResult(
maybeResultValue, scope.tryCatch(), objectGroup.fromMaybe(""),
returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), result,
exceptionDetails);
maybeResultValue, scope.tryCatch(), objectGroup.fromMaybe(""), mode,
result, exceptionDetails);
}
Response V8DebuggerAgentImpl::setVariableValue(
......@@ -1268,8 +1271,9 @@ Response V8DebuggerAgentImpl::currentCallFrames(
if (injectedScript) {
v8::Local<v8::Value> receiver;
if (iterator->GetReceiver().ToLocal(&receiver)) {
res = injectedScript->wrapObject(receiver, kBacktraceObjectGroup, false,
false, &protocolReceiver);
res =
injectedScript->wrapObject(receiver, kBacktraceObjectGroup,
WrapMode::kNoPreview, &protocolReceiver);
if (!res.isSuccess()) return res;
}
}
......@@ -1320,7 +1324,7 @@ Response V8DebuggerAgentImpl::currentCallFrames(
if (!returnValue.IsEmpty() && injectedScript) {
std::unique_ptr<RemoteObject> value;
res = injectedScript->wrapObject(returnValue, kBacktraceObjectGroup,
false, false, &value);
WrapMode::kNoPreview, &value);
if (!res.isSuccess()) return res;
frame->setReturnValue(std::move(value));
}
......@@ -1528,8 +1532,8 @@ void V8DebuggerAgentImpl::didPause(
? protocol::Debugger::Paused::ReasonEnum::PromiseRejection
: protocol::Debugger::Paused::ReasonEnum::Exception;
std::unique_ptr<protocol::Runtime::RemoteObject> obj;
injectedScript->wrapObject(exception, kBacktraceObjectGroup, false, false,
&obj);
injectedScript->wrapObject(exception, kBacktraceObjectGroup,
WrapMode::kNoPreview, &obj);
std::unique_ptr<protocol::DictionaryValue> breakAuxData;
if (obj) {
breakAuxData = obj->toValue();
......
......@@ -10,7 +10,6 @@
#include "src/inspector/v8-debugger-agent-impl.h"
#include "src/inspector/v8-inspector-impl.h"
#include "src/inspector/v8-inspector-session-impl.h"
#include "src/inspector/v8-internal-value-type.h"
#include "src/inspector/v8-runtime-agent-impl.h"
#include "src/inspector/v8-stack-trace-impl.h"
#include "src/inspector/v8-value-utils.h"
......@@ -24,102 +23,6 @@ namespace {
static const int kMaxAsyncTaskStacks = 128 * 1024;
static const int kNoBreakpointId = 0;
v8::MaybeLocal<v8::Array> collectionsEntries(v8::Local<v8::Context> context,
v8::Local<v8::Value> value) {
v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::Array> entries;
bool isKeyValue = false;
if (!value->IsObject() ||
!value.As<v8::Object>()->PreviewEntries(&isKeyValue).ToLocal(&entries)) {
return v8::MaybeLocal<v8::Array>();
}
v8::Local<v8::Array> wrappedEntries = v8::Array::New(isolate);
CHECK(!isKeyValue || wrappedEntries->Length() % 2 == 0);
if (!wrappedEntries->SetPrototype(context, v8::Null(isolate))
.FromMaybe(false))
return v8::MaybeLocal<v8::Array>();
for (uint32_t i = 0; i < entries->Length(); i += isKeyValue ? 2 : 1) {
v8::Local<v8::Value> item;
if (!entries->Get(context, i).ToLocal(&item)) continue;
v8::Local<v8::Value> value;
if (isKeyValue && !entries->Get(context, i + 1).ToLocal(&value)) continue;
v8::Local<v8::Object> wrapper = v8::Object::New(isolate);
if (!wrapper->SetPrototype(context, v8::Null(isolate)).FromMaybe(false))
continue;
createDataProperty(
context, wrapper,
toV8StringInternalized(isolate, isKeyValue ? "key" : "value"), item);
if (isKeyValue) {
createDataProperty(context, wrapper,
toV8StringInternalized(isolate, "value"), value);
}
createDataProperty(context, wrappedEntries, wrappedEntries->Length(),
wrapper);
}
if (!markArrayEntriesAsInternal(context, wrappedEntries,
V8InternalValueType::kEntry)) {
return v8::MaybeLocal<v8::Array>();
}
return wrappedEntries;
}
v8::MaybeLocal<v8::Object> buildLocation(v8::Local<v8::Context> context,
int scriptId, int lineNumber,
int columnNumber) {
if (scriptId == v8::UnboundScript::kNoScriptId)
return v8::MaybeLocal<v8::Object>();
if (lineNumber == v8::Function::kLineOffsetNotFound ||
columnNumber == v8::Function::kLineOffsetNotFound) {
return v8::MaybeLocal<v8::Object>();
}
v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::Object> location = v8::Object::New(isolate);
if (!location->SetPrototype(context, v8::Null(isolate)).FromMaybe(false)) {
return v8::MaybeLocal<v8::Object>();
}
if (!createDataProperty(context, location,
toV8StringInternalized(isolate, "scriptId"),
toV8String(isolate, String16::fromInteger(scriptId)))
.FromMaybe(false)) {
return v8::MaybeLocal<v8::Object>();
}
if (!createDataProperty(context, location,
toV8StringInternalized(isolate, "lineNumber"),
v8::Integer::New(isolate, lineNumber))
.FromMaybe(false)) {
return v8::MaybeLocal<v8::Object>();
}
if (!createDataProperty(context, location,
toV8StringInternalized(isolate, "columnNumber"),
v8::Integer::New(isolate, columnNumber))
.FromMaybe(false)) {
return v8::MaybeLocal<v8::Object>();
}
if (!markAsInternal(context, location, V8InternalValueType::kLocation)) {
return v8::MaybeLocal<v8::Object>();
}
return location;
}
v8::MaybeLocal<v8::Object> generatorObjectLocation(
v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
if (!value->IsGeneratorObject()) return v8::MaybeLocal<v8::Object>();
v8::Local<v8::debug::GeneratorObject> generatorObject =
v8::debug::GeneratorObject::Cast(value);
if (!generatorObject->IsSuspended()) {
v8::Local<v8::Function> func = generatorObject->Function();
return buildLocation(context, func->ScriptId(), func->GetScriptLineNumber(),
func->GetScriptColumnNumber());
}
v8::Local<v8::debug::Script> script;
if (!generatorObject->Script().ToLocal(&script))
return v8::MaybeLocal<v8::Object>();
v8::debug::Location suspendedLocation = generatorObject->SuspendedLocation();
return buildLocation(context, script->Id(), suspendedLocation.GetLineNumber(),
suspendedLocation.GetColumnNumber());
}
template <typename Map>
void cleanupExpiredWeakPointers(Map& map) {
for (auto it = map.begin(); it != map.end();) {
......@@ -714,28 +617,52 @@ v8::MaybeLocal<v8::Value> V8Debugger::getTargetScopes(
for (; !iterator->Done(); iterator->Advance()) {
v8::Local<v8::Object> scope = v8::Object::New(m_isolate);
if (!markAsInternal(context, scope, V8InternalValueType::kScope)) {
if (!addInternalObject(context, scope, V8InternalValueType::kScope)) {
return v8::MaybeLocal<v8::Value>();
}
String16 type = v8_inspector::scopeType(iterator->GetType());
String16 name;
v8::Local<v8::Value> maybe_name = iterator->GetFunctionDebugName();
if (!maybe_name->IsUndefined()) {
name = toProtocolStringWithTypeCheck(m_isolate, maybe_name);
String16 nameSuffix = toProtocolStringWithTypeCheck(
m_isolate, iterator->GetFunctionDebugName());
String16 description;
if (nameSuffix.length()) nameSuffix = " (" + nameSuffix + ")";
switch (iterator->GetType()) {
case v8::debug::ScopeIterator::ScopeTypeGlobal:
description = "Global" + nameSuffix;
break;
case v8::debug::ScopeIterator::ScopeTypeLocal:
description = "Local" + nameSuffix;
break;
case v8::debug::ScopeIterator::ScopeTypeWith:
description = "With Block" + nameSuffix;
break;
case v8::debug::ScopeIterator::ScopeTypeClosure:
description = "Closure" + nameSuffix;
break;
case v8::debug::ScopeIterator::ScopeTypeCatch:
description = "Catch" + nameSuffix;
break;
case v8::debug::ScopeIterator::ScopeTypeBlock:
description = "Block" + nameSuffix;
break;
case v8::debug::ScopeIterator::ScopeTypeScript:
description = "Script" + nameSuffix;
break;
case v8::debug::ScopeIterator::ScopeTypeEval:
description = "Eval" + nameSuffix;
break;
case v8::debug::ScopeIterator::ScopeTypeModule:
description = "Module" + nameSuffix;
break;
}
v8::Local<v8::Object> object = iterator->GetObject();
createDataProperty(context, scope,
toV8StringInternalized(m_isolate, "type"),
toV8String(m_isolate, type));
createDataProperty(context, scope,
toV8StringInternalized(m_isolate, "name"),
toV8String(m_isolate, name));
toV8StringInternalized(m_isolate, "description"),
toV8String(m_isolate, description));
createDataProperty(context, scope,
toV8StringInternalized(m_isolate, "object"), object);
createDataProperty(context, result, result->Length(), scope);
}
if (!markAsInternal(context, v8::Local<v8::Array>::Cast(result),
V8InternalValueType::kScopeList))
if (!addInternalObject(context, v8::Local<v8::Array>::Cast(result),
V8InternalValueType::kScopeList))
return v8::MaybeLocal<v8::Value>();
return result;
}
......@@ -750,6 +677,45 @@ v8::MaybeLocal<v8::Value> V8Debugger::generatorScopes(
return getTargetScopes(context, generator, GENERATOR);
}
v8::MaybeLocal<v8::Array> V8Debugger::collectionsEntries(
v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::Array> entries;
bool isKeyValue = false;
if (!value->IsObject() ||
!value.As<v8::Object>()->PreviewEntries(&isKeyValue).ToLocal(&entries)) {
return v8::MaybeLocal<v8::Array>();
}
v8::Local<v8::Array> wrappedEntries = v8::Array::New(isolate);
CHECK(!isKeyValue || wrappedEntries->Length() % 2 == 0);
if (!wrappedEntries->SetPrototype(context, v8::Null(isolate))
.FromMaybe(false))
return v8::MaybeLocal<v8::Array>();
for (uint32_t i = 0; i < entries->Length(); i += isKeyValue ? 2 : 1) {
v8::Local<v8::Value> item;
if (!entries->Get(context, i).ToLocal(&item)) continue;
v8::Local<v8::Value> value;
if (isKeyValue && !entries->Get(context, i + 1).ToLocal(&value)) continue;
v8::Local<v8::Object> wrapper = v8::Object::New(isolate);
if (!wrapper->SetPrototype(context, v8::Null(isolate)).FromMaybe(false))
continue;
createDataProperty(
context, wrapper,
toV8StringInternalized(isolate, isKeyValue ? "key" : "value"), item);
if (isKeyValue) {
createDataProperty(context, wrapper,
toV8StringInternalized(isolate, "value"), value);
}
if (!addInternalObject(context, wrapper, V8InternalValueType::kEntry)) {
continue;
}
createDataProperty(context, wrappedEntries, wrappedEntries->Length(),
wrapper);
}
return wrappedEntries;
}
v8::MaybeLocal<v8::Uint32> V8Debugger::stableObjectId(
v8::Local<v8::Context> context, v8::Local<v8::Value> value) {
DCHECK(value->IsObject());
......@@ -781,25 +747,6 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
createDataProperty(context, properties, properties->Length(), id);
}
}
if (value->IsFunction()) {
v8::Local<v8::Function> function = value.As<v8::Function>();
v8::Local<v8::Object> location;
if (buildLocation(context, function->ScriptId(),
function->GetScriptLineNumber(),
function->GetScriptColumnNumber())
.ToLocal(&location)) {
createDataProperty(
context, properties, properties->Length(),
toV8StringInternalized(m_isolate, "[[FunctionLocation]]"));
createDataProperty(context, properties, properties->Length(), location);
}
if (function->IsGeneratorFunction()) {
createDataProperty(context, properties, properties->Length(),
toV8StringInternalized(m_isolate, "[[IsGenerator]]"));
createDataProperty(context, properties, properties->Length(),
v8::True(m_isolate));
}
}
v8::Local<v8::Array> entries;
if (collectionsEntries(context, value).ToLocal(&entries)) {
createDataProperty(context, properties, properties->Length(),
......@@ -807,13 +754,6 @@ v8::MaybeLocal<v8::Array> V8Debugger::internalProperties(
createDataProperty(context, properties, properties->Length(), entries);
}
if (value->IsGeneratorObject()) {
v8::Local<v8::Object> location;
if (generatorObjectLocation(context, value).ToLocal(&location)) {
createDataProperty(
context, properties, properties->Length(),
toV8StringInternalized(m_isolate, "[[GeneratorLocation]]"));
createDataProperty(context, properties, properties->Length(), location);
}
v8::Local<v8::Value> scopes;
if (generatorScopes(context, value).ToLocal(&scopes)) {
createDataProperty(context, properties, properties->Length(),
......@@ -1196,6 +1136,31 @@ std::pair<int64_t, int64_t> V8Debugger::debuggerIdFor(
return std::make_pair(0, 0);
}
bool V8Debugger::addInternalObject(v8::Local<v8::Context> context,
v8::Local<v8::Object> object,
V8InternalValueType type) {
if (m_internalObjects.IsEmpty()) {
m_internalObjects.Reset(m_isolate, v8::debug::WeakMap::New(m_isolate));
}
return !m_internalObjects.Get(m_isolate)
->Set(context, object,
v8::Integer::New(m_isolate, static_cast<int>(type)))
.IsEmpty();
}
V8InternalValueType V8Debugger::getInternalType(v8::Local<v8::Context> context,
v8::Local<v8::Object> object) {
if (m_internalObjects.IsEmpty()) return V8InternalValueType::kNone;
v8::Local<v8::Value> typeValue;
if (!m_internalObjects.Get(m_isolate)
->Get(context, object)
.ToLocal(&typeValue) ||
!typeValue->IsUint32()) {
return V8InternalValueType::kNone;
}
return static_cast<V8InternalValueType>(typeValue.As<v8::Int32>()->Value());
}
void V8Debugger::dumpAsyncTaskStacksStateForTest() {
fprintf(stdout, "Async stacks count: %d\n", m_asyncStacksCount);
fprintf(stdout, "Scheduled async tasks: %zu\n", m_asyncTaskStacks.size());
......
......@@ -30,6 +30,9 @@ class V8InspectorImpl;
class V8StackTraceImpl;
struct V8StackTraceId;
enum class WrapMode { kForceValue, kNoPreview, kWithPreview };
enum class V8InternalValueType { kNone, kEntry, kScope, kScopeList };
using protocol::Response;
using ScheduleStepIntoAsyncCallback =
protocol::Debugger::Backend::ScheduleStepIntoAsyncCallback;
......@@ -134,6 +137,12 @@ class V8Debugger : public v8::debug::DebugDelegate,
std::shared_ptr<AsyncStackTrace> stackTraceFor(int contextGroupId,
const V8StackTraceId& id);
bool addInternalObject(v8::Local<v8::Context> context,
v8::Local<v8::Object> object,
V8InternalValueType type);
V8InternalValueType getInternalType(v8::Local<v8::Context> context,
v8::Local<v8::Object> object);
private:
void clearContinueToLocation();
bool shouldContinueToCurrentLocation();
......@@ -160,6 +169,8 @@ class V8Debugger : public v8::debug::DebugDelegate,
v8::Local<v8::Function>);
v8::MaybeLocal<v8::Value> generatorScopes(v8::Local<v8::Context>,
v8::Local<v8::Value>);
v8::MaybeLocal<v8::Array> collectionsEntries(v8::Local<v8::Context> context,
v8::Local<v8::Value> value);
void asyncTaskScheduledForStack(const String16& taskName, void* task,
bool recurring);
......@@ -253,6 +264,8 @@ class V8Debugger : public v8::debug::DebugDelegate,
uint32_t m_lastStableObjectId = 0;
v8::Global<v8::debug::WeakMap> m_stableObjectId;
v8::Global<v8::debug::WeakMap> m_internalObjects;
WasmTranslation m_wasmTranslation;
DISALLOW_COPY_AND_ASSIGN(V8Debugger);
......
/*
* Copyright (C) 2009 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. 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
* OWNER 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.
*/
#include "src/inspector/v8-function-call.h"
#include "src/inspector/inspected-context.h"
#include "src/inspector/string-util.h"
#include "src/inspector/v8-debugger.h"
#include "src/inspector/v8-inspector-impl.h"
#include "include/v8-inspector.h"
namespace v8_inspector {
V8FunctionCall::V8FunctionCall(V8InspectorImpl* inspector,
v8::Local<v8::Context> context,
v8::Local<v8::Value> value, const String16& name)
: m_inspector(inspector),
m_context(context),
m_name(toV8String(context->GetIsolate(), name)),
m_value(value) {}
void V8FunctionCall::appendArgument(v8::Local<v8::Value> value) {
m_arguments.push_back(value);
}
void V8FunctionCall::appendArgument(const String16& argument) {
m_arguments.push_back(toV8String(m_context->GetIsolate(), argument));
}
void V8FunctionCall::appendArgument(int argument) {
m_arguments.push_back(v8::Number::New(m_context->GetIsolate(), argument));
}
void V8FunctionCall::appendArgument(bool argument) {
m_arguments.push_back(argument ? v8::True(m_context->GetIsolate())
: v8::False(m_context->GetIsolate()));
}
v8::Local<v8::Value> V8FunctionCall::call(bool& hadException,
bool reportExceptions) {
v8::TryCatch tryCatch(m_context->GetIsolate());
tryCatch.SetVerbose(reportExceptions);
v8::Local<v8::Value> result = callWithoutExceptionHandling();
hadException = tryCatch.HasCaught();
return result;
}
v8::Local<v8::Value> V8FunctionCall::callWithoutExceptionHandling() {
v8::Context::Scope contextScope(m_context);
v8::Local<v8::Object> thisObject = v8::Local<v8::Object>::Cast(m_value);
v8::Local<v8::Value> value;
if (!thisObject->Get(m_context, m_name).ToLocal(&value))
return v8::Local<v8::Value>();
DCHECK(value->IsFunction());
v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(value);
std::unique_ptr<v8::Local<v8::Value>[]> info(
new v8::Local<v8::Value>[m_arguments.size()]);
for (size_t i = 0; i < m_arguments.size(); ++i) {
info[i] = m_arguments[i];
DCHECK(!info[i].IsEmpty());
}
int contextGroupId = m_inspector->contextGroupId(m_context);
if (contextGroupId) {
m_inspector->client()->muteMetrics(contextGroupId);
m_inspector->muteExceptions(contextGroupId);
}
v8::MicrotasksScope microtasksScope(m_context->GetIsolate(),
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Isolate::AllowJavascriptExecutionScope(m_context->GetIsolate());
v8::MaybeLocal<v8::Value> maybeResult = function->Call(
m_context, thisObject, static_cast<int>(m_arguments.size()), info.get());
if (contextGroupId) {
m_inspector->client()->unmuteMetrics(contextGroupId);
m_inspector->unmuteExceptions(contextGroupId);
}
v8::Local<v8::Value> result;
if (!maybeResult.ToLocal(&result)) return v8::Local<v8::Value>();
return result;
}
} // namespace v8_inspector
/*
* Copyright (C) 2009 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. 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
* OWNER 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.
*/
#ifndef V8_INSPECTOR_V8_FUNCTION_CALL_H_
#define V8_INSPECTOR_V8_FUNCTION_CALL_H_
#include "src/inspector/string-16.h"
#include "include/v8.h"
namespace v8_inspector {
class V8InspectorImpl;
class V8FunctionCall {
public:
V8FunctionCall(V8InspectorImpl*, v8::Local<v8::Context>, v8::Local<v8::Value>,
const String16& name);
void appendArgument(v8::Local<v8::Value>);
void appendArgument(const String16&);
void appendArgument(int);
void appendArgument(bool);
v8::Local<v8::Value> call(bool& hadException, bool reportExceptions = true);
v8::Local<v8::Value> callWithoutExceptionHandling();
protected:
V8InspectorImpl* m_inspector;
v8::Local<v8::Context> m_context;
std::vector<v8::Local<v8::Value>> m_arguments;
v8::Local<v8::String> m_name;
v8::Local<v8::Value> m_value;
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8_FUNCTION_CALL_H_
// Copyright 2015 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.
#include "src/inspector/v8-injected-script-host.h"
#include "src/base/macros.h"
#include "src/debug/debug-interface.h"
#include "src/inspector/injected-script.h"
#include "src/inspector/string-util.h"
#include "src/inspector/v8-debugger.h"
#include "src/inspector/v8-inspector-impl.h"
#include "src/inspector/v8-internal-value-type.h"
#include "src/inspector/v8-value-utils.h"
#include "include/v8-inspector.h"
namespace v8_inspector {
namespace {
void setFunctionProperty(v8::Local<v8::Context> context,
v8::Local<v8::Object> obj, const char* name,
v8::FunctionCallback callback,
v8::Local<v8::External> external) {
v8::Local<v8::String> funcName =
toV8StringInternalized(context->GetIsolate(), name);
v8::Local<v8::Function> func;
if (!v8::Function::New(context, callback, external, 0,
v8::ConstructorBehavior::kThrow)
.ToLocal(&func))
return;
func->SetName(funcName);
createDataProperty(context, obj, funcName, func);
}
V8InspectorImpl* unwrapInspector(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK(!info.Data().IsEmpty());
DCHECK(info.Data()->IsExternal());
V8InspectorImpl* inspector =
static_cast<V8InspectorImpl*>(info.Data().As<v8::External>()->Value());
DCHECK(inspector);
return inspector;
}
template <typename TypedArray>
void addTypedArrayProperty(std::vector<v8::Local<v8::Value>>* props,
v8::Isolate* isolate,
v8::Local<v8::ArrayBuffer> arraybuffer,
String16 name, size_t length) {
props->push_back(toV8String(isolate, name));
props->push_back(TypedArray::New(arraybuffer, 0, length));
}
} // namespace
v8::Local<v8::Object> V8InjectedScriptHost::create(
v8::Local<v8::Context> context, V8InspectorImpl* inspector) {
v8::Isolate* isolate = inspector->isolate();
v8::Local<v8::Object> injectedScriptHost = v8::Object::New(isolate);
bool success = injectedScriptHost->SetPrototype(context, v8::Null(isolate))
.FromMaybe(false);
DCHECK(success);
USE(success);
v8::Local<v8::External> debuggerExternal =
v8::External::New(isolate, inspector);
setFunctionProperty(context, injectedScriptHost, "nullifyPrototype",
V8InjectedScriptHost::nullifyPrototypeCallback,
debuggerExternal);
setFunctionProperty(context, injectedScriptHost, "getProperty",
V8InjectedScriptHost::getPropertyCallback,
debuggerExternal);
setFunctionProperty(context, injectedScriptHost, "internalConstructorName",
V8InjectedScriptHost::internalConstructorNameCallback,
debuggerExternal);
setFunctionProperty(
context, injectedScriptHost, "formatAccessorsAsProperties",
V8InjectedScriptHost::formatAccessorsAsProperties, debuggerExternal);
setFunctionProperty(context, injectedScriptHost, "subtype",
V8InjectedScriptHost::subtypeCallback, debuggerExternal);
setFunctionProperty(context, injectedScriptHost, "getInternalProperties",
V8InjectedScriptHost::getInternalPropertiesCallback,
debuggerExternal);
setFunctionProperty(context, injectedScriptHost, "objectHasOwnProperty",
V8InjectedScriptHost::objectHasOwnPropertyCallback,
debuggerExternal);
setFunctionProperty(context, injectedScriptHost, "bind",
V8InjectedScriptHost::bindCallback, debuggerExternal);
setFunctionProperty(context, injectedScriptHost, "proxyTargetValue",
V8InjectedScriptHost::proxyTargetValueCallback,
debuggerExternal);
setFunctionProperty(context, injectedScriptHost, "nativeAccessorDescriptor",
V8InjectedScriptHost::nativeAccessorDescriptorCallback,
debuggerExternal);
setFunctionProperty(context, injectedScriptHost, "typedArrayProperties",
V8InjectedScriptHost::typedArrayPropertiesCallback,
debuggerExternal);
createDataProperty(context, injectedScriptHost,
toV8StringInternalized(isolate, "keys"),
v8::debug::GetBuiltin(isolate, v8::debug::kObjectKeys));
createDataProperty(
context, injectedScriptHost,
toV8StringInternalized(isolate, "getPrototypeOf"),
v8::debug::GetBuiltin(isolate, v8::debug::kObjectGetPrototypeOf));
createDataProperty(
context, injectedScriptHost,
toV8StringInternalized(isolate, "getOwnPropertyDescriptor"),
v8::debug::GetBuiltin(isolate,
v8::debug::kObjectGetOwnPropertyDescriptor));
createDataProperty(
context, injectedScriptHost,
toV8StringInternalized(isolate, "getOwnPropertyNames"),
v8::debug::GetBuiltin(isolate, v8::debug::kObjectGetOwnPropertyNames));
createDataProperty(
context, injectedScriptHost,
toV8StringInternalized(isolate, "getOwnPropertySymbols"),
v8::debug::GetBuiltin(isolate, v8::debug::kObjectGetOwnPropertySymbols));
return injectedScriptHost;
}
void V8InjectedScriptHost::nullifyPrototypeCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
CHECK_EQ(1, info.Length());
DCHECK(info[0]->IsObject());
if (!info[0]->IsObject()) return;
v8::Isolate* isolate = info.GetIsolate();
info[0]
.As<v8::Object>()
->SetPrototype(isolate->GetCurrentContext(), v8::Null(isolate))
.ToChecked();
}
void V8InjectedScriptHost::getPropertyCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
CHECK(info.Length() == 2 && info[1]->IsString());
if (!info[0]->IsObject()) return;
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::TryCatch tryCatch(isolate);
v8::Isolate::DisallowJavascriptExecutionScope throwJs(
isolate, v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
v8::Local<v8::Value> property;
if (info[0]
.As<v8::Object>()
->Get(context, v8::Local<v8::String>::Cast(info[1]))
.ToLocal(&property)) {
info.GetReturnValue().Set(property);
}
}
void V8InjectedScriptHost::internalConstructorNameCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() < 1 || !info[0]->IsObject()) return;
v8::Local<v8::Object> object = info[0].As<v8::Object>();
info.GetReturnValue().Set(object->GetConstructorName());
}
void V8InjectedScriptHost::formatAccessorsAsProperties(
const v8::FunctionCallbackInfo<v8::Value>& info) {
DCHECK_EQ(info.Length(), 2);
info.GetReturnValue().Set(false);
if (!info[1]->IsFunction()) return;
// Check that function is user-defined.
if (info[1].As<v8::Function>()->ScriptId() != v8::UnboundScript::kNoScriptId)
return;
info.GetReturnValue().Set(
unwrapInspector(info)->client()->formatAccessorsAsProperties(info[0]));
}
void V8InjectedScriptHost::subtypeCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() < 1) return;
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Value> value = info[0];
if (value->IsObject()) {
v8::Local<v8::Value> internalType = v8InternalValueTypeFrom(
isolate->GetCurrentContext(), v8::Local<v8::Object>::Cast(value));
if (internalType->IsString()) {
info.GetReturnValue().Set(internalType);
return;
}
}
if (value->IsArray() || value->IsArgumentsObject()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "array"));
return;
}
if (value->IsTypedArray()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "typedarray"));
return;
}
if (value->IsDate()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "date"));
return;
}
if (value->IsRegExp()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "regexp"));
return;
}
if (value->IsMap()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "map"));
return;
}
if (value->IsWeakMap()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "weakmap"));
return;
}
if (value->IsSet()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "set"));
return;
}
if (value->IsWeakSet()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "weakset"));
return;
}
if (value->IsMapIterator() || value->IsSetIterator()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "iterator"));
return;
}
if (value->IsGeneratorObject()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "generator"));
return;
}
if (value->IsNativeError()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "error"));
return;
}
if (value->IsProxy()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "proxy"));
return;
}
if (value->IsPromise()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "promise"));
return;
}
if (value->IsArrayBuffer() || value->IsSharedArrayBuffer()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "arraybuffer"));
return;
}
if (value->IsDataView()) {
info.GetReturnValue().Set(toV8StringInternalized(isolate, "dataview"));
return;
}
std::unique_ptr<StringBuffer> subtype =
unwrapInspector(info)->client()->valueSubtype(value);
if (subtype) {
info.GetReturnValue().Set(toV8String(isolate, subtype->string()));
return;
}
}
void V8InjectedScriptHost::getInternalPropertiesCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() < 1) return;
std::unordered_set<String16> allowedProperties;
if (info[0]->IsBooleanObject() || info[0]->IsNumberObject() ||
info[0]->IsStringObject() || info[0]->IsSymbolObject() ||
info[0]->IsBigIntObject()) {
allowedProperties.insert(String16("[[PrimitiveValue]]"));
} else if (info[0]->IsPromise()) {
allowedProperties.insert(String16("[[PromiseStatus]]"));
allowedProperties.insert(String16("[[PromiseValue]]"));
} else if (info[0]->IsGeneratorObject()) {
allowedProperties.insert(String16("[[GeneratorStatus]]"));
} else if (info[0]->IsMap() || info[0]->IsWeakMap() || info[0]->IsSet() ||
info[0]->IsWeakSet() || info[0]->IsMapIterator() ||
info[0]->IsSetIterator()) {
allowedProperties.insert(String16("[[Entries]]"));
}
if (!allowedProperties.size()) return;
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Array> allProperties;
if (!unwrapInspector(info)
->debugger()
->internalProperties(isolate->GetCurrentContext(), info[0])
.ToLocal(&allProperties) ||
!allProperties->IsArray() || allProperties->Length() % 2 != 0)
return;
{
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::TryCatch tryCatch(isolate);
v8::Isolate::DisallowJavascriptExecutionScope throwJs(
isolate,
v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
v8::Local<v8::Array> properties = v8::Array::New(isolate);
if (tryCatch.HasCaught()) return;
uint32_t outputIndex = 0;
for (uint32_t i = 0; i < allProperties->Length(); i += 2) {
v8::Local<v8::Value> key;
if (!allProperties->Get(context, i).ToLocal(&key)) continue;
if (tryCatch.HasCaught()) {
tryCatch.Reset();
continue;
}
String16 keyString = toProtocolStringWithTypeCheck(isolate, key);
if (keyString.isEmpty() ||
allowedProperties.find(keyString) == allowedProperties.end())
continue;
v8::Local<v8::Value> value;
if (!allProperties->Get(context, i + 1).ToLocal(&value)) continue;
if (tryCatch.HasCaught()) {
tryCatch.Reset();
continue;
}
createDataProperty(context, properties, outputIndex++, key);
createDataProperty(context, properties, outputIndex++, value);
}
info.GetReturnValue().Set(properties);
}
}
void V8InjectedScriptHost::objectHasOwnPropertyCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() < 2 || !info[0]->IsObject() || !info[1]->IsString()) return;
bool result = info[0]
.As<v8::Object>()
->HasOwnProperty(info.GetIsolate()->GetCurrentContext(),
v8::Local<v8::String>::Cast(info[1]))
.FromMaybe(false);
info.GetReturnValue().Set(v8::Boolean::New(info.GetIsolate(), result));
}
void V8InjectedScriptHost::bindCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() < 2 || !info[1]->IsString()) return;
InjectedScript* injectedScript =
InjectedScript::fromInjectedScriptHost(info.GetIsolate(), info.Holder());
if (!injectedScript) return;
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
v8::Local<v8::String> v8groupName =
info[1]->ToString(context).ToLocalChecked();
String16 groupName =
toProtocolStringWithTypeCheck(info.GetIsolate(), v8groupName);
int id = injectedScript->bindObject(info[0], groupName);
info.GetReturnValue().Set(id);
}
void V8InjectedScriptHost::proxyTargetValueCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() != 1 || !info[0]->IsProxy()) {
UNREACHABLE();
return;
}
v8::Local<v8::Value> target = info[0].As<v8::Proxy>();
while (target->IsProxy())
target = v8::Local<v8::Proxy>::Cast(target)->GetTarget();
info.GetReturnValue().Set(target);
}
void V8InjectedScriptHost::nativeAccessorDescriptorCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
if (info.Length() != 2 || !info[0]->IsObject() || !info[1]->IsName()) {
info.GetReturnValue().Set(v8::Undefined(isolate));
return;
}
v8::Local<v8::Context> context = isolate->GetCurrentContext();
int flags = v8::debug::GetNativeAccessorDescriptor(
context, v8::Local<v8::Object>::Cast(info[0]),
v8::Local<v8::Name>::Cast(info[1]));
if (flags == static_cast<int>(v8::debug::NativeAccessorType::None)) {
info.GetReturnValue().Set(v8::Undefined(isolate));
return;
}
bool isBuiltin =
flags & static_cast<int>(v8::debug::NativeAccessorType::IsBuiltin);
bool hasGetter =
flags & static_cast<int>(v8::debug::NativeAccessorType::HasGetter);
bool hasSetter =
flags & static_cast<int>(v8::debug::NativeAccessorType::HasSetter);
v8::Local<v8::Object> result = v8::Object::New(isolate);
result->SetPrototype(context, v8::Null(isolate)).ToChecked();
createDataProperty(context, result, toV8String(isolate, "isBuiltin"),
v8::Boolean::New(isolate, isBuiltin));
createDataProperty(context, result, toV8String(isolate, "hasGetter"),
v8::Boolean::New(isolate, hasGetter));
createDataProperty(context, result, toV8String(isolate, "hasSetter"),
v8::Boolean::New(isolate, hasSetter));
info.GetReturnValue().Set(result);
}
void V8InjectedScriptHost::typedArrayPropertiesCallback(
const v8::FunctionCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
if (info.Length() != 1 || !info[0]->IsArrayBuffer()) return;
v8::TryCatch tryCatch(isolate);
v8::Isolate::DisallowJavascriptExecutionScope throwJs(
isolate, v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
v8::Local<v8::ArrayBuffer> arrayBuffer = info[0].As<v8::ArrayBuffer>();
size_t length = arrayBuffer->ByteLength();
if (length == 0) return;
std::vector<v8::Local<v8::Value>> arrays_vector;
addTypedArrayProperty<v8::Int8Array>(&arrays_vector, isolate, arrayBuffer,
"[[Int8Array]]", length);
addTypedArrayProperty<v8::Uint8Array>(&arrays_vector, isolate, arrayBuffer,
"[[Uint8Array]]", length);
if (length % 2 == 0) {
addTypedArrayProperty<v8::Int16Array>(&arrays_vector, isolate, arrayBuffer,
"[[Int16Array]]", length / 2);
}
if (length % 4 == 0) {
addTypedArrayProperty<v8::Int32Array>(&arrays_vector, isolate, arrayBuffer,
"[[Int32Array]]", length / 4);
}
if (tryCatch.HasCaught()) return;
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Array> arrays =
v8::Array::New(isolate, static_cast<uint32_t>(arrays_vector.size()));
for (uint32_t i = 0; i < static_cast<uint32_t>(arrays_vector.size()); i++)
createDataProperty(context, arrays, i, arrays_vector[i]);
if (tryCatch.HasCaught()) return;
info.GetReturnValue().Set(arrays);
}
} // namespace v8_inspector
// Copyright 2015 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_INSPECTOR_V8_INJECTED_SCRIPT_HOST_H_
#define V8_INSPECTOR_V8_INJECTED_SCRIPT_HOST_H_
#include "include/v8.h"
namespace v8_inspector {
class V8InspectorImpl;
// SECURITY NOTE: Although the InjectedScriptHost is intended for use solely by
// the inspector,
// a reference to the InjectedScriptHost may be leaked to the page being
// inspected. Thus, the
// InjectedScriptHost must never implemment methods that have more power over
// the page than the
// page already has itself (e.g. origin restriction bypasses).
class V8InjectedScriptHost {
public:
// We expect that debugger outlives any JS context and thus
// V8InjectedScriptHost (owned by JS)
// is destroyed before inspector.
static v8::Local<v8::Object> create(v8::Local<v8::Context>, V8InspectorImpl*);
private:
static void nullifyPrototypeCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
static void getPropertyCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void internalConstructorNameCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
static void formatAccessorsAsProperties(
const v8::FunctionCallbackInfo<v8::Value>&);
static void subtypeCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void getInternalPropertiesCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
static void objectHasOwnPropertyCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
static void bindCallback(const v8::FunctionCallbackInfo<v8::Value>&);
static void proxyTargetValueCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
static void nativeAccessorDescriptorCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
static void typedArrayPropertiesCallback(
const v8::FunctionCallbackInfo<v8::Value>&);
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8_INJECTED_SCRIPT_HOST_H_
......@@ -203,12 +203,7 @@ Response V8InspectorSessionImpl::findInjectedScript(
if (!context) return Response::Error("Cannot find context with specified id");
injectedScript = context->getInjectedScript(m_sessionId);
if (!injectedScript) {
if (!context->createInjectedScript(m_sessionId)) {
if (m_inspector->isolate()->IsExecutionTerminating())
return Response::Error("Execution was terminated");
return Response::Error("Cannot access specified execution context");
}
injectedScript = context->getInjectedScript(m_sessionId);
injectedScript = context->createInjectedScript(m_sessionId);
if (m_customObjectFormatterEnabled)
injectedScript->setCustomObjectFormatterEnabled(true);
}
......@@ -285,14 +280,16 @@ V8InspectorSessionImpl::wrapObject(v8::Local<v8::Context> context,
findInjectedScript(InspectedContext::contextId(context), injectedScript);
if (!injectedScript) return nullptr;
std::unique_ptr<protocol::Runtime::RemoteObject> result;
injectedScript->wrapObject(value, groupName, false, generatePreview, &result);
injectedScript->wrapObject(
value, groupName,
generatePreview ? WrapMode::kWithPreview : WrapMode::kNoPreview, &result);
return result;
}
std::unique_ptr<protocol::Runtime::RemoteObject>
V8InspectorSessionImpl::wrapTable(v8::Local<v8::Context> context,
v8::Local<v8::Value> table,
v8::Local<v8::Value> columns) {
v8::Local<v8::Object> table,
v8::MaybeLocal<v8::Array> columns) {
InjectedScript* injectedScript = nullptr;
findInjectedScript(InspectedContext::contextId(context), injectedScript);
if (!injectedScript) return nullptr;
......
......@@ -55,8 +55,8 @@ class V8InspectorSessionImpl : public V8InspectorSession,
v8::Local<v8::Context>, v8::Local<v8::Value>, const String16& groupName,
bool generatePreview);
std::unique_ptr<protocol::Runtime::RemoteObject> wrapTable(
v8::Local<v8::Context>, v8::Local<v8::Value> table,
v8::Local<v8::Value> columns);
v8::Local<v8::Context>, v8::Local<v8::Object> table,
v8::MaybeLocal<v8::Array> columns);
std::vector<std::unique_ptr<protocol::Schema::Domain>> supportedDomainsImpl();
Response unwrapObject(const String16& objectId, v8::Local<v8::Value>*,
v8::Local<v8::Context>*, String16* objectGroup);
......
// 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.
#include "src/inspector/v8-internal-value-type.h"
#include "src/inspector/string-util.h"
namespace v8_inspector {
namespace {
v8::Local<v8::Private> internalSubtypePrivate(v8::Isolate* isolate) {
return v8::Private::ForApi(
isolate,
toV8StringInternalized(isolate, "V8InternalType#internalSubtype"));
}
v8::Local<v8::String> subtypeForInternalType(v8::Isolate* isolate,
V8InternalValueType type) {
switch (type) {
case V8InternalValueType::kEntry:
return toV8StringInternalized(isolate, "internal#entry");
case V8InternalValueType::kLocation:
return toV8StringInternalized(isolate, "internal#location");
case V8InternalValueType::kScope:
return toV8StringInternalized(isolate, "internal#scope");
case V8InternalValueType::kScopeList:
return toV8StringInternalized(isolate, "internal#scopeList");
}
UNREACHABLE();
}
} // namespace
bool markAsInternal(v8::Local<v8::Context> context,
v8::Local<v8::Object> object, V8InternalValueType type) {
v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::Private> privateValue = internalSubtypePrivate(isolate);
v8::Local<v8::String> subtype = subtypeForInternalType(isolate, type);
return object->SetPrivate(context, privateValue, subtype).FromMaybe(false);
}
bool markArrayEntriesAsInternal(v8::Local<v8::Context> context,
v8::Local<v8::Array> array,
V8InternalValueType type) {
v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::Private> privateValue = internalSubtypePrivate(isolate);
v8::Local<v8::String> subtype = subtypeForInternalType(isolate, type);
for (uint32_t i = 0; i < array->Length(); ++i) {
v8::Local<v8::Value> entry;
if (!array->Get(context, i).ToLocal(&entry) || !entry->IsObject())
return false;
if (!entry.As<v8::Object>()
->SetPrivate(context, privateValue, subtype)
.FromMaybe(false))
return false;
}
return true;
}
v8::Local<v8::Value> v8InternalValueTypeFrom(v8::Local<v8::Context> context,
v8::Local<v8::Object> object) {
v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::Private> privateValue = internalSubtypePrivate(isolate);
if (!object->HasPrivate(context, privateValue).FromMaybe(false))
return v8::Null(isolate);
v8::Local<v8::Value> subtypeValue;
if (!object->GetPrivate(context, privateValue).ToLocal(&subtypeValue) ||
!subtypeValue->IsString())
return v8::Null(isolate);
return subtypeValue;
}
} // namespace v8_inspector
// 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_INSPECTOR_V8_INTERNAL_VALUE_TYPE_H_
#define V8_INSPECTOR_V8_INTERNAL_VALUE_TYPE_H_
#include "include/v8.h"
namespace v8_inspector {
enum class V8InternalValueType { kEntry, kLocation, kScope, kScopeList };
bool markAsInternal(v8::Local<v8::Context>, v8::Local<v8::Object>,
V8InternalValueType);
bool markArrayEntriesAsInternal(v8::Local<v8::Context>, v8::Local<v8::Array>,
V8InternalValueType);
v8::Local<v8::Value> v8InternalValueTypeFrom(v8::Local<v8::Context>,
v8::Local<v8::Object>);
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8_INTERNAL_VALUE_TYPE_H_
......@@ -90,14 +90,14 @@ template <typename ProtocolCallback>
bool wrapEvaluateResultAsync(InjectedScript* injectedScript,
v8::MaybeLocal<v8::Value> maybeResultValue,
const v8::TryCatch& tryCatch,
const String16& objectGroup, bool returnByValue,
bool generatePreview, ProtocolCallback* callback) {
const String16& objectGroup, WrapMode wrapMode,
ProtocolCallback* callback) {
std::unique_ptr<RemoteObject> result;
Maybe<protocol::Runtime::ExceptionDetails> exceptionDetails;
Response response = injectedScript->wrapEvaluateResult(
maybeResultValue, tryCatch, objectGroup, returnByValue, generatePreview,
&result, &exceptionDetails);
maybeResultValue, tryCatch, objectGroup, wrapMode, &result,
&exceptionDetails);
if (response.isSuccess()) {
callback->sendSuccess(std::move(result), std::move(exceptionDetails));
return true;
......@@ -110,8 +110,8 @@ void innerCallFunctionOn(
V8InspectorSessionImpl* session, InjectedScript::Scope& scope,
v8::Local<v8::Value> recv, const String16& expression,
Maybe<protocol::Array<protocol::Runtime::CallArgument>> optionalArguments,
bool silent, bool returnByValue, bool generatePreview, bool userGesture,
bool awaitPromise, const String16& objectGroup,
bool silent, WrapMode wrapMode, bool userGesture, bool awaitPromise,
const String16& objectGroup,
std::unique_ptr<V8RuntimeAgentImpl::CallFunctionOnCallback> callback) {
V8InspectorImpl* inspector = session->inspector();
......@@ -159,7 +159,7 @@ void innerCallFunctionOn(
if (scope.tryCatch().HasCaught()) {
wrapEvaluateResultAsync(scope.injectedScript(), maybeFunctionValue,
scope.tryCatch(), objectGroup, false, false,
scope.tryCatch(), objectGroup, WrapMode::kNoPreview,
callback.get());
return;
}
......@@ -189,13 +189,13 @@ void innerCallFunctionOn(
if (!awaitPromise || scope.tryCatch().HasCaught()) {
wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue,
scope.tryCatch(), objectGroup, returnByValue,
generatePreview, callback.get());
scope.tryCatch(), objectGroup, wrapMode,
callback.get());
return;
}
scope.injectedScript()->addPromiseCallback(
session, maybeResultValue, objectGroup, returnByValue, generatePreview,
session, maybeResultValue, objectGroup, wrapMode,
EvaluateCallbackWrapper<V8RuntimeAgentImpl::CallFunctionOnCallback>::wrap(
std::move(callback)));
}
......@@ -284,16 +284,17 @@ void V8RuntimeAgentImpl::evaluate(
return;
}
WrapMode mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
: WrapMode::kNoPreview;
if (returnByValue.fromMaybe(false)) mode = WrapMode::kForceValue;
if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) {
wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue,
scope.tryCatch(), objectGroup.fromMaybe(""),
returnByValue.fromMaybe(false),
generatePreview.fromMaybe(false), callback.get());
scope.tryCatch(), objectGroup.fromMaybe(""), mode,
callback.get());
return;
}
scope.injectedScript()->addPromiseCallback(
m_session, maybeResultValue, objectGroup.fromMaybe(""),
returnByValue.fromMaybe(false), generatePreview.fromMaybe(false),
m_session, maybeResultValue, objectGroup.fromMaybe(""), mode,
EvaluateCallbackWrapper<EvaluateCallback>::wrap(std::move(callback)));
}
......@@ -312,9 +313,11 @@ void V8RuntimeAgentImpl::awaitPromise(
Response::Error("Could not find promise with given id"));
return;
}
WrapMode mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
: WrapMode::kNoPreview;
if (returnByValue.fromMaybe(false)) mode = WrapMode::kForceValue;
scope.injectedScript()->addPromiseCallback(
m_session, scope.object(), scope.objectGroupName(),
returnByValue.fromMaybe(false), generatePreview.fromMaybe(false),
m_session, scope.object(), scope.objectGroupName(), mode,
EvaluateCallbackWrapper<AwaitPromiseCallback>::wrap(std::move(callback)));
}
......@@ -335,6 +338,9 @@ void V8RuntimeAgentImpl::callFunctionOn(
"Either ObjectId or executionContextId must be specified"));
return;
}
WrapMode mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
: WrapMode::kNoPreview;
if (returnByValue.fromMaybe(false)) mode = WrapMode::kForceValue;
if (objectId.isJust()) {
InjectedScript::ObjectScope scope(m_session, objectId.fromJust());
Response response = scope.initialize();
......@@ -342,14 +348,13 @@ void V8RuntimeAgentImpl::callFunctionOn(
callback->sendFailure(response);
return;
}
innerCallFunctionOn(
m_session, scope, scope.object(), expression,
std::move(optionalArguments), silent.fromMaybe(false),
returnByValue.fromMaybe(false), generatePreview.fromMaybe(false),
userGesture.fromMaybe(false), awaitPromise.fromMaybe(false),
objectGroup.isJust() ? objectGroup.fromMaybe(String16())
: scope.objectGroupName(),
std::move(callback));
innerCallFunctionOn(m_session, scope, scope.object(), expression,
std::move(optionalArguments), silent.fromMaybe(false),
mode, userGesture.fromMaybe(false),
awaitPromise.fromMaybe(false),
objectGroup.isJust() ? objectGroup.fromMaybe(String16())
: scope.objectGroupName(),
std::move(callback));
} else {
int contextId = 0;
Response response =
......@@ -365,12 +370,11 @@ void V8RuntimeAgentImpl::callFunctionOn(
callback->sendFailure(response);
return;
}
innerCallFunctionOn(
m_session, scope, scope.context()->Global(), expression,
std::move(optionalArguments), silent.fromMaybe(false),
returnByValue.fromMaybe(false), generatePreview.fromMaybe(false),
userGesture.fromMaybe(false), awaitPromise.fromMaybe(false),
objectGroup.fromMaybe(""), std::move(callback));
innerCallFunctionOn(m_session, scope, scope.context()->Global(), expression,
std::move(optionalArguments), silent.fromMaybe(false),
mode, userGesture.fromMaybe(false),
awaitPromise.fromMaybe(false),
objectGroup.fromMaybe(""), std::move(callback));
}
}
......@@ -397,40 +401,18 @@ Response V8RuntimeAgentImpl::getProperties(
v8::Local<v8::Object> object = scope.object().As<v8::Object>();
response = scope.injectedScript()->getProperties(
object, scope.objectGroupName(), ownProperties.fromMaybe(false),
accessorPropertiesOnly.fromMaybe(false), generatePreview.fromMaybe(false),
accessorPropertiesOnly.fromMaybe(false),
generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
: WrapMode::kNoPreview,
result, exceptionDetails);
if (!response.isSuccess()) return response;
if (exceptionDetails->isJust() || accessorPropertiesOnly.fromMaybe(false))
return Response::OK();
v8::Local<v8::Array> propertiesArray;
if (!m_inspector->debugger()
->internalProperties(scope.context(), scope.object())
.ToLocal(&propertiesArray)) {
return Response::InternalError();
}
std::unique_ptr<protocol::Array<InternalPropertyDescriptor>>
propertiesProtocolArray =
protocol::Array<InternalPropertyDescriptor>::create();
for (uint32_t i = 0; i < propertiesArray->Length(); i += 2) {
v8::Local<v8::Value> name;
if (!propertiesArray->Get(scope.context(), i).ToLocal(&name) ||
!name->IsString()) {
return Response::InternalError();
}
v8::Local<v8::Value> value;
if (!propertiesArray->Get(scope.context(), i + 1).ToLocal(&value))
return Response::InternalError();
std::unique_ptr<RemoteObject> wrappedValue;
protocol::Response response = scope.injectedScript()->wrapObject(
value, scope.objectGroupName(), false, false, &wrappedValue);
if (!response.isSuccess()) return response;
propertiesProtocolArray->addItem(
InternalPropertyDescriptor::create()
.setName(
toProtocolString(m_inspector->isolate(), name.As<v8::String>()))
.setValue(std::move(wrappedValue))
.build());
}
propertiesProtocolArray;
response = scope.injectedScript()->getInternalProperties(
object, scope.objectGroupName(), &propertiesProtocolArray);
if (!response.isSuccess()) return response;
if (propertiesProtocolArray->length())
*internalProperties = std::move(propertiesProtocolArray);
return Response::OK();
......@@ -499,7 +481,7 @@ Response V8RuntimeAgentImpl::compileScript(
if (!isOk) {
if (scope.tryCatch().HasCaught()) {
response = scope.injectedScript()->createExceptionDetails(
scope.tryCatch(), String16(), false, exceptionDetails);
scope.tryCatch(), String16(), WrapMode::kNoPreview, exceptionDetails);
if (!response.isSuccess()) return response;
return Response::OK();
} else {
......@@ -577,17 +559,18 @@ void V8RuntimeAgentImpl::runScript(
return;
}
WrapMode mode = generatePreview.fromMaybe(false) ? WrapMode::kWithPreview
: WrapMode::kNoPreview;
if (returnByValue.fromMaybe(false)) mode = WrapMode::kForceValue;
if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) {
wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue,
scope.tryCatch(), objectGroup.fromMaybe(""),
returnByValue.fromMaybe(false),
generatePreview.fromMaybe(false), callback.get());
scope.tryCatch(), objectGroup.fromMaybe(""), mode,
callback.get());
return;
}
scope.injectedScript()->addPromiseCallback(
m_session, maybeResultValue.ToLocalChecked(),
objectGroup.fromMaybe(""), returnByValue.fromMaybe(false),
generatePreview.fromMaybe(false),
m_session, maybeResultValue.ToLocalChecked(), objectGroup.fromMaybe(""),
mode,
EvaluateCallbackWrapper<RunScriptCallback>::wrap(std::move(callback)));
}
......@@ -603,8 +586,8 @@ Response V8RuntimeAgentImpl::queryObjects(
v8::Local<v8::Array> resultArray = m_inspector->debugger()->queryObjects(
scope.context(), v8::Local<v8::Object>::Cast(scope.object()));
return scope.injectedScript()->wrapObject(
resultArray, objectGroup.fromMaybe(scope.objectGroupName()), false, false,
objects);
resultArray, objectGroup.fromMaybe(scope.objectGroupName()),
WrapMode::kNoPreview, objects);
}
Response V8RuntimeAgentImpl::globalLexicalScopeNames(
......
......@@ -6,102 +6,6 @@
namespace v8_inspector {
namespace {
protocol::Response toProtocolValue(v8::Local<v8::Context> context,
v8::Local<v8::Value> value, int maxDepth,
std::unique_ptr<protocol::Value>* result) {
using protocol::Response;
if (value.IsEmpty()) {
UNREACHABLE();
}
if (!maxDepth) return Response::Error("Object reference chain is too long");
maxDepth--;
if (value->IsNull() || value->IsUndefined()) {
*result = protocol::Value::null();
return Response::OK();
}
if (value->IsBoolean()) {
*result =
protocol::FundamentalValue::create(value.As<v8::Boolean>()->Value());
return Response::OK();
}
if (value->IsNumber()) {
double doubleValue = value.As<v8::Number>()->Value();
int intValue = static_cast<int>(doubleValue);
if (intValue == doubleValue) {
*result = protocol::FundamentalValue::create(intValue);
return Response::OK();
}
*result = protocol::FundamentalValue::create(doubleValue);
return Response::OK();
}
if (value->IsString()) {
*result = protocol::StringValue::create(
toProtocolString(context->GetIsolate(), value.As<v8::String>()));
return Response::OK();
}
if (value->IsArray()) {
v8::Local<v8::Array> array = value.As<v8::Array>();
std::unique_ptr<protocol::ListValue> inspectorArray =
protocol::ListValue::create();
uint32_t length = array->Length();
for (uint32_t i = 0; i < length; i++) {
v8::Local<v8::Value> value;
if (!array->Get(context, i).ToLocal(&value))
return Response::InternalError();
std::unique_ptr<protocol::Value> element;
Response response = toProtocolValue(context, value, maxDepth, &element);
if (!response.isSuccess()) return response;
inspectorArray->pushValue(std::move(element));
}
*result = std::move(inspectorArray);
return Response::OK();
}
if (value->IsObject()) {
std::unique_ptr<protocol::DictionaryValue> jsonObject =
protocol::DictionaryValue::create();
v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value);
v8::Local<v8::Array> propertyNames;
if (!object->GetPropertyNames(context).ToLocal(&propertyNames))
return Response::InternalError();
uint32_t length = propertyNames->Length();
for (uint32_t i = 0; i < length; i++) {
v8::Local<v8::Value> name;
if (!propertyNames->Get(context, i).ToLocal(&name))
return Response::InternalError();
// FIXME(yurys): v8::Object should support GetOwnPropertyNames
if (name->IsString()) {
v8::Maybe<bool> hasRealNamedProperty = object->HasRealNamedProperty(
context, v8::Local<v8::String>::Cast(name));
if (hasRealNamedProperty.IsNothing() ||
!hasRealNamedProperty.FromJust())
continue;
}
v8::Local<v8::String> propertyName;
if (!name->ToString(context).ToLocal(&propertyName)) continue;
v8::Local<v8::Value> property;
if (!object->Get(context, name).ToLocal(&property))
return Response::InternalError();
if (property->IsUndefined()) continue;
std::unique_ptr<protocol::Value> propertyValue;
Response response =
toProtocolValue(context, property, maxDepth, &propertyValue);
if (!response.isSuccess()) return response;
jsonObject->setValue(
toProtocolString(context->GetIsolate(), propertyName),
std::move(propertyValue));
}
*result = std::move(jsonObject);
return Response::OK();
}
return Response::Error("Object couldn't be returned by value");
}
} // namespace
v8::Maybe<bool> createDataProperty(v8::Local<v8::Context> context,
v8::Local<v8::Object> object,
v8::Local<v8::Name> key,
......@@ -122,11 +26,4 @@ v8::Maybe<bool> createDataProperty(v8::Local<v8::Context> context,
v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
return array->CreateDataProperty(context, index, value);
}
protocol::Response toProtocolValue(v8::Local<v8::Context> context,
v8::Local<v8::Value> value,
std::unique_ptr<protocol::Value>* result) {
return toProtocolValue(context, value, 1000, result);
}
} // namespace v8_inspector
......@@ -18,9 +18,6 @@ v8::Maybe<bool> createDataProperty(v8::Local<v8::Context>,
v8::Maybe<bool> createDataProperty(v8::Local<v8::Context>, v8::Local<v8::Array>,
int index, v8::Local<v8::Value>);
protocol::Response toProtocolValue(v8::Local<v8::Context>, v8::Local<v8::Value>,
std::unique_ptr<protocol::Value>* result);
} // namespace v8_inspector
#endif // V8_INSPECTOR_V8_VALUE_UTILS_H_
// 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.
#include "src/inspector/value-mirror.h"
#include <algorithm>
#include <cmath>
#include "src/debug/debug-interface.h"
#include "src/inspector/v8-debugger.h"
#include "src/inspector/v8-inspector-impl.h"
#include "src/inspector/v8-value-utils.h"
namespace v8_inspector {
using protocol::Response;
using protocol::Runtime::RemoteObject;
using protocol::Runtime::ObjectPreview;
using protocol::Runtime::PropertyPreview;
using protocol::Runtime::EntryPreview;
using protocol::Runtime::InternalPropertyDescriptor;
namespace {
V8InspectorClient* clientFor(v8::Local<v8::Context> context) {
return static_cast<V8InspectorImpl*>(
v8::debug::GetInspector(context->GetIsolate()))
->client();
}
V8InternalValueType v8InternalValueTypeFrom(v8::Local<v8::Context> context,
v8::Local<v8::Value> value) {
if (!value->IsObject()) return V8InternalValueType::kNone;
V8Debugger* debugger = static_cast<V8InspectorImpl*>(
v8::debug::GetInspector(context->GetIsolate()))
->debugger();
return debugger->getInternalType(context, value.As<v8::Object>());
}
Response toProtocolValue(v8::Local<v8::Context> context,
v8::Local<v8::Value> value, int maxDepth,
std::unique_ptr<protocol::Value>* result) {
if (!maxDepth) return Response::Error("Object reference chain is too long");
maxDepth--;
if (value->IsNull() || value->IsUndefined()) {
*result = protocol::Value::null();
return Response::OK();
}
if (value->IsBoolean()) {
*result =
protocol::FundamentalValue::create(value.As<v8::Boolean>()->Value());
return Response::OK();
}
if (value->IsNumber()) {
double doubleValue = value.As<v8::Number>()->Value();
int intValue = static_cast<int>(doubleValue);
if (intValue == doubleValue) {
*result = protocol::FundamentalValue::create(intValue);
return Response::OK();
}
*result = protocol::FundamentalValue::create(doubleValue);
return Response::OK();
}
if (value->IsString()) {
*result = protocol::StringValue::create(
toProtocolString(context->GetIsolate(), value.As<v8::String>()));
return Response::OK();
}
if (value->IsArray()) {
v8::Local<v8::Array> array = value.As<v8::Array>();
std::unique_ptr<protocol::ListValue> inspectorArray =
protocol::ListValue::create();
uint32_t length = array->Length();
for (uint32_t i = 0; i < length; i++) {
v8::Local<v8::Value> value;
if (!array->Get(context, i).ToLocal(&value))
return Response::InternalError();
std::unique_ptr<protocol::Value> element;
Response response = toProtocolValue(context, value, maxDepth, &element);
if (!response.isSuccess()) return response;
inspectorArray->pushValue(std::move(element));
}
*result = std::move(inspectorArray);
return Response::OK();
}
if (value->IsObject()) {
std::unique_ptr<protocol::DictionaryValue> jsonObject =
protocol::DictionaryValue::create();
v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value);
v8::Local<v8::Array> propertyNames;
if (!object->GetPropertyNames(context).ToLocal(&propertyNames))
return Response::InternalError();
uint32_t length = propertyNames->Length();
for (uint32_t i = 0; i < length; i++) {
v8::Local<v8::Value> name;
if (!propertyNames->Get(context, i).ToLocal(&name))
return Response::InternalError();
// FIXME(yurys): v8::Object should support GetOwnPropertyNames
if (name->IsString()) {
v8::Maybe<bool> hasRealNamedProperty = object->HasRealNamedProperty(
context, v8::Local<v8::String>::Cast(name));
if (hasRealNamedProperty.IsNothing() ||
!hasRealNamedProperty.FromJust())
continue;
}
v8::Local<v8::String> propertyName;
if (!name->ToString(context).ToLocal(&propertyName)) continue;
v8::Local<v8::Value> property;
if (!object->Get(context, name).ToLocal(&property))
return Response::InternalError();
if (property->IsUndefined()) continue;
std::unique_ptr<protocol::Value> propertyValue;
Response response =
toProtocolValue(context, property, maxDepth, &propertyValue);
if (!response.isSuccess()) return response;
jsonObject->setValue(
toProtocolString(context->GetIsolate(), propertyName),
std::move(propertyValue));
}
*result = std::move(jsonObject);
return Response::OK();
}
return Response::Error("Object couldn't be returned by value");
}
Response toProtocolValue(v8::Local<v8::Context> context,
v8::Local<v8::Value> value,
std::unique_ptr<protocol::Value>* result) {
if (value->IsUndefined()) return Response::OK();
return toProtocolValue(context, value, 1000, result);
}
enum AbbreviateMode { kMiddle, kEnd };
String16 abbreviateString(const String16& value, AbbreviateMode mode) {
const size_t maxLength = 100;
if (value.length() <= maxLength) return value;
UChar ellipsis = static_cast<UChar>(0x2026);
if (mode == kMiddle) {
return String16::concat(
value.substring(0, maxLength / 2), String16(&ellipsis, 1),
value.substring(value.length() - maxLength / 2 + 1));
}
return String16::concat(value.substring(0, maxLength - 1), ellipsis);
}
String16 descriptionForSymbol(v8::Local<v8::Context> context,
v8::Local<v8::Symbol> symbol) {
return String16::concat(
"Symbol(",
toProtocolStringWithTypeCheck(context->GetIsolate(), symbol->Name()),
")");
}
String16 descriptionForBigInt(v8::Local<v8::Context> context,
v8::Local<v8::BigInt> value) {
v8::Isolate* isolate = context->GetIsolate();
v8::TryCatch tryCatch(isolate);
v8::Local<v8::String> description;
if (!value->ToString(context).ToLocal(&description)) return String16();
return toProtocolString(isolate, description) + "n";
}
String16 descriptionForPrimitiveType(v8::Local<v8::Context> context,
v8::Local<v8::Value> value) {
if (value->IsUndefined()) return RemoteObject::TypeEnum::Undefined;
if (value->IsNull()) return RemoteObject::SubtypeEnum::Null;
if (value->IsBoolean()) {
return value.As<v8::Boolean>()->Value() ? "true" : "false";
}
if (value->IsString()) {
return toProtocolString(context->GetIsolate(), value.As<v8::String>());
}
UNREACHABLE();
return String16();
}
String16 descriptionForRegExp(v8::Isolate* isolate,
v8::Local<v8::RegExp> value) {
String16Builder description;
description.append('/');
description.append(toProtocolString(isolate, value->GetSource()));
description.append('/');
v8::RegExp::Flags flags = value->GetFlags();
if (flags & v8::RegExp::Flags::kGlobal) description.append('g');
if (flags & v8::RegExp::Flags::kIgnoreCase) description.append('i');
if (flags & v8::RegExp::Flags::kMultiline) description.append('m');
if (flags & v8::RegExp::Flags::kDotAll) description.append('s');
if (flags & v8::RegExp::Flags::kUnicode) description.append('u');
if (flags & v8::RegExp::Flags::kSticky) description.append('y');
return description.toString();
}
enum class ErrorType { kNative, kClient };
String16 descriptionForError(v8::Local<v8::Context> context,
v8::Local<v8::Object> object, ErrorType type) {
v8::Isolate* isolate = context->GetIsolate();
v8::TryCatch tryCatch(isolate);
String16 className = toProtocolString(isolate, object->GetConstructorName());
v8::Local<v8::Value> stackValue;
if (!object->Get(context, toV8String(isolate, "stack"))
.ToLocal(&stackValue) ||
!stackValue->IsString()) {
return String16();
}
String16 stack = toProtocolString(isolate, stackValue.As<v8::String>());
String16 description = stack;
if (type == ErrorType::kClient) {
if (stack.substring(0, className.length()) != className) {
v8::Local<v8::Value> messageValue;
if (!object->Get(context, toV8String(isolate, "message"))
.ToLocal(&messageValue) ||
!messageValue->IsString()) {
return stack;
}
String16 message = toProtocolStringWithTypeCheck(isolate, messageValue);
size_t index = stack.find(message);
String16 stackWithoutMessage =
index != String16::kNotFound
? stack.substring(index + message.length())
: String16();
description = className + ": " + message + stackWithoutMessage;
}
}
return description;
}
String16 descriptionForObject(v8::Isolate* isolate,
v8::Local<v8::Object> object) {
return toProtocolString(isolate, object->GetConstructorName());
}
String16 descriptionForDate(v8::Local<v8::Context> context,
v8::Local<v8::Date> date) {
v8::Isolate* isolate = context->GetIsolate();
v8::TryCatch tryCatch(isolate);
v8::Local<v8::String> description;
if (!date->ToString(context).ToLocal(&description)) {
return descriptionForObject(isolate, date);
}
return toProtocolString(isolate, description);
}
String16 descriptionForScopeList(v8::Local<v8::Array> list) {
return String16::concat(
"Scopes[", String16::fromInteger(static_cast<size_t>(list->Length())),
']');
}
String16 descriptionForScope(v8::Local<v8::Context> context,
v8::Local<v8::Object> object) {
v8::Isolate* isolate = context->GetIsolate();
v8::Local<v8::Value> value;
if (!object->GetRealNamedProperty(context, toV8String(isolate, "description"))
.ToLocal(&value)) {
return String16();
}
return toProtocolStringWithTypeCheck(isolate, value);
}
String16 descriptionForCollection(v8::Isolate* isolate,
v8::Local<v8::Object> object, size_t length) {
String16 className = toProtocolString(isolate, object->GetConstructorName());
return String16::concat(className, '(', String16::fromInteger(length), ')');
}
String16 descriptionForEntry(v8::Local<v8::Context> context,
v8::Local<v8::Object> object) {
v8::Isolate* isolate = context->GetIsolate();
String16 key;
v8::Local<v8::Value> tmp;
if (object->GetRealNamedProperty(context, toV8String(isolate, "key"))
.ToLocal(&tmp)) {
auto wrapper = ValueMirror::create(context, tmp);
if (wrapper) {
std::unique_ptr<ObjectPreview> preview;
int limit = 5;
wrapper->buildEntryPreview(context, false, &limit, &limit, &preview);
if (preview) {
key = preview->getDescription(String16());
if (preview->getType() == RemoteObject::TypeEnum::String) {
key = String16::concat('\"', key, '\"');
}
}
}
}
String16 value;
if (object->GetRealNamedProperty(context, toV8String(isolate, "value"))
.ToLocal(&tmp)) {
auto wrapper = ValueMirror::create(context, tmp);
if (wrapper) {
std::unique_ptr<ObjectPreview> preview;
int limit = 5;
wrapper->buildEntryPreview(context, false, &limit, &limit, &preview);
if (preview) {
value = preview->getDescription(String16());
if (preview->getType() == RemoteObject::TypeEnum::String) {
value = String16::concat('\"', value, '\"');
}
}
}
}
return key.length() ? ("{" + key + " => " + value + "}") : value;
}
String16 descriptionForFunction(v8::Local<v8::Context> context,
v8::Local<v8::Function> value) {
v8::Isolate* isolate = context->GetIsolate();
v8::TryCatch tryCatch(isolate);
v8::Local<v8::String> description;
if (!value->ToString(context).ToLocal(&description)) {
return descriptionForObject(isolate, value);
}
return toProtocolString(isolate, description);
}
class PrimitiveValueMirror final : public ValueMirror {
public:
PrimitiveValueMirror(v8::Local<v8::Value> value, const String16& type)
: m_value(value), m_type(type) {}
v8::Local<v8::Value> v8Value() override { return m_value; }
Response buildRemoteObject(v8::Local<v8::Context> context, WrapMode mode,
std::unique_ptr<RemoteObject>* result) override {
std::unique_ptr<protocol::Value> protocolValue;
toProtocolValue(context, m_value, &protocolValue);
*result = RemoteObject::create()
.setType(m_type)
.setValue(std::move(protocolValue))
.build();
if (m_value->IsNull())
(*result)->setSubtype(RemoteObject::SubtypeEnum::Null);
return Response::OK();
}
void buildEntryPreview(v8::Local<v8::Context> context,
bool generatePreviewForProperties, int* nameLimit,
int* indexLimit,
std::unique_ptr<ObjectPreview>* preview) override {
*preview =
ObjectPreview::create()
.setType(m_type)
.setDescription(descriptionForPrimitiveType(context, m_value))
.setOverflow(false)
.setProperties(protocol::Array<PropertyPreview>::create())
.build();
if (m_value->IsNull())
(*preview)->setSubtype(RemoteObject::SubtypeEnum::Null);
}
void buildPropertyPreview(
v8::Local<v8::Context> context, const String16& name,
std::unique_ptr<PropertyPreview>* preview) override {
*preview = PropertyPreview::create()
.setName(name)
.setValue(abbreviateString(
descriptionForPrimitiveType(context, m_value), kMiddle))
.setType(m_type)
.build();
if (m_value->IsNull())
(*preview)->setSubtype(RemoteObject::SubtypeEnum::Null);
}
private:
v8::Local<v8::Value> m_value;
String16 m_type;
String16 m_subtype;
};
class NumberMirror final : public ValueMirror {
public:
explicit NumberMirror(v8::Local<v8::Number> value) : m_value(value) {}
v8::Local<v8::Value> v8Value() override { return m_value; }
Response buildRemoteObject(v8::Local<v8::Context> context, WrapMode mode,
std::unique_ptr<RemoteObject>* result) override {
bool unserializable = false;
String16 descriptionValue = description(&unserializable);
*result = RemoteObject::create()
.setType(RemoteObject::TypeEnum::Number)
.setDescription(descriptionValue)
.build();
if (unserializable) {
(*result)->setUnserializableValue(descriptionValue);
} else {
(*result)->setValue(protocol::FundamentalValue::create(m_value->Value()));
}
return Response::OK();
}
void buildPropertyPreview(v8::Local<v8::Context> context,
const String16& name,
std::unique_ptr<PropertyPreview>* result) override {
bool unserializable = false;
*result = PropertyPreview::create()
.setName(name)
.setType(RemoteObject::TypeEnum::Number)
.setValue(description(&unserializable))
.build();
}
void buildEntryPreview(v8::Local<v8::Context> context,
bool generatePreviewForProperties, int* nameLimit,
int* indexLimit,
std::unique_ptr<ObjectPreview>* preview) override {
bool unserializable = false;
*preview = ObjectPreview::create()
.setType(RemoteObject::TypeEnum::Number)
.setDescription(description(&unserializable))
.setOverflow(false)
.setProperties(protocol::Array<PropertyPreview>::create())
.build();
}
private:
String16 description(bool* unserializable) {
*unserializable = true;
double rawValue = m_value->Value();
if (std::isnan(rawValue)) return "NaN";
if (rawValue == 0.0 && std::signbit(rawValue)) return "-0";
if (std::isinf(rawValue)) {
return std::signbit(rawValue) ? "-Infinity" : "Infinity";
}
*unserializable = false;
return String16::fromDouble(rawValue);
}
v8::Local<v8::Number> m_value;
};
class BigIntMirror final : public ValueMirror {
public:
explicit BigIntMirror(v8::Local<v8::BigInt> value) : m_value(value) {}
Response buildRemoteObject(v8::Local<v8::Context> context, WrapMode mode,
std::unique_ptr<RemoteObject>* result) override {
String16 description = descriptionForBigInt(context, m_value);
*result = RemoteObject::create()
.setType(RemoteObject::TypeEnum::Bigint)
.setUnserializableValue(description)
.setDescription(description)
.build();
return Response::OK();
}
void buildPropertyPreview(
v8::Local<v8::Context> context, const String16& name,
std::unique_ptr<protocol::Runtime::PropertyPreview>* preview) override {
*preview = PropertyPreview::create()
.setName(name)
.setType(RemoteObject::TypeEnum::Bigint)
.setValue(abbreviateString(
descriptionForBigInt(context, m_value), kMiddle))
.build();
}
void buildEntryPreview(
v8::Local<v8::Context> context, bool generatePreviewForProperties,
int* nameLimit, int* indexLimit,
std::unique_ptr<protocol::Runtime::ObjectPreview>* preview) override {
*preview = ObjectPreview::create()
.setType(RemoteObject::TypeEnum::Bigint)
.setDescription(descriptionForBigInt(context, m_value))
.setOverflow(false)
.setProperties(protocol::Array<PropertyPreview>::create())
.build();
}
v8::Local<v8::Value> v8Value() override { return m_value; }
private:
v8::Local<v8::BigInt> m_value;
};
class SymbolMirror final : public ValueMirror {
public:
explicit SymbolMirror(v8::Local<v8::Value> value)
: m_symbol(value.As<v8::Symbol>()) {}
Response buildRemoteObject(v8::Local<v8::Context> context, WrapMode mode,
std::unique_ptr<RemoteObject>* result) override {
if (mode == WrapMode::kForceValue) {
return Response::Error("Object couldn't be returned by value");
}
*result = RemoteObject::create()
.setType(RemoteObject::TypeEnum::Symbol)
.setDescription(descriptionForSymbol(context, m_symbol))
.build();
return Response::OK();
}
void buildPropertyPreview(
v8::Local<v8::Context> context, const String16& name,
std::unique_ptr<protocol::Runtime::PropertyPreview>* preview) override {
*preview = PropertyPreview::create()
.setName(name)
.setType(RemoteObject::TypeEnum::Symbol)
.setValue(abbreviateString(
descriptionForSymbol(context, m_symbol), kEnd))
.build();
}
v8::Local<v8::Value> v8Value() override { return m_symbol; }
private:
v8::Local<v8::Symbol> m_symbol;
};
class LocationMirror final : public ValueMirror {
public:
static std::unique_ptr<LocationMirror> create(
v8::Local<v8::Function> function) {
return create(function, function->ScriptId(),
function->GetScriptLineNumber(),
function->GetScriptColumnNumber());
}
static std::unique_ptr<LocationMirror> createForGenerator(
v8::Local<v8::Value> value) {
v8::Local<v8::debug::GeneratorObject> generatorObject =
v8::debug::GeneratorObject::Cast(value);
if (!generatorObject->IsSuspended()) {
return create(generatorObject->Function());
}
v8::Local<v8::debug::Script> script;
if (!generatorObject->Script().ToLocal(&script)) return nullptr;
v8::debug::Location suspendedLocation =
generatorObject->SuspendedLocation();
return create(value, script->Id(), suspendedLocation.GetLineNumber(),
suspendedLocation.GetColumnNumber());
}
Response buildRemoteObject(v8::Local<v8::Context> context, WrapMode mode,
std::unique_ptr<RemoteObject>* result) override {
auto location = protocol::DictionaryValue::create();
location->setString("scriptId", String16::fromInteger(m_scriptId));
location->setInteger("lineNumber", m_lineNumber);
location->setInteger("columnNumber", m_columnNumber);
*result = RemoteObject::create()
.setType(RemoteObject::TypeEnum::Object)
.setSubtype("internal#location")
.setDescription("Object")
.setValue(std::move(location))
.build();
return Response::OK();
}
v8::Local<v8::Value> v8Value() override { return m_value; }
private:
static std::unique_ptr<LocationMirror> create(v8::Local<v8::Value> value,
int scriptId, int lineNumber,
int columnNumber) {
if (scriptId == v8::UnboundScript::kNoScriptId) return nullptr;
if (lineNumber == v8::Function::kLineOffsetNotFound ||
columnNumber == v8::Function::kLineOffsetNotFound) {
return nullptr;
}
return std::unique_ptr<LocationMirror>(
new LocationMirror(value, scriptId, lineNumber, columnNumber));
}
LocationMirror(v8::Local<v8::Value> value, int scriptId, int lineNumber,
int columnNumber)
: m_value(value),
m_scriptId(scriptId),
m_lineNumber(lineNumber),
m_columnNumber(columnNumber) {}
v8::Local<v8::Value> m_value;
int m_scriptId;
int m_lineNumber;
int m_columnNumber;
};
class FunctionMirror final : public ValueMirror {
public:
explicit FunctionMirror(v8::Local<v8::Value> value)
: m_value(value.As<v8::Function>()) {}
v8::Local<v8::Value> v8Value() override { return m_value; }
Response buildRemoteObject(v8::Local<v8::Context> context, WrapMode mode,
std::unique_ptr<RemoteObject>* result) override {
// TODO(alph): drop this functionality.
if (mode == WrapMode::kForceValue) {
std::unique_ptr<protocol::Value> protocolValue;
Response response = toProtocolValue(context, m_value, &protocolValue);
if (!response.isSuccess()) return response;
*result = RemoteObject::create()
.setType(RemoteObject::TypeEnum::Function)
.setValue(std::move(protocolValue))
.build();
} else {
*result = RemoteObject::create()
.setType(RemoteObject::TypeEnum::Function)
.setClassName(toProtocolStringWithTypeCheck(
context->GetIsolate(), m_value->GetConstructorName()))
.setDescription(descriptionForFunction(context, m_value))
.build();
}
return Response::OK();
}
void buildPropertyPreview(v8::Local<v8::Context> context,
const String16& name,
std::unique_ptr<PropertyPreview>* result) override {
*result = PropertyPreview::create()
.setName(name)
.setType(RemoteObject::TypeEnum::Function)
.setValue(String16())
.build();
}
void buildEntryPreview(v8::Local<v8::Context> context,
bool generatePreviewForProperties, int* nameLimit,
int* indexLimit,
std::unique_ptr<ObjectPreview>* preview) override {
*preview = ObjectPreview::create()
.setType(RemoteObject::TypeEnum::Function)
.setDescription(descriptionForFunction(context, m_value))
.setOverflow(false)
.setProperties(protocol::Array<PropertyPreview>::create())
.build();
}
private:
v8::Local<v8::Function> m_value;
};
bool isArrayLike(v8::Local<v8::Context> context, v8::Local<v8::Value> value,
size_t* length) {
if (!value->IsObject()) return false;
v8::Isolate* isolate = context->GetIsolate();
v8::TryCatch tryCatch(isolate);
v8::MicrotasksScope microtasksScope(isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::Local<v8::Object> object = value.As<v8::Object>();
v8::Local<v8::Value> spliceValue;
if (!object->IsArgumentsObject() &&
(!object->GetRealNamedProperty(context, toV8String(isolate, "splice"))
.ToLocal(&spliceValue) ||
!spliceValue->IsFunction())) {
return false;
}
v8::Local<v8::Value> lengthValue;
v8::Maybe<bool> result =
object->HasOwnProperty(context, toV8String(isolate, "length"));
if (result.IsNothing()) return false;
if (!result.FromJust() ||
!object->Get(context, toV8String(isolate, "length"))
.ToLocal(&lengthValue) ||
!lengthValue->IsUint32()) {
return false;
}
*length = v8::Local<v8::Uint32>::Cast(lengthValue)->Value();
return true;
}
struct EntryMirror {
std::unique_ptr<ValueMirror> key;
std::unique_ptr<ValueMirror> value;
static bool getEntries(v8::Local<v8::Context> context,
v8::Local<v8::Object> object, size_t limit,
bool* overflow, std::vector<EntryMirror>* mirrors) {
bool isKeyValue = false;
v8::Local<v8::Array> entries;
if (!object->PreviewEntries(&isKeyValue).ToLocal(&entries)) return false;
for (uint32_t i = 0; i < entries->Length(); i += isKeyValue ? 2 : 1) {
v8::Local<v8::Value> tmp;
std::unique_ptr<ValueMirror> keyMirror;
if (isKeyValue && entries->Get(context, i).ToLocal(&tmp)) {
keyMirror = ValueMirror::create(context, tmp);
}
std::unique_ptr<ValueMirror> valueMirror;
if (entries->Get(context, isKeyValue ? i + 1 : i).ToLocal(&tmp)) {
valueMirror = ValueMirror::create(context, tmp);
} else {
continue;
}
if (mirrors->size() == limit) {
*overflow = true;
return true;
}
mirrors->emplace_back(
EntryMirror{std::move(keyMirror), std::move(valueMirror)});
}
return mirrors->size() > 0;
}
};
class PreviewPropertyAccumulator : public ValueMirror::PropertyAccumulator {
public:
PreviewPropertyAccumulator(const std::vector<String16>& blacklist,
int skipIndex, int* nameLimit, int* indexLimit,
bool* overflow,
std::vector<PropertyMirror>* mirrors)
: m_blacklist(blacklist),
m_skipIndex(skipIndex),
m_nameLimit(nameLimit),
m_indexLimit(indexLimit),
m_overflow(overflow),
m_mirrors(mirrors) {}
bool Add(PropertyMirror mirror) override {
if (mirror.exception) return true;
if ((!mirror.getter || !mirror.getter->v8Value()->IsFunction()) &&
!mirror.value) {
return true;
}
if (!mirror.isOwn) return true;
if (std::find(m_blacklist.begin(), m_blacklist.end(), mirror.name) !=
m_blacklist.end()) {
return true;
}
if (mirror.isIndex && m_skipIndex > 0) {
--m_skipIndex;
if (m_skipIndex > 0) return true;
}
int* limit = mirror.isIndex ? m_indexLimit : m_nameLimit;
if (!*limit) {
*m_overflow = true;
return false;
}
--*limit;
m_mirrors->push_back(std::move(mirror));
return true;
}
private:
std::vector<String16> m_blacklist;
int m_skipIndex;
int* m_nameLimit;
int* m_indexLimit;
bool* m_overflow;
std::vector<PropertyMirror>* m_mirrors;
};
bool getPropertiesForPreview(v8::Local<v8::Context> context,
v8::Local<v8::Object> object, int* nameLimit,
int* indexLimit, bool* overflow,
std::vector<PropertyMirror>* properties) {
std::vector<String16> blacklist;
size_t length = 0;
if (object->IsArray() || isArrayLike(context, object, &length) ||
object->IsStringObject()) {
blacklist.push_back("length");
} else {
auto clientSubtype = clientFor(context)->valueSubtype(object);
if (clientSubtype && toString16(clientSubtype->string()) == "array") {
blacklist.push_back("length");
}
}
if (object->IsArrayBuffer() || object->IsSharedArrayBuffer()) {
blacklist.push_back("[[Int8Array]]");
blacklist.push_back("[[Uint8Array]]");
blacklist.push_back("[[Int16Array]]");
blacklist.push_back("[[Int32Array]]");
}
int skipIndex = object->IsStringObject()
? object.As<v8::StringObject>()->ValueOf()->Length() + 1
: -1;
PreviewPropertyAccumulator accumulator(blacklist, skipIndex, nameLimit,
indexLimit, overflow, properties);
return ValueMirror::getProperties(context, object, false, false,
&accumulator);
}
void getInternalPropertiesForPreview(
v8::Local<v8::Context> context, v8::Local<v8::Object> object,
int* nameLimit, bool* overflow,
std::vector<InternalPropertyMirror>* properties) {
std::vector<InternalPropertyMirror> mirrors;
ValueMirror::getInternalProperties(context, object, &mirrors);
std::vector<String16> whitelist;
if (object->IsBooleanObject() || object->IsNumberObject() ||
object->IsStringObject() || object->IsSymbolObject() ||
object->IsBigIntObject()) {
whitelist.emplace_back("[[PrimitiveValue]]");
} else if (object->IsPromise()) {
whitelist.emplace_back("[[PromiseStatus]]");
whitelist.emplace_back("[[PromiseValue]]");
} else if (object->IsGeneratorObject()) {
whitelist.emplace_back("[[GeneratorStatus]]");
}
for (auto& mirror : mirrors) {
if (std::find(whitelist.begin(), whitelist.end(), mirror.name) ==
whitelist.end()) {
continue;
}
if (!*nameLimit) {
*overflow = true;
return;
}
--*nameLimit;
properties->push_back(std::move(mirror));
}
}
class ObjectMirror final : public ValueMirror {
public:
ObjectMirror(v8::Local<v8::Value> value, const String16& description)
: m_value(value.As<v8::Object>()),
m_description(description),
m_hasSubtype(false) {}
ObjectMirror(v8::Local<v8::Value> value, const String16& subtype,
const String16& description)
: m_value(value.As<v8::Object>()),
m_description(description),
m_hasSubtype(true),
m_subtype(subtype) {}
v8::Local<v8::Value> v8Value() override { return m_value; }
Response buildRemoteObject(v8::Local<v8::Context> context, WrapMode mode,
std::unique_ptr<RemoteObject>* result) override {
if (mode == WrapMode::kForceValue) {
std::unique_ptr<protocol::Value> protocolValue;
Response response = toProtocolValue(context, m_value, &protocolValue);
if (!response.isSuccess()) return response;
*result = RemoteObject::create()
.setType(RemoteObject::TypeEnum::Object)
.setValue(std::move(protocolValue))
.build();
} else {
v8::Isolate* isolate = context->GetIsolate();
*result = RemoteObject::create()
.setType(RemoteObject::TypeEnum::Object)
.setClassName(toProtocolString(
isolate, m_value->GetConstructorName()))
.setDescription(m_description)
.build();
if (m_hasSubtype) (*result)->setSubtype(m_subtype);
if (mode == WrapMode::kWithPreview) {
std::unique_ptr<ObjectPreview> previewValue;
int nameLimit = 5;
int indexLimit = 100;
buildObjectPreview(context, false, &nameLimit, &indexLimit,
&previewValue);
(*result)->setPreview(std::move(previewValue));
}
}
return Response::OK();
}
void buildObjectPreview(v8::Local<v8::Context> context,
bool generatePreviewForProperties, int* nameLimit,
int* indexLimit,
std::unique_ptr<ObjectPreview>* result) override {
buildObjectPreviewInternal(context, false /* forEntry */,
generatePreviewForProperties, nameLimit,
indexLimit, result);
}
void buildEntryPreview(v8::Local<v8::Context> context,
bool generatePreviewForProperties, int* nameLimit,
int* indexLimit,
std::unique_ptr<ObjectPreview>* result) override {
buildObjectPreviewInternal(context, true /* forEntry */,
generatePreviewForProperties, nameLimit,
indexLimit, result);
}
void buildPropertyPreview(v8::Local<v8::Context> context,
const String16& name,
std::unique_ptr<PropertyPreview>* result) override {
*result = PropertyPreview::create()
.setName(name)
.setType(RemoteObject::TypeEnum::Object)
.setValue(abbreviateString(
m_description,
m_subtype == RemoteObject::SubtypeEnum::Regexp ? kMiddle
: kEnd))
.build();
if (m_hasSubtype) (*result)->setSubtype(m_subtype);
}
private:
void buildObjectPreviewInternal(v8::Local<v8::Context> context, bool forEntry,
bool generatePreviewForProperties,
int* nameLimit, int* indexLimit,
std::unique_ptr<ObjectPreview>* result) {
std::unique_ptr<protocol::Array<PropertyPreview>> properties =
protocol::Array<PropertyPreview>::create();
std::unique_ptr<protocol::Array<EntryPreview>> entriesPreview;
bool overflow = false;
v8::Local<v8::Value> value = m_value;
while (value->IsProxy()) value = value.As<v8::Proxy>()->GetTarget();
if (value->IsObject() && !value->IsProxy()) {
v8::Local<v8::Object> objectForPreview = value.As<v8::Object>();
std::vector<InternalPropertyMirror> internalProperties;
getInternalPropertiesForPreview(context, objectForPreview, nameLimit,
&overflow, &internalProperties);
for (size_t i = 0; i < internalProperties.size(); ++i) {
std::unique_ptr<PropertyPreview> propertyPreview;
internalProperties[i].value->buildPropertyPreview(
context, internalProperties[i].name, &propertyPreview);
if (propertyPreview) {
properties->addItem(std::move(propertyPreview));
}
}
std::vector<PropertyMirror> mirrors;
if (getPropertiesForPreview(context, objectForPreview, nameLimit,
indexLimit, &overflow, &mirrors)) {
for (size_t i = 0; i < mirrors.size(); ++i) {
std::unique_ptr<PropertyPreview> preview;
std::unique_ptr<ObjectPreview> valuePreview;
if (mirrors[i].value) {
mirrors[i].value->buildPropertyPreview(context, mirrors[i].name,
&preview);
if (generatePreviewForProperties) {
mirrors[i].value->buildObjectPreview(context, false, nameLimit,
indexLimit, &valuePreview);
}
} else {
preview = PropertyPreview::create()
.setName(mirrors[i].name)
.setType(PropertyPreview::TypeEnum::Accessor)
.build();
}
if (valuePreview) {
preview->setValuePreview(std::move(valuePreview));
}
properties->addItem(std::move(preview));
}
}
std::vector<EntryMirror> entries;
if (EntryMirror::getEntries(context, objectForPreview, 5, &overflow,
&entries)) {
if (forEntry) {
overflow = true;
} else {
entriesPreview = protocol::Array<EntryPreview>::create();
for (const auto& entry : entries) {
std::unique_ptr<ObjectPreview> valuePreview;
entry.value->buildEntryPreview(
context, generatePreviewForProperties, nameLimit, indexLimit,
&valuePreview);
if (!valuePreview) continue;
std::unique_ptr<ObjectPreview> keyPreview;
if (entry.key) {
entry.key->buildEntryPreview(context,
generatePreviewForProperties,
nameLimit, indexLimit, &keyPreview);
if (!keyPreview) continue;
}
std::unique_ptr<EntryPreview> entryPreview =
EntryPreview::create()
.setValue(std::move(valuePreview))
.build();
if (keyPreview) entryPreview->setKey(std::move(keyPreview));
entriesPreview->addItem(std::move(entryPreview));
}
}
}
}
*result = ObjectPreview::create()
.setType(RemoteObject::TypeEnum::Object)
.setDescription(m_description)
.setOverflow(overflow)
.setProperties(std::move(properties))
.build();
if (m_hasSubtype) (*result)->setSubtype(m_subtype);
if (entriesPreview) (*result)->setEntries(std::move(entriesPreview));
}
v8::Local<v8::Object> m_value;
String16 m_description;
bool m_hasSubtype;
String16 m_subtype;
};
void nativeGetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
v8::Local<v8::Object> data = info.Data().As<v8::Object>();
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Value> name;
if (!data->GetRealNamedProperty(context, toV8String(isolate, "name"))
.ToLocal(&name)) {
return;
}
v8::Local<v8::Value> object;
if (!data->GetRealNamedProperty(context, toV8String(isolate, "object"))
.ToLocal(&object) ||
!object->IsObject()) {
return;
}
v8::Local<v8::Value> value;
if (!object.As<v8::Object>()->Get(context, name).ToLocal(&value)) return;
info.GetReturnValue().Set(value);
}
std::unique_ptr<ValueMirror> createNativeGetter(v8::Local<v8::Context> context,
v8::Local<v8::Value> object,
v8::Local<v8::Name> name) {
v8::Isolate* isolate = context->GetIsolate();
v8::TryCatch tryCatch(isolate);
v8::Local<v8::Object> data = v8::Object::New(isolate);
if (data->Set(context, toV8String(isolate, "name"), name).IsNothing()) {
return nullptr;
}
if (data->Set(context, toV8String(isolate, "object"), object).IsNothing()) {
return nullptr;
}
v8::Local<v8::Function> function;
if (!v8::Function::New(context, nativeGetterCallback, data, 0,
v8::ConstructorBehavior::kThrow)
.ToLocal(&function)) {
return nullptr;
}
return ValueMirror::create(context, function);
}
void nativeSetterCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
if (info.Length() < 1) return;
v8::Local<v8::Object> data = info.Data().As<v8::Object>();
v8::Isolate* isolate = info.GetIsolate();
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Value> name;
if (!data->GetRealNamedProperty(context, toV8String(isolate, "name"))
.ToLocal(&name)) {
return;
}
v8::Local<v8::Value> object;
if (!data->GetRealNamedProperty(context, toV8String(isolate, "object"))
.ToLocal(&object) ||
!object->IsObject()) {
return;
}
v8::Local<v8::Value> value;
if (!object.As<v8::Object>()->Set(context, name, info[0]).IsNothing()) return;
}
std::unique_ptr<ValueMirror> createNativeSetter(v8::Local<v8::Context> context,
v8::Local<v8::Value> object,
v8::Local<v8::Name> name) {
v8::Isolate* isolate = context->GetIsolate();
v8::TryCatch tryCatch(isolate);
v8::Local<v8::Object> data = v8::Object::New(isolate);
if (data->Set(context, toV8String(isolate, "name"), name).IsNothing()) {
return nullptr;
}
if (data->Set(context, toV8String(isolate, "object"), object).IsNothing()) {
return nullptr;
}
v8::Local<v8::Function> function;
if (!v8::Function::New(context, nativeSetterCallback, data, 1,
v8::ConstructorBehavior::kThrow)
.ToLocal(&function)) {
return nullptr;
}
return ValueMirror::create(context, function);
}
bool doesAttributeHaveObservableSideEffectOnGet(v8::Local<v8::Context> context,
v8::Local<v8::Object> object,
v8::Local<v8::Name> name) {
// TODO(dgozman): we should remove this, annotate more embedder properties as
// side-effect free, and call all getters which do not produce side effects.
if (!name->IsString()) return false;
v8::Isolate* isolate = context->GetIsolate();
if (!name.As<v8::String>()->StringEquals(toV8String(isolate, "body"))) {
return false;
}
v8::TryCatch tryCatch(isolate);
v8::Local<v8::Value> request;
if (context->Global()
->GetRealNamedProperty(context, toV8String(isolate, "Request"))
.ToLocal(&request)) {
if (request->IsObject() &&
object->InstanceOf(context, request.As<v8::Object>())
.FromMaybe(false)) {
return true;
}
}
if (tryCatch.HasCaught()) tryCatch.Reset();
v8::Local<v8::Value> response;
if (context->Global()
->GetRealNamedProperty(context, toV8String(isolate, "Response"))
.ToLocal(&response)) {
if (response->IsObject() &&
object->InstanceOf(context, response.As<v8::Object>())
.FromMaybe(false)) {
return true;
}
}
return false;
}
template <typename ArrayView, typename ArrayBuffer>
void addTypedArrayView(v8::Local<v8::Context> context,
v8::Local<ArrayBuffer> buffer, size_t length,
const char* name,
ValueMirror::PropertyAccumulator* accumulator) {
accumulator->Add(PropertyMirror{
String16(name), false, false, false, true, false,
ValueMirror::create(context, ArrayView::New(buffer, 0, length)), nullptr,
nullptr, nullptr, nullptr});
}
template <typename ArrayBuffer>
void addTypedArrayViews(v8::Local<v8::Context> context,
v8::Local<ArrayBuffer> buffer,
ValueMirror::PropertyAccumulator* accumulator) {
// TODO(alph): these should be internal properties.
size_t length = buffer->ByteLength();
addTypedArrayView<v8::Int8Array>(context, buffer, length, "[[Int8Array]]",
accumulator);
addTypedArrayView<v8::Uint8Array>(context, buffer, length, "[[Uint8Array]]",
accumulator);
if (buffer->ByteLength() % 2 == 0) {
addTypedArrayView<v8::Int16Array>(context, buffer, length / 2,
"[[Int16Array]]", accumulator);
}
if (buffer->ByteLength() % 4 == 0) {
addTypedArrayView<v8::Int32Array>(context, buffer, length / 4,
"[[Int32Array]]", accumulator);
}
}
} // anonymous namespace
ValueMirror::~ValueMirror() = default;
// static
bool ValueMirror::getProperties(v8::Local<v8::Context> context,
v8::Local<v8::Object> object,
bool ownProperties, bool accessorPropertiesOnly,
PropertyAccumulator* accumulator) {
v8::Isolate* isolate = context->GetIsolate();
v8::TryCatch tryCatch(isolate);
v8::Local<v8::Set> set = v8::Set::New(isolate);
v8::MicrotasksScope microtasksScope(isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
V8InternalValueType internalType = v8InternalValueTypeFrom(context, object);
if (internalType == V8InternalValueType::kScope) {
v8::Local<v8::Value> value;
if (!object->Get(context, toV8String(isolate, "object")).ToLocal(&value) ||
!value->IsObject()) {
return false;
} else {
object = value.As<v8::Object>();
}
}
if (internalType == V8InternalValueType::kScopeList) {
if (!set->Add(context, toV8String(isolate, "length")).ToLocal(&set)) {
return false;
}
}
bool shouldSkipProto = internalType == V8InternalValueType::kScopeList;
bool formatAccessorsAsProperties =
clientFor(context)->formatAccessorsAsProperties(object);
if (object->IsArrayBuffer()) {
addTypedArrayViews(context, object.As<v8::ArrayBuffer>(), accumulator);
}
if (object->IsSharedArrayBuffer()) {
addTypedArrayViews(context, object.As<v8::SharedArrayBuffer>(),
accumulator);
}
for (auto iterator = v8::debug::PropertyIterator::Create(object);
!iterator->Done(); iterator->Advance()) {
bool isOwn = iterator->is_own();
if (!isOwn && ownProperties) break;
v8::Local<v8::Name> v8Name = iterator->name();
v8::Maybe<bool> result = set->Has(context, v8Name);
if (result.IsNothing()) return false;
if (result.FromJust()) continue;
if (!set->Add(context, v8Name).ToLocal(&set)) return false;
String16 name;
std::unique_ptr<ValueMirror> symbolMirror;
if (v8Name->IsString()) {
name = toProtocolString(isolate, v8Name.As<v8::String>());
} else {
v8::Local<v8::Symbol> symbol = v8Name.As<v8::Symbol>();
name = descriptionForSymbol(context, symbol);
symbolMirror = ValueMirror::create(context, symbol);
}
v8::PropertyAttribute attributes;
std::unique_ptr<ValueMirror> valueMirror;
std::unique_ptr<ValueMirror> getterMirror;
std::unique_ptr<ValueMirror> setterMirror;
std::unique_ptr<ValueMirror> exceptionMirror;
bool writable = false;
bool enumerable = false;
bool configurable = false;
bool isAccessorProperty = false;
v8::TryCatch tryCatch(isolate);
if (!iterator->attributes().To(&attributes)) {
exceptionMirror = ValueMirror::create(context, tryCatch.Exception());
} else {
if (iterator->is_native_accessor()) {
if (iterator->has_native_getter()) {
getterMirror = createNativeGetter(context, object, v8Name);
}
if (iterator->has_native_setter()) {
setterMirror = createNativeSetter(context, object, v8Name);
}
writable = !(attributes & v8::PropertyAttribute::ReadOnly);
enumerable = !(attributes & v8::PropertyAttribute::DontEnum);
configurable = !(attributes & v8::PropertyAttribute::DontDelete);
isAccessorProperty = getterMirror || setterMirror;
} else {
v8::TryCatch tryCatch(isolate);
v8::debug::PropertyDescriptor descriptor;
if (!iterator->descriptor().To(&descriptor)) {
exceptionMirror = ValueMirror::create(context, tryCatch.Exception());
} else {
writable = descriptor.has_writable ? descriptor.writable : false;
enumerable =
descriptor.has_enumerable ? descriptor.enumerable : false;
configurable =
descriptor.has_configurable ? descriptor.configurable : false;
if (!descriptor.value.IsEmpty()) {
valueMirror = ValueMirror::create(context, descriptor.value);
}
bool getterIsNativeFunction = false;
if (!descriptor.get.IsEmpty()) {
v8::Local<v8::Value> get = descriptor.get;
getterMirror = ValueMirror::create(context, get);
getterIsNativeFunction =
get->IsFunction() && get.As<v8::Function>()->ScriptId() ==
v8::UnboundScript::kNoScriptId;
}
if (!descriptor.set.IsEmpty()) {
setterMirror = ValueMirror::create(context, descriptor.set);
}
isAccessorProperty = getterMirror || setterMirror;
bool isSymbolDescription =
object->IsSymbol() && name == "description";
if (isSymbolDescription ||
(name != "__proto__" && getterIsNativeFunction &&
formatAccessorsAsProperties &&
!doesAttributeHaveObservableSideEffectOnGet(context, object,
v8Name))) {
v8::TryCatch tryCatch(isolate);
v8::Local<v8::Value> value;
if (object->Get(context, v8Name).ToLocal(&value)) {
valueMirror = ValueMirror::create(context, value);
isOwn = true;
setterMirror = nullptr;
getterMirror = nullptr;
}
}
}
}
}
if (accessorPropertiesOnly && !isAccessorProperty) continue;
auto mirror = PropertyMirror{name,
writable,
configurable,
enumerable,
isOwn,
iterator->is_array_index(),
std::move(valueMirror),
std::move(getterMirror),
std::move(setterMirror),
std::move(symbolMirror),
std::move(exceptionMirror)};
if (!accumulator->Add(std::move(mirror))) return true;
}
if (!shouldSkipProto && ownProperties && !object->IsProxy() &&
!accessorPropertiesOnly) {
v8::Local<v8::Value> prototype = object->GetPrototype();
if (prototype->IsObject()) {
accumulator->Add(PropertyMirror{String16("__proto__"), true, true, false,
true, false,
ValueMirror::create(context, prototype),
nullptr, nullptr, nullptr, nullptr});
}
}
return true;
}
// static
void ValueMirror::getInternalProperties(
v8::Local<v8::Context> context, v8::Local<v8::Object> object,
std::vector<InternalPropertyMirror>* mirrors) {
v8::Isolate* isolate = context->GetIsolate();
v8::MicrotasksScope microtasksScope(isolate,
v8::MicrotasksScope::kDoNotRunMicrotasks);
v8::TryCatch tryCatch(isolate);
if (object->IsFunction()) {
v8::Local<v8::Function> function = object.As<v8::Function>();
auto location = LocationMirror::create(function);
if (location) {
mirrors->emplace_back(InternalPropertyMirror{
String16("[[FunctionLocation]]"), std::move(location)});
}
if (function->IsGeneratorFunction()) {
mirrors->emplace_back(InternalPropertyMirror{
String16("[[IsGenerator]]"),
ValueMirror::create(context, v8::True(context->GetIsolate()))});
}
}
if (object->IsGeneratorObject()) {
auto location = LocationMirror::createForGenerator(object);
if (location) {
mirrors->emplace_back(InternalPropertyMirror{
String16("[[GeneratorLocation]]"), std::move(location)});
}
}
V8Debugger* debugger =
static_cast<V8InspectorImpl*>(v8::debug::GetInspector(isolate))
->debugger();
v8::Local<v8::Array> properties;
if (debugger->internalProperties(context, object).ToLocal(&properties)) {
for (uint32_t i = 0; i < properties->Length(); i += 2) {
v8::Local<v8::Value> name;
if (!properties->Get(context, i).ToLocal(&name) || !name->IsString()) {
tryCatch.Reset();
continue;
}
v8::Local<v8::Value> value;
if (!properties->Get(context, i + 1).ToLocal(&value)) {
tryCatch.Reset();
continue;
}
auto wrapper = ValueMirror::create(context, value);
if (wrapper) {
mirrors->emplace_back(InternalPropertyMirror{
toProtocolStringWithTypeCheck(context->GetIsolate(), name),
std::move(wrapper)});
}
}
}
}
String16 descriptionForNode(v8::Local<v8::Context> context,
v8::Local<v8::Value> value) {
if (!value->IsObject()) return String16();
v8::Local<v8::Object> object = value.As<v8::Object>();
v8::Isolate* isolate = context->GetIsolate();
v8::TryCatch tryCatch(isolate);
v8::Local<v8::Value> nodeName;
if (!object->Get(context, toV8String(isolate, "nodeName"))
.ToLocal(&nodeName)) {
return String16();
}
String16 description;
v8::Local<v8::Function> toLowerCase =
v8::debug::GetBuiltin(isolate, v8::debug::kStringToLowerCase);
if (nodeName->IsString()) {
if (!toLowerCase->Call(context, nodeName, 0, nullptr).ToLocal(&nodeName))
return String16();
if (nodeName->IsString()) {
description = toProtocolString(isolate, nodeName.As<v8::String>());
}
}
if (!description.length()) {
v8::Local<v8::Value> value;
if (!object->Get(context, toV8String(isolate, "constructor"))
.ToLocal(&value) ||
!value->IsObject()) {
return String16();
}
if (!value.As<v8::Object>()
->Get(context, toV8String(isolate, "name"))
.ToLocal(&value) ||
!value->IsString()) {
return String16();
}
description = toProtocolString(isolate, value.As<v8::String>());
}
v8::Local<v8::Value> nodeType;
if (!object->Get(context, toV8String(isolate, "nodeType"))
.ToLocal(&nodeType) ||
!nodeType->IsInt32()) {
return description;
}
if (nodeType.As<v8::Int32>()->Value() == 1) {
v8::Local<v8::Value> idValue;
if (!object->Get(context, toV8String(isolate, "id")).ToLocal(&idValue)) {
return description;
}
if (idValue->IsString()) {
String16 id = toProtocolString(isolate, idValue.As<v8::String>());
if (id.length()) {
description = String16::concat(description, '#', id);
}
}
v8::Local<v8::Value> classNameValue;
if (!object->Get(context, toV8String(isolate, "className"))
.ToLocal(&classNameValue)) {
return description;
}
if (classNameValue->IsString() &&
classNameValue.As<v8::String>()->Length()) {
String16 classes =
toProtocolString(isolate, classNameValue.As<v8::String>());
String16Builder output;
bool previousIsDot = false;
for (size_t i = 0; i < classes.length(); ++i) {
if (classes[i] == ' ') {
if (!previousIsDot) {
output.append('.');
previousIsDot = true;
}
} else {
output.append(classes[i]);
previousIsDot = classes[i] == '.';
}
}
description = String16::concat(description, '.', output.toString());
}
} else if (nodeType.As<v8::Int32>()->Value() == 1) {
return String16::concat("<!DOCTYPE ", description, '>');
}
return description;
}
std::unique_ptr<ValueMirror> clientMirror(v8::Local<v8::Context> context,
v8::Local<v8::Value> value,
const String16& subtype) {
// TODO(alph): description and length retrieval should move to embedder.
if (subtype == "node") {
return v8::base::make_unique<ObjectMirror>(
value, subtype, descriptionForNode(context, value));
}
if (subtype == "error") {
return v8::base::make_unique<ObjectMirror>(
value, RemoteObject::SubtypeEnum::Error,
descriptionForError(context, value.As<v8::Object>(),
ErrorType::kClient));
}
if (subtype == "array" && value->IsObject()) {
v8::Isolate* isolate = context->GetIsolate();
v8::TryCatch tryCatch(isolate);
v8::Local<v8::Object> object = value.As<v8::Object>();
v8::Local<v8::Value> lengthValue;
if (object->Get(context, toV8String(isolate, "length"))
.ToLocal(&lengthValue)) {
if (lengthValue->IsInt32()) {
return v8::base::make_unique<ObjectMirror>(
value, RemoteObject::SubtypeEnum::Array,
descriptionForCollection(isolate, object,
lengthValue.As<v8::Int32>()->Value()));
}
}
}
return v8::base::make_unique<ObjectMirror>(
value,
descriptionForObject(context->GetIsolate(), value.As<v8::Object>()));
}
std::unique_ptr<ValueMirror> ValueMirror::create(v8::Local<v8::Context> context,
v8::Local<v8::Value> value) {
if (value->IsNull()) {
return v8::base::make_unique<PrimitiveValueMirror>(
value, RemoteObject::TypeEnum::Object);
}
if (value->IsBoolean()) {
return v8::base::make_unique<PrimitiveValueMirror>(
value, RemoteObject::TypeEnum::Boolean);
}
if (value->IsNumber()) {
return v8::base::make_unique<NumberMirror>(value.As<v8::Number>());
}
v8::Isolate* isolate = context->GetIsolate();
if (value->IsString()) {
return v8::base::make_unique<PrimitiveValueMirror>(
value, RemoteObject::TypeEnum::String);
}
if (value->IsBigInt()) {
return v8::base::make_unique<BigIntMirror>(value.As<v8::BigInt>());
}
if (value->IsSymbol()) {
return v8::base::make_unique<SymbolMirror>(value.As<v8::Symbol>());
}
auto clientSubtype = (value->IsUndefined() || value->IsObject())
? clientFor(context)->valueSubtype(value)
: nullptr;
if (clientSubtype) {
String16 subtype = toString16(clientSubtype->string());
return clientMirror(context, value, subtype);
}
if (value->IsUndefined()) {
return v8::base::make_unique<PrimitiveValueMirror>(
value, RemoteObject::TypeEnum::Undefined);
}
if (value->IsRegExp()) {
return v8::base::make_unique<ObjectMirror>(
value, RemoteObject::SubtypeEnum::Regexp,
descriptionForRegExp(isolate, value.As<v8::RegExp>()));
}
if (value->IsFunction()) {
return v8::base::make_unique<FunctionMirror>(value);
}
if (value->IsProxy()) {
return v8::base::make_unique<ObjectMirror>(
value, RemoteObject::SubtypeEnum::Proxy, "Proxy");
}
if (value->IsDate()) {
return v8::base::make_unique<ObjectMirror>(
value, RemoteObject::SubtypeEnum::Date,
descriptionForDate(context, value.As<v8::Date>()));
}
if (value->IsPromise()) {
v8::Local<v8::Promise> promise = value.As<v8::Promise>();
return v8::base::make_unique<ObjectMirror>(
promise, RemoteObject::SubtypeEnum::Promise,
descriptionForObject(isolate, promise));
}
if (value->IsNativeError()) {
return v8::base::make_unique<ObjectMirror>(
value, RemoteObject::SubtypeEnum::Error,
descriptionForError(context, value.As<v8::Object>(),
ErrorType::kNative));
}
if (value->IsMap()) {
v8::Local<v8::Map> map = value.As<v8::Map>();
return v8::base::make_unique<ObjectMirror>(
value, RemoteObject::SubtypeEnum::Map,
descriptionForCollection(isolate, map, map->Size()));
}
if (value->IsSet()) {
v8::Local<v8::Set> set = value.As<v8::Set>();
return v8::base::make_unique<ObjectMirror>(
value, RemoteObject::SubtypeEnum::Set,
descriptionForCollection(isolate, set, set->Size()));
}
if (value->IsWeakMap()) {
return v8::base::make_unique<ObjectMirror>(
value, RemoteObject::SubtypeEnum::Weakmap,
descriptionForObject(isolate, value.As<v8::Object>()));
}
if (value->IsWeakSet()) {
return v8::base::make_unique<ObjectMirror>(
value, RemoteObject::SubtypeEnum::Weakset,
descriptionForObject(isolate, value.As<v8::Object>()));
}
if (value->IsMapIterator() || value->IsSetIterator()) {
return v8::base::make_unique<ObjectMirror>(
value, RemoteObject::SubtypeEnum::Iterator,
descriptionForObject(isolate, value.As<v8::Object>()));
}
if (value->IsGeneratorObject()) {
v8::Local<v8::Object> object = value.As<v8::Object>();
return v8::base::make_unique<ObjectMirror>(
object, RemoteObject::SubtypeEnum::Generator,
descriptionForObject(isolate, object));
}
if (value->IsTypedArray()) {
v8::Local<v8::TypedArray> array = value.As<v8::TypedArray>();
return v8::base::make_unique<ObjectMirror>(
value, RemoteObject::SubtypeEnum::Typedarray,
descriptionForCollection(isolate, array, array->Length()));
}
if (value->IsArrayBuffer()) {
v8::Local<v8::ArrayBuffer> buffer = value.As<v8::ArrayBuffer>();
return v8::base::make_unique<ObjectMirror>(
value, RemoteObject::SubtypeEnum::Arraybuffer,
descriptionForCollection(isolate, buffer, buffer->ByteLength()));
}
if (value->IsSharedArrayBuffer()) {
v8::Local<v8::SharedArrayBuffer> buffer = value.As<v8::SharedArrayBuffer>();
return v8::base::make_unique<ObjectMirror>(
value, RemoteObject::SubtypeEnum::Arraybuffer,
descriptionForCollection(isolate, buffer, buffer->ByteLength()));
}
if (value->IsDataView()) {
v8::Local<v8::DataView> view = value.As<v8::DataView>();
return v8::base::make_unique<ObjectMirror>(
value, RemoteObject::SubtypeEnum::Dataview,
descriptionForCollection(isolate, view, view->ByteLength()));
}
V8InternalValueType internalType =
v8InternalValueTypeFrom(context, v8::Local<v8::Object>::Cast(value));
if (value->IsArray() && internalType == V8InternalValueType::kScopeList) {
return v8::base::make_unique<ObjectMirror>(
value, "internal#scopeList",
descriptionForScopeList(value.As<v8::Array>()));
}
if (value->IsObject() && internalType == V8InternalValueType::kEntry) {
return v8::base::make_unique<ObjectMirror>(
value, "internal#entry",
descriptionForEntry(context, value.As<v8::Object>()));
}
if (value->IsObject() && internalType == V8InternalValueType::kScope) {
return v8::base::make_unique<ObjectMirror>(
value, "internal#scope",
descriptionForScope(context, value.As<v8::Object>()));
}
size_t length = 0;
if (value->IsArray() || isArrayLike(context, value, &length)) {
length = value->IsArray() ? value.As<v8::Array>()->Length() : length;
return v8::base::make_unique<ObjectMirror>(
value, RemoteObject::SubtypeEnum::Array,
descriptionForCollection(isolate, value.As<v8::Object>(), length));
}
if (value->IsObject()) {
return v8::base::make_unique<ObjectMirror>(
value, descriptionForObject(isolate, value.As<v8::Object>()));
}
return nullptr;
}
} // namespace v8_inspector
// 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 V8_INSPECTOR_VALUE_MIRROR_H_
#define V8_INSPECTOR_VALUE_MIRROR_H_
#include <memory>
#include "src/base/macros.h"
#include "src/inspector/protocol/Protocol.h"
#include "src/inspector/protocol/Runtime.h"
#include "src/inspector/string-16.h"
#include "include/v8-inspector.h"
#include "include/v8.h"
namespace v8_inspector {
class ValueMirror;
enum class WrapMode;
struct InternalPropertyMirror {
String16 name;
std::unique_ptr<ValueMirror> value;
};
struct PropertyMirror {
String16 name;
bool writable;
bool configurable;
bool enumerable;
bool isOwn;
bool isIndex;
std::unique_ptr<ValueMirror> value;
std::unique_ptr<ValueMirror> getter;
std::unique_ptr<ValueMirror> setter;
std::unique_ptr<ValueMirror> symbol;
std::unique_ptr<ValueMirror> exception;
};
class ValueMirror {
public:
virtual ~ValueMirror();
static std::unique_ptr<ValueMirror> create(v8::Local<v8::Context> context,
v8::Local<v8::Value> value);
virtual protocol::Response buildRemoteObject(
v8::Local<v8::Context> context, WrapMode mode,
std::unique_ptr<protocol::Runtime::RemoteObject>* result) = 0;
virtual void buildPropertyPreview(
v8::Local<v8::Context> context, const String16& name,
std::unique_ptr<protocol::Runtime::PropertyPreview>*) {}
virtual void buildObjectPreview(
v8::Local<v8::Context> context, bool generatePreviewForProperties,
int* nameLimit, int* indexLimit,
std::unique_ptr<protocol::Runtime::ObjectPreview>*) {}
virtual void buildEntryPreview(
v8::Local<v8::Context> context, bool generatePreviewForProperties,
int* nameLimit, int* indexLimit,
std::unique_ptr<protocol::Runtime::ObjectPreview>*) {}
virtual v8::Local<v8::Value> v8Value() = 0;
class PropertyAccumulator {
public:
virtual ~PropertyAccumulator() = default;
virtual bool Add(PropertyMirror mirror) = 0;
};
static bool getProperties(v8::Local<v8::Context> context,
v8::Local<v8::Object> object, bool ownProperties,
bool accessorPropertiesOnly,
PropertyAccumulator* accumulator);
static void getInternalProperties(
v8::Local<v8::Context> context, v8::Local<v8::Object> object,
std::vector<InternalPropertyMirror>* mirrors);
};
} // namespace v8_inspector
#endif // V8_INSPECTOR_VALUE_MIRROR_H_
......@@ -44,7 +44,6 @@ v8_executable("inspector-test") {
"sessions/",
"testcfg.py",
"type-profiler/",
"../../src/inspector/injected-script-source.js",
]
cflags = []
......
......@@ -26,18 +26,7 @@ expression: Object(Symbol(42))
{
name : [[PrimitiveValue]]
type : symbol
valuePreview : {
description : Symbol
overflow : false
properties : [
[0] : {
name : description
type : string
value : 42
}
]
type : object
}
value : Symbol(42)
}
expression: Object(BigInt(2))
......
Tests that stepping ignores injected script
InjectedSciptSource was not reached
// Copyright 2017 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.
let {session, contextGroup, Protocol} = InspectorTest.start('Tests that stepping ignores injected script');
Protocol.Debugger.onPaused(message => {
let url = session._scriptMap.get(message.params.callFrames[0].location.scriptId).url;
if (url !== 'test.js') {
InspectorTest.log('InjectedSciptSource on stack.');
InspectorTest.completeTest();
}
Protocol.Debugger.stepInto();
});
session.setupScriptMap();
Protocol.Debugger.enable();
Protocol.Debugger.pause();
Protocol.Runtime.evaluate({expression: 'console.log(42)//# sourceURL=test.js'})
.then(() => InspectorTest.log('InjectedSciptSource was not reached'))
.then(InspectorTest.completeTest);
Tests that stepping does not ignore injected script when passed a flag
InjectedSciptSource on stack.
// Copyright 2017 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.
// Flags: --expose-inspector-scripts
let {session, contextGroup, Protocol} = InspectorTest.start('Tests that stepping does not ignore injected script when passed a flag');
Protocol.Debugger.onPaused(message => {
let url = session._scriptMap.get(message.params.callFrames[0].location.scriptId).url;
if (url !== 'test.js') {
InspectorTest.log('InjectedSciptSource on stack.');
InspectorTest.completeTest();
}
Protocol.Debugger.stepInto();
});
session.setupScriptMap();
Protocol.Debugger.enable();
Protocol.Debugger.pause();
Protocol.Runtime.evaluate({expression: 'console.log(42)//# sourceURL=test.js'})
.then(() => InspectorTest.log('InjectedSciptSource was not reached'))
.then(InspectorTest.completeTest);
......@@ -17,8 +17,6 @@
##############################################################################
['system == android', {
# https://crbug.com/v8/8160
'debugger/stepping-with-exposed-injected-script': [FAIL],
# https://crbug.com/v8/8197
'debugger/get-possible-breakpoints-class-fields': [SKIP],
}], # 'system == android'
......
......@@ -176,8 +176,6 @@ InspectorTest.ContextGroup = class {
if (session) {
InspectorTest.log('WARNING: setupInjectedScriptEnvironment with debug flag for debugging only and should not be landed.');
InspectorTest.log('WARNING: run test with --expose-inspector-scripts flag to get more details.');
InspectorTest.log('WARNING: you can additionally comment rjsmin in xxd.py to get unminified injected-script-source.js.');
session.setupScriptMap();
session.Protocol.Debugger.enable();
session.Protocol.Debugger.onPaused(message => {
......
......@@ -239,6 +239,7 @@ Checks console methods
name : 0
subtype : array
type : object
value : Array(2)
valuePreview : {
description : Array(2)
overflow : false
......@@ -262,6 +263,7 @@ Checks console methods
name : 1
subtype : array
type : object
value : Array(2)
valuePreview : {
description : Array(2)
overflow : false
......@@ -328,6 +330,7 @@ Checks console methods
name : 0
subtype : array
type : object
value : Array(2)
valuePreview : {
description : Array(2)
overflow : false
......@@ -346,6 +349,7 @@ Checks console methods
name : 1
subtype : array
type : object
value : Array(2)
valuePreview : {
description : Array(2)
overflow : false
......
......@@ -47,8 +47,9 @@ console.table
name : 0
subtype : array
type : object
value : Array(2)
valuePreview : {
description : Array(3)
description : Array(2)
overflow : false
properties : [
[0] : {
......@@ -70,8 +71,9 @@ console.table
name : 1
subtype : array
type : object
value : Array(2)
valuePreview : {
description : Array(3)
description : Array(2)
overflow : false
properties : [
[0] : {
......@@ -93,8 +95,9 @@ console.table
name : 2
subtype : array
type : object
value : Array(2)
valuePreview : {
description : Array(3)
description : Array(2)
overflow : false
properties : [
[0] : {
......@@ -123,8 +126,9 @@ console.table
[0] : {
name : 0
type : object
value : Person
valuePreview : {
description : Array(3)
description : Person
overflow : false
properties : [
[0] : {
......@@ -138,15 +142,15 @@ console.table
value : Smith
}
]
subtype : array
type : object
}
}
[1] : {
name : 1
type : object
value : Person
valuePreview : {
description : Array(3)
description : Person
overflow : false
properties : [
[0] : {
......@@ -160,15 +164,15 @@ console.table
value : Doe
}
]
subtype : array
type : object
}
}
[2] : {
name : 2
type : object
value : Person
valuePreview : {
description : Array(3)
description : Person
overflow : false
properties : [
[0] : {
......@@ -182,7 +186,6 @@ console.table
value : Jones
}
]
subtype : array
type : object
}
}
......@@ -197,8 +200,9 @@ console.table
[0] : {
name : mother
type : object
value : Person
valuePreview : {
description : Object
description : Person
overflow : false
properties : [
[0] : {
......@@ -218,8 +222,9 @@ console.table
[1] : {
name : father
type : object
value : Person
valuePreview : {
description : Object
description : Person
overflow : false
properties : [
[0] : {
......@@ -239,8 +244,9 @@ console.table
[2] : {
name : daughter
type : object
value : Person
valuePreview : {
description : Object
description : Person
overflow : false
properties : [
[0] : {
......@@ -267,8 +273,9 @@ console.table
[0] : {
name : 0
type : object
value : Person
valuePreview : {
description : Array(3)
description : Person
overflow : false
properties : [
[0] : {
......@@ -277,15 +284,15 @@ console.table
value : John
}
]
subtype : array
type : object
}
}
[1] : {
name : 1
type : object
value : Person
valuePreview : {
description : Array(3)
description : Person
overflow : false
properties : [
[0] : {
......@@ -294,15 +301,15 @@ console.table
value : Jane
}
]
subtype : array
type : object
}
}
[2] : {
name : 2
type : object
value : Person
valuePreview : {
description : Array(3)
description : Person
overflow : false
properties : [
[0] : {
......@@ -311,7 +318,6 @@ console.table
value : Emily
}
]
subtype : array
type : object
}
}
......
......@@ -7,10 +7,6 @@ expression: (function* foo() { yield 1 })
result : {
internalProperties : [
[0] : {
name : [[StableObjectId]]
value : <StablectObjectId>
}
[1] : {
name : [[FunctionLocation]]
value : {
description : Object
......@@ -23,13 +19,17 @@ expression: (function* foo() { yield 1 })
}
}
}
[2] : {
[1] : {
name : [[IsGenerator]]
value : {
type : boolean
value : true
}
}
[2] : {
name : [[StableObjectId]]
value : <StablectObjectId>
}
[3] : {
name : [[Scopes]]
value : {
......@@ -51,10 +51,6 @@ expression: (function foo() {})
result : {
internalProperties : [
[0] : {
name : [[StableObjectId]]
value : <StablectObjectId>
}
[1] : {
name : [[FunctionLocation]]
value : {
description : Object
......@@ -67,6 +63,10 @@ expression: (function foo() {})
}
}
}
[1] : {
name : [[StableObjectId]]
value : <StablectObjectId>
}
[2] : {
name : [[Scopes]]
value : {
......@@ -242,13 +242,26 @@ expression: gen1
result : {
internalProperties : [
[0] : {
name : [[GeneratorLocation]]
value : {
description : Object
subtype : internal#location
type : object
value : {
columnNumber : 13
lineNumber : 8
scriptId : <scriptId>
}
}
}
[1] : {
name : [[GeneratorStatus]]
value : {
type : string
value : suspended
}
}
[1] : {
[2] : {
name : [[GeneratorFunction]]
value : {
className : GeneratorFunction
......@@ -257,7 +270,7 @@ expression: gen1
type : function
}
}
[2] : {
[3] : {
name : [[GeneratorReceiver]]
value : {
className : global
......@@ -266,23 +279,10 @@ expression: gen1
type : object
}
}
[3] : {
[4] : {
name : [[StableObjectId]]
value : <StablectObjectId>
}
[4] : {
name : [[GeneratorLocation]]
value : {
description : Object
subtype : internal#location
type : object
value : {
columnNumber : 13
lineNumber : 8
scriptId : <scriptId>
}
}
}
[5] : {
name : [[Scopes]]
value : {
......@@ -302,13 +302,26 @@ expression: gen1.next();gen1
result : {
internalProperties : [
[0] : {
name : [[GeneratorLocation]]
value : {
description : Object
subtype : internal#location
type : object
value : {
columnNumber : 2
lineNumber : 9
scriptId : <scriptId>
}
}
}
[1] : {
name : [[GeneratorStatus]]
value : {
type : string
value : suspended
}
}
[1] : {
[2] : {
name : [[GeneratorFunction]]
value : {
className : GeneratorFunction
......@@ -317,7 +330,7 @@ expression: gen1.next();gen1
type : function
}
}
[2] : {
[3] : {
name : [[GeneratorReceiver]]
value : {
className : global
......@@ -326,23 +339,10 @@ expression: gen1.next();gen1
type : object
}
}
[3] : {
[4] : {
name : [[StableObjectId]]
value : <StablectObjectId>
}
[4] : {
name : [[GeneratorLocation]]
value : {
description : Object
subtype : internal#location
type : object
value : {
columnNumber : 2
lineNumber : 9
scriptId : <scriptId>
}
}
}
[5] : {
name : [[Scopes]]
value : {
......@@ -362,13 +362,26 @@ expression: gen1.next();gen1
result : {
internalProperties : [
[0] : {
name : [[GeneratorLocation]]
value : {
description : Object
subtype : internal#location
type : object
value : {
columnNumber : 13
lineNumber : 8
scriptId : <scriptId>
}
}
}
[1] : {
name : [[GeneratorStatus]]
value : {
type : string
value : closed
}
}
[1] : {
[2] : {
name : [[GeneratorFunction]]
value : {
className : GeneratorFunction
......@@ -377,7 +390,7 @@ expression: gen1.next();gen1
type : function
}
}
[2] : {
[3] : {
name : [[GeneratorReceiver]]
value : {
className : global
......@@ -386,11 +399,21 @@ expression: gen1.next();gen1
type : object
}
}
[3] : {
[4] : {
name : [[StableObjectId]]
value : <StablectObjectId>
}
[4] : {
]
}
}
Running test: generatorObjectDebuggerDisabled
expression: gen2
{
id : <messageId>
result : {
internalProperties : [
[0] : {
name : [[GeneratorLocation]]
value : {
description : Object
......@@ -403,24 +426,14 @@ expression: gen1.next();gen1
}
}
}
]
}
}
Running test: generatorObjectDebuggerDisabled
expression: gen2
{
id : <messageId>
result : {
internalProperties : [
[0] : {
[1] : {
name : [[GeneratorStatus]]
value : {
type : string
value : suspended
}
}
[1] : {
[2] : {
name : [[GeneratorFunction]]
value : {
className : GeneratorFunction
......@@ -429,7 +442,7 @@ expression: gen2
type : function
}
}
[2] : {
[3] : {
name : [[GeneratorReceiver]]
value : {
className : global
......@@ -438,23 +451,10 @@ expression: gen2
type : object
}
}
[3] : {
[4] : {
name : [[StableObjectId]]
value : <StablectObjectId>
}
[4] : {
name : [[GeneratorLocation]]
value : {
description : Object
subtype : internal#location
type : object
value : {
columnNumber : 13
lineNumber : 8
scriptId : <scriptId>
}
}
}
[5] : {
name : [[Scopes]]
value : {
......@@ -474,13 +474,26 @@ expression: gen2.next();gen2
result : {
internalProperties : [
[0] : {
name : [[GeneratorLocation]]
value : {
description : Object
subtype : internal#location
type : object
value : {
columnNumber : 2
lineNumber : 9
scriptId : <scriptId>
}
}
}
[1] : {
name : [[GeneratorStatus]]
value : {
type : string
value : suspended
}
}
[1] : {
[2] : {
name : [[GeneratorFunction]]
value : {
className : GeneratorFunction
......@@ -489,7 +502,7 @@ expression: gen2.next();gen2
type : function
}
}
[2] : {
[3] : {
name : [[GeneratorReceiver]]
value : {
className : global
......@@ -498,23 +511,10 @@ expression: gen2.next();gen2
type : object
}
}
[3] : {
[4] : {
name : [[StableObjectId]]
value : <StablectObjectId>
}
[4] : {
name : [[GeneratorLocation]]
value : {
description : Object
subtype : internal#location
type : object
value : {
columnNumber : 2
lineNumber : 9
scriptId : <scriptId>
}
}
}
[5] : {
name : [[Scopes]]
value : {
......@@ -534,13 +534,26 @@ expression: gen2.next();gen2
result : {
internalProperties : [
[0] : {
name : [[GeneratorLocation]]
value : {
description : Object
subtype : internal#location
type : object
value : {
columnNumber : 13
lineNumber : 8
scriptId : <scriptId>
}
}
}
[1] : {
name : [[GeneratorStatus]]
value : {
type : string
value : closed
}
}
[1] : {
[2] : {
name : [[GeneratorFunction]]
value : {
className : GeneratorFunction
......@@ -549,7 +562,7 @@ expression: gen2.next();gen2
type : function
}
}
[2] : {
[3] : {
name : [[GeneratorReceiver]]
value : {
className : global
......@@ -558,23 +571,10 @@ expression: gen2.next();gen2
type : object
}
}
[3] : {
[4] : {
name : [[StableObjectId]]
value : <StablectObjectId>
}
[4] : {
name : [[GeneratorLocation]]
value : {
description : Object
subtype : internal#location
type : object
value : {
columnNumber : 13
lineNumber : 8
scriptId : <scriptId>
}
}
}
]
}
}
......
......@@ -589,8 +589,6 @@ Running test: testMap
objectId : <objectId>
preview : {
description : Map(0)
entries : [
]
overflow : false
properties : [
]
......@@ -818,8 +816,6 @@ Running test: testMap
objectId : <objectId>
preview : {
description : MapIterator
entries : [
]
overflow : false
properties : [
]
......@@ -897,8 +893,6 @@ Running test: testMap
objectId : <objectId>
preview : {
description : MapIterator
entries : [
]
overflow : false
properties : [
]
......@@ -1116,8 +1110,6 @@ Running test: testWeakMap
objectId : <objectId>
preview : {
description : WeakMap
entries : [
]
overflow : false
properties : [
]
......@@ -1202,8 +1194,6 @@ Running test: testWeakSet
objectId : <objectId>
preview : {
description : WeakSet
entries : [
]
overflow : false
properties : [
]
......
Runtime.getProperties for objects with accessor
title property with getter and setter:
{
configurable : false
enumerable : false
configurable : true
enumerable : true
get : {
className : Function
description : function nativeGetter() { [native code] }
description : function () { [native code] }
objectId : <objectId>
type : function
}
......@@ -13,18 +13,18 @@ title property with getter and setter:
name : title
set : {
className : Function
description : function nativeSetter() { [native code] }
description : function () { [native code] }
objectId : <objectId>
type : function
}
}
title property with getter only:
{
configurable : false
enumerable : false
configurable : true
enumerable : true
get : {
className : Function
description : function nativeGetter() { [native code] }
description : function () { [native code] }
objectId : <objectId>
type : function
}
......
......@@ -8,7 +8,7 @@ Terminate first evaluation (it forces injected-script-source compilation)
{
error : {
code : -32000
message : Cannot access specified execution context
message : Execution was terminated
}
id : <messageId>
}
......
......@@ -60,7 +60,6 @@ class TestCase(testcase.TestCase):
def _get_resources(self):
return [
os.path.join('src', 'inspector', 'injected-script-source.js'),
os.path.join(
'test', 'inspector', 'debugger', 'resources', 'break-locations.js'),
]
......
......@@ -412,21 +412,15 @@ class SourceProcessor(SourceFileProcessor):
IGNORE_COPYRIGHTS = ['box2d.js',
'cpplint.py',
'check_injected_script_source.py',
'copy.js',
'corrections.js',
'crypto.js',
'daemon.py',
'debugger-script.js',
'earley-boyer.js',
'fannkuch.js',
'fasta.js',
'generate_protocol_externs.py',
'injected-script.cc',
'injected-script.h',
'injected-script-source.js',
'java-script-call-frame.cc',
'java-script-call-frame.h',
'jsmin.py',
'libraries.cc',
'libraries-empty.cc',
......@@ -438,14 +432,11 @@ class SourceProcessor(SourceFileProcessor):
'raytrace.js',
'regexp-pcre.js',
'resources-123.js',
'rjsmin.py',
'sqlite.js',
'sqlite-change-heap.js',
'sqlite-pointer-masking.js',
'sqlite-safe-heap.js',
'v8-debugger-script.h',
'v8-function-call.cc',
'v8-function-call.h',
'v8-inspector-impl.cc',
'v8-inspector-impl.h',
'v8-runtime-agent-impl.cc',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment