mirror of https://github.com/lukechilds/node.git
Browse Source
This implementation switches to V8 inspector from the V8 repository. The new inspector integration is now using final APIs and exposes a stable wire protocol, removing the need for pointing the users to specific devtools version. PR-URL: https://github.com/nodejs/node/pull/9028 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>v6
Eugene Ostroukhov
8 years ago
186 changed files with 14861 additions and 14870 deletions
@ -0,0 +1,13 @@ |
|||
// Copyright 2016 the V8 project authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
|||
// found in the LICENSE file.
|
|||
|
|||
#ifndef V8_V8_INSPECTOR_PROTOCOL_H_ |
|||
#define V8_V8_INSPECTOR_PROTOCOL_H_ |
|||
|
|||
#include "inspector/Debugger.h" // NOLINT(build/include) |
|||
#include "inspector/Runtime.h" // NOLINT(build/include) |
|||
#include "inspector/Schema.h" // NOLINT(build/include) |
|||
#include "v8-inspector.h" // NOLINT(build/include) |
|||
|
|||
#endif // V8_V8_INSPECTOR_PROTOCOL_H_
|
@ -0,0 +1,267 @@ |
|||
// Copyright 2016 the V8 project authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
|||
// found in the LICENSE file.
|
|||
|
|||
#ifndef V8_V8_INSPECTOR_H_ |
|||
#define V8_V8_INSPECTOR_H_ |
|||
|
|||
#include <stdint.h> |
|||
#include <cctype> |
|||
|
|||
#include <memory> |
|||
|
|||
#include "v8.h" // NOLINT(build/include) |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
namespace protocol { |
|||
namespace Debugger { |
|||
namespace API { |
|||
class SearchMatch; |
|||
} |
|||
} |
|||
namespace Runtime { |
|||
namespace API { |
|||
class RemoteObject; |
|||
class StackTrace; |
|||
} |
|||
} |
|||
namespace Schema { |
|||
namespace API { |
|||
class Domain; |
|||
} |
|||
} |
|||
} // namespace protocol
|
|||
|
|||
class V8_EXPORT StringView { |
|||
public: |
|||
StringView() : m_is8Bit(true), m_length(0), m_characters8(nullptr) {} |
|||
|
|||
StringView(const uint8_t* characters, size_t length) |
|||
: m_is8Bit(true), m_length(length), m_characters8(characters) {} |
|||
|
|||
StringView(const uint16_t* characters, size_t length) |
|||
: m_is8Bit(false), m_length(length), m_characters16(characters) {} |
|||
|
|||
bool is8Bit() const { return m_is8Bit; } |
|||
size_t length() const { return m_length; } |
|||
|
|||
// TODO(dgozman): add DCHECK(m_is8Bit) to accessors once platform can be used
|
|||
// here.
|
|||
const uint8_t* characters8() const { return m_characters8; } |
|||
const uint16_t* characters16() const { return m_characters16; } |
|||
|
|||
private: |
|||
bool m_is8Bit; |
|||
size_t m_length; |
|||
union { |
|||
const uint8_t* m_characters8; |
|||
const uint16_t* m_characters16; |
|||
}; |
|||
}; |
|||
|
|||
class V8_EXPORT StringBuffer { |
|||
public: |
|||
virtual ~StringBuffer() {} |
|||
virtual const StringView& string() = 0; |
|||
// This method copies contents.
|
|||
static std::unique_ptr<StringBuffer> create(const StringView&); |
|||
}; |
|||
|
|||
class V8_EXPORT V8ContextInfo { |
|||
public: |
|||
V8ContextInfo(v8::Local<v8::Context> context, int contextGroupId, |
|||
const StringView& humanReadableName) |
|||
: context(context), |
|||
contextGroupId(contextGroupId), |
|||
humanReadableName(humanReadableName), |
|||
hasMemoryOnConsole(false) {} |
|||
|
|||
v8::Local<v8::Context> context; |
|||
// Each v8::Context is a part of a group. The group id must be non-zero.
|
|||
int contextGroupId; |
|||
StringView humanReadableName; |
|||
StringView origin; |
|||
StringView auxData; |
|||
bool hasMemoryOnConsole; |
|||
|
|||
private: |
|||
// Disallow copying and allocating this one.
|
|||
enum NotNullTagEnum { NotNullLiteral }; |
|||
void* operator new(size_t) = delete; |
|||
void* operator new(size_t, NotNullTagEnum, void*) = delete; |
|||
void* operator new(size_t, void*) = delete; |
|||
V8ContextInfo(const V8ContextInfo&) = delete; |
|||
V8ContextInfo& operator=(const V8ContextInfo&) = delete; |
|||
}; |
|||
|
|||
class V8_EXPORT V8StackTrace { |
|||
public: |
|||
virtual bool isEmpty() const = 0; |
|||
virtual StringView topSourceURL() const = 0; |
|||
virtual int topLineNumber() const = 0; |
|||
virtual int topColumnNumber() const = 0; |
|||
virtual StringView topScriptId() const = 0; |
|||
virtual StringView topFunctionName() const = 0; |
|||
|
|||
virtual ~V8StackTrace() {} |
|||
virtual std::unique_ptr<protocol::Runtime::API::StackTrace> |
|||
buildInspectorObject() const = 0; |
|||
virtual std::unique_ptr<StringBuffer> toString() const = 0; |
|||
|
|||
// Safe to pass between threads, drops async chain.
|
|||
virtual std::unique_ptr<V8StackTrace> clone() = 0; |
|||
}; |
|||
|
|||
class V8_EXPORT V8InspectorSession { |
|||
public: |
|||
virtual ~V8InspectorSession() {} |
|||
|
|||
// Cross-context inspectable values (DOM nodes in different worlds, etc.).
|
|||
class V8_EXPORT Inspectable { |
|||
public: |
|||
virtual v8::Local<v8::Value> get(v8::Local<v8::Context>) = 0; |
|||
virtual ~Inspectable() {} |
|||
}; |
|||
virtual void addInspectedObject(std::unique_ptr<Inspectable>) = 0; |
|||
|
|||
// Dispatching protocol messages.
|
|||
static bool canDispatchMethod(const StringView& method); |
|||
virtual void dispatchProtocolMessage(const StringView& message) = 0; |
|||
virtual std::unique_ptr<StringBuffer> stateJSON() = 0; |
|||
virtual std::vector<std::unique_ptr<protocol::Schema::API::Domain>> |
|||
supportedDomains() = 0; |
|||
|
|||
// Debugger actions.
|
|||
virtual void schedulePauseOnNextStatement(const StringView& breakReason, |
|||
const StringView& breakDetails) = 0; |
|||
virtual void cancelPauseOnNextStatement() = 0; |
|||
virtual void breakProgram(const StringView& breakReason, |
|||
const StringView& breakDetails) = 0; |
|||
virtual void setSkipAllPauses(bool) = 0; |
|||
virtual void resume() = 0; |
|||
virtual void stepOver() = 0; |
|||
virtual std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>> |
|||
searchInTextByLines(const StringView& text, const StringView& query, |
|||
bool caseSensitive, bool isRegex) = 0; |
|||
|
|||
// Remote objects.
|
|||
virtual std::unique_ptr<protocol::Runtime::API::RemoteObject> wrapObject( |
|||
v8::Local<v8::Context>, v8::Local<v8::Value>, |
|||
const StringView& groupName) = 0; |
|||
virtual bool unwrapObject(std::unique_ptr<StringBuffer>* error, |
|||
const StringView& objectId, v8::Local<v8::Value>*, |
|||
v8::Local<v8::Context>*, |
|||
std::unique_ptr<StringBuffer>* objectGroup) = 0; |
|||
virtual void releaseObjectGroup(const StringView&) = 0; |
|||
}; |
|||
|
|||
enum class V8ConsoleAPIType { kClear, kDebug, kLog, kInfo, kWarning, kError }; |
|||
|
|||
class V8_EXPORT V8InspectorClient { |
|||
public: |
|||
virtual ~V8InspectorClient() {} |
|||
|
|||
virtual void runMessageLoopOnPause(int contextGroupId) {} |
|||
virtual void quitMessageLoopOnPause() {} |
|||
virtual void runIfWaitingForDebugger(int contextGroupId) {} |
|||
|
|||
virtual void muteMetrics(int contextGroupId) {} |
|||
virtual void unmuteMetrics(int contextGroupId) {} |
|||
|
|||
virtual void beginUserGesture() {} |
|||
virtual void endUserGesture() {} |
|||
|
|||
virtual std::unique_ptr<StringBuffer> valueSubtype(v8::Local<v8::Value>) { |
|||
return nullptr; |
|||
} |
|||
virtual bool formatAccessorsAsProperties(v8::Local<v8::Value>) { |
|||
return false; |
|||
} |
|||
virtual bool isInspectableHeapObject(v8::Local<v8::Object>) { return true; } |
|||
|
|||
virtual v8::Local<v8::Context> ensureDefaultContextInGroup( |
|||
int contextGroupId) { |
|||
return v8::Local<v8::Context>(); |
|||
} |
|||
virtual void beginEnsureAllContextsInGroup(int contextGroupId) {} |
|||
virtual void endEnsureAllContextsInGroup(int contextGroupId) {} |
|||
|
|||
virtual void installAdditionalCommandLineAPI(v8::Local<v8::Context>, |
|||
v8::Local<v8::Object>) {} |
|||
virtual void consoleAPIMessage(int contextGroupId, V8ConsoleAPIType, |
|||
const StringView& message, |
|||
const StringView& url, unsigned lineNumber, |
|||
unsigned columnNumber, V8StackTrace*) {} |
|||
virtual v8::MaybeLocal<v8::Value> memoryInfo(v8::Isolate*, |
|||
v8::Local<v8::Context>) { |
|||
return v8::MaybeLocal<v8::Value>(); |
|||
} |
|||
|
|||
virtual void consoleTime(const StringView& title) {} |
|||
virtual void consoleTimeEnd(const StringView& title) {} |
|||
virtual void consoleTimeStamp(const StringView& title) {} |
|||
virtual double currentTimeMS() { return 0; } |
|||
typedef void (*TimerCallback)(void*); |
|||
virtual void startRepeatingTimer(double, TimerCallback, void* data) {} |
|||
virtual void cancelTimer(void* data) {} |
|||
|
|||
// TODO(dgozman): this was added to support service worker shadow page. We
|
|||
// should not connect at all.
|
|||
virtual bool canExecuteScripts(int contextGroupId) { return true; } |
|||
}; |
|||
|
|||
class V8_EXPORT V8Inspector { |
|||
public: |
|||
static std::unique_ptr<V8Inspector> create(v8::Isolate*, V8InspectorClient*); |
|||
virtual ~V8Inspector() {} |
|||
|
|||
// Contexts instrumentation.
|
|||
virtual void contextCreated(const V8ContextInfo&) = 0; |
|||
virtual void contextDestroyed(v8::Local<v8::Context>) = 0; |
|||
virtual void resetContextGroup(int contextGroupId) = 0; |
|||
|
|||
// Various instrumentation.
|
|||
virtual void willExecuteScript(v8::Local<v8::Context>, int scriptId) = 0; |
|||
virtual void didExecuteScript(v8::Local<v8::Context>) = 0; |
|||
virtual void idleStarted() = 0; |
|||
virtual void idleFinished() = 0; |
|||
|
|||
// Async stack traces instrumentation.
|
|||
virtual void asyncTaskScheduled(const StringView& taskName, void* task, |
|||
bool recurring) = 0; |
|||
virtual void asyncTaskCanceled(void* task) = 0; |
|||
virtual void asyncTaskStarted(void* task) = 0; |
|||
virtual void asyncTaskFinished(void* task) = 0; |
|||
virtual void allAsyncTasksCanceled() = 0; |
|||
|
|||
// Exceptions instrumentation.
|
|||
virtual unsigned exceptionThrown( |
|||
v8::Local<v8::Context>, const StringView& message, |
|||
v8::Local<v8::Value> exception, const StringView& detailedMessage, |
|||
const StringView& url, unsigned lineNumber, unsigned columnNumber, |
|||
std::unique_ptr<V8StackTrace>, int scriptId) = 0; |
|||
virtual void exceptionRevoked(v8::Local<v8::Context>, unsigned exceptionId, |
|||
const StringView& message) = 0; |
|||
|
|||
// Connection.
|
|||
class V8_EXPORT Channel { |
|||
public: |
|||
virtual ~Channel() {} |
|||
virtual void sendProtocolResponse(int callId, |
|||
const StringView& message) = 0; |
|||
virtual void sendProtocolNotification(const StringView& message) = 0; |
|||
virtual void flushProtocolNotifications() = 0; |
|||
}; |
|||
virtual std::unique_ptr<V8InspectorSession> connect( |
|||
int contextGroupId, Channel*, const StringView& state) = 0; |
|||
|
|||
// API methods.
|
|||
virtual std::unique_ptr<V8StackTrace> createStackTrace( |
|||
v8::Local<v8::StackTrace>) = 0; |
|||
virtual std::unique_ptr<V8StackTrace> captureStackTrace(bool fullStack) = 0; |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_V8_INSPECTOR_H_
|
@ -0,0 +1,189 @@ |
|||
# 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("../../gni/v8.gni") |
|||
|
|||
_inspector_protocol = "//third_party/WebKit/Source/platform/inspector_protocol" |
|||
import("$_inspector_protocol/inspector_protocol.gni") |
|||
|
|||
_protocol_generated = [ |
|||
"protocol/Forward.h", |
|||
"protocol/Protocol.cpp", |
|||
"protocol/Protocol.h", |
|||
"protocol/Console.cpp", |
|||
"protocol/Console.h", |
|||
"protocol/Debugger.cpp", |
|||
"protocol/Debugger.h", |
|||
"protocol/HeapProfiler.cpp", |
|||
"protocol/HeapProfiler.h", |
|||
"protocol/Profiler.cpp", |
|||
"protocol/Profiler.h", |
|||
"protocol/Runtime.cpp", |
|||
"protocol/Runtime.h", |
|||
"protocol/Schema.cpp", |
|||
"protocol/Schema.h", |
|||
"../../include/inspector/Debugger.h", |
|||
"../../include/inspector/Runtime.h", |
|||
"../../include/inspector/Schema.h", |
|||
] |
|||
|
|||
action("protocol_compatibility") { |
|||
visibility = [ ":*" ] # Only targets in this file can depend on this. |
|||
script = "$_inspector_protocol/CheckProtocolCompatibility.py" |
|||
inputs = [ |
|||
"js_protocol.json", |
|||
] |
|||
_stamp = "$target_gen_dir/js_protocol.stamp" |
|||
outputs = [ |
|||
_stamp, |
|||
] |
|||
args = [ |
|||
"--stamp", |
|||
rebase_path(_stamp, root_build_dir), |
|||
rebase_path("js_protocol.json", root_build_dir), |
|||
] |
|||
} |
|||
|
|||
inspector_protocol_generate("protocol_generated_sources") { |
|||
visibility = [ ":*" ] # Only targets in this file can depend on this. |
|||
deps = [ |
|||
":protocol_compatibility", |
|||
] |
|||
|
|||
out_dir = target_gen_dir |
|||
config_file = "inspector_protocol_config.json" |
|||
inputs = [ |
|||
"js_protocol.json", |
|||
"inspector_protocol_config.json", |
|||
] |
|||
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), |
|||
] |
|||
} |
|||
|
|||
action("inspector_debugger_script") { |
|||
visibility = [ ":*" ] # Only targets in this file can depend on this. |
|||
script = "build/xxd.py" |
|||
inputs = [ |
|||
"debugger-script.js", |
|||
] |
|||
outputs = [ |
|||
"$target_gen_dir/debugger-script.h", |
|||
] |
|||
args = [ |
|||
"DebuggerScript_js", |
|||
rebase_path("debugger-script.js", root_build_dir), |
|||
rebase_path("$target_gen_dir/debugger-script.h", root_build_dir), |
|||
] |
|||
} |
|||
|
|||
config("inspector_config") { |
|||
visibility = [ ":*" ] # Only targets in this file can depend on this. |
|||
cflags = [] |
|||
if (is_win) { |
|||
cflags += [ |
|||
"/wd4267", # Truncation from size_t to int. |
|||
"/wd4305", # Truncation from 'type1' to 'type2'. |
|||
"/wd4324", # Struct padded due to declspec(align). |
|||
"/wd4714", # Function marked forceinline not inlined. |
|||
"/wd4800", # Value forced to bool. |
|||
"/wd4996", # Deprecated function call. |
|||
] |
|||
} |
|||
if (is_component_build) { |
|||
defines = [ "BUILDING_V8_SHARED" ] |
|||
} |
|||
} |
|||
|
|||
v8_source_set("inspector") { |
|||
deps = [ |
|||
":inspector_debugger_script", |
|||
":inspector_injected_script", |
|||
":protocol_generated_sources", |
|||
] |
|||
configs = [ ":inspector_config" ] |
|||
include_dirs = [ |
|||
"../..", |
|||
"../../include", |
|||
"$target_gen_dir/../..", |
|||
"$target_gen_dir/../../include", |
|||
] |
|||
sources = rebase_path(_protocol_generated, ".", target_gen_dir) |
|||
sources += [ |
|||
"../../include/v8-inspector-protocol.h", |
|||
"../../include/v8-inspector.h", |
|||
] |
|||
sources += get_target_outputs(":inspector_injected_script") |
|||
sources += get_target_outputs(":inspector_debugger_script") |
|||
sources += [ |
|||
"injected-script-native.cc", |
|||
"injected-script-native.h", |
|||
"injected-script.cc", |
|||
"injected-script.h", |
|||
"inspected-context.cc", |
|||
"inspected-context.h", |
|||
"java-script-call-frame.cc", |
|||
"java-script-call-frame.h", |
|||
"protocol-platform.h", |
|||
"remote-object-id.cc", |
|||
"remote-object-id.h", |
|||
"script-breakpoint.h", |
|||
"search-util.cc", |
|||
"search-util.h", |
|||
"string-16.cc", |
|||
"string-16.h", |
|||
"string-util.cc", |
|||
"string-util.h", |
|||
"v8-console-agent-impl.cc", |
|||
"v8-console-agent-impl.h", |
|||
"v8-console-message.cc", |
|||
"v8-console-message.h", |
|||
"v8-console.cc", |
|||
"v8-console.h", |
|||
"v8-debugger-agent-impl.cc", |
|||
"v8-debugger-agent-impl.h", |
|||
"v8-debugger-script.cc", |
|||
"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", |
|||
"v8-regex.h", |
|||
"v8-runtime-agent-impl.cc", |
|||
"v8-runtime-agent-impl.h", |
|||
"v8-schema-agent-impl.cc", |
|||
"v8-schema-agent-impl.h", |
|||
"v8-stack-trace-impl.cc", |
|||
"v8-stack-trace-impl.h", |
|||
"v8-value-copier.cc", |
|||
"v8-value-copier.h", |
|||
] |
|||
} |
@ -0,0 +1,8 @@ |
|||
include_rules = [ |
|||
"-src", |
|||
"+src/inspector", |
|||
"+src/base/atomicops.h", |
|||
"+src/base/macros.h", |
|||
"+src/base/logging.h", |
|||
"+src/base/platform/platform.h", |
|||
] |
@ -1,8 +1,12 @@ |
|||
set noparent |
|||
|
|||
alph@chromium.org |
|||
caseq@chromium.org |
|||
dgozman@chromium.org |
|||
jochen@chromium.org |
|||
kozyatinskiy@chromium.org |
|||
pfeldman@chromium.org |
|||
yangguo@chromium.org |
|||
|
|||
# Changes to remote debugging protocol require devtools review to |
|||
# ensure backwards compatibility and committment to maintain. |
@ -0,0 +1,55 @@ |
|||
#!/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", |
|||
"debugger_script_externs.js", |
|||
"injected_script_externs.js", |
|||
"check_injected_script_source.js", |
|||
"debugger-script.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 |
@ -0,0 +1,88 @@ |
|||
#!/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)) |
@ -0,0 +1 @@ |
|||
69937d3c239ca63e4c9045718886ddd096ffc054 |
@ -0,0 +1,169 @@ |
|||
#!/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') |
|||
debugger_script_source_name = path.join(v8_inspector_path, |
|||
'debugger-script.js') |
|||
debugger_script_externs_file = path.join(v8_inspector_path, |
|||
'debugger_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 'Compiling debugger-script.js...' |
|||
|
|||
command = spawned_compiler_command + [ |
|||
'--externs', debugger_script_externs_file, |
|||
'--js', debugger_script_source_name, |
|||
'--new_type_inf' |
|||
] |
|||
|
|||
debugger_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) |
|||
|
|||
(debugger_script_compiler_out, _) = debugger_script_compile_proc.communicate() |
|||
print 'debugger-script.js compilation output:%s' % os.linesep |
|||
print debugger_script_compiler_out |
|||
errors_found |= has_errors(debugger_script_compiler_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) |
@ -0,0 +1,246 @@ |
|||
#!/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) |
@ -1,4 +1,4 @@ |
|||
# Copyright 2016 The Chromium Authors. All rights reserved. |
|||
# 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. |
|||
|
@ -0,0 +1,89 @@ |
|||
// 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/injected-script-native.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
InjectedScriptNative::InjectedScriptNative(v8::Isolate* isolate) |
|||
: m_lastBoundObjectId(1), m_isolate(isolate) {} |
|||
|
|||
static const char privateKeyName[] = "v8-inspector#injectedScript"; |
|||
|
|||
InjectedScriptNative::~InjectedScriptNative() {} |
|||
|
|||
void InjectedScriptNative::setOnInjectedScriptHost( |
|||
v8::Local<v8::Object> injectedScriptHost) { |
|||
v8::HandleScope handleScope(m_isolate); |
|||
v8::Local<v8::External> external = v8::External::New(m_isolate, this); |
|||
v8::Local<v8::Private> privateKey = v8::Private::ForApi( |
|||
m_isolate, v8::String::NewFromUtf8(m_isolate, privateKeyName, |
|||
v8::NewStringType::kInternalized) |
|||
.ToLocalChecked()); |
|||
injectedScriptHost->SetPrivate(m_isolate->GetCurrentContext(), privateKey, |
|||
external); |
|||
} |
|||
|
|||
InjectedScriptNative* InjectedScriptNative::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<InjectedScriptNative*>(external->Value()); |
|||
} |
|||
|
|||
int InjectedScriptNative::bind(v8::Local<v8::Value> value, |
|||
const String16& groupName) { |
|||
if (m_lastBoundObjectId <= 0) m_lastBoundObjectId = 1; |
|||
int id = m_lastBoundObjectId++; |
|||
m_idToWrappedObject[id] = |
|||
wrapUnique(new v8::Global<v8::Value>(m_isolate, value)); |
|||
addObjectToGroup(id, groupName); |
|||
return id; |
|||
} |
|||
|
|||
void InjectedScriptNative::unbind(int id) { |
|||
m_idToWrappedObject.erase(id); |
|||
m_idToObjectGroupName.erase(id); |
|||
} |
|||
|
|||
v8::Local<v8::Value> InjectedScriptNative::objectForId(int id) { |
|||
auto iter = m_idToWrappedObject.find(id); |
|||
return iter != m_idToWrappedObject.end() ? iter->second->Get(m_isolate) |
|||
: v8::Local<v8::Value>(); |
|||
} |
|||
|
|||
void InjectedScriptNative::addObjectToGroup(int objectId, |
|||
const String16& groupName) { |
|||
if (groupName.isEmpty()) return; |
|||
if (objectId <= 0) return; |
|||
m_idToObjectGroupName[objectId] = groupName; |
|||
m_nameToObjectGroup[groupName].push_back( |
|||
objectId); // Creates an empty vector if key is not there
|
|||
} |
|||
|
|||
void InjectedScriptNative::releaseObjectGroup(const String16& groupName) { |
|||
if (groupName.isEmpty()) return; |
|||
NameToObjectGroup::iterator groupIt = m_nameToObjectGroup.find(groupName); |
|||
if (groupIt == m_nameToObjectGroup.end()) return; |
|||
for (int id : groupIt->second) unbind(id); |
|||
m_nameToObjectGroup.erase(groupIt); |
|||
} |
|||
|
|||
String16 InjectedScriptNative::groupName(int objectId) const { |
|||
if (objectId <= 0) return String16(); |
|||
IdToObjectGroupName::const_iterator iterator = |
|||
m_idToObjectGroupName.find(objectId); |
|||
return iterator != m_idToObjectGroupName.end() ? iterator->second |
|||
: String16(); |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,47 @@ |
|||
// 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_INJECTEDSCRIPTNATIVE_H_ |
|||
#define V8_INSPECTOR_INJECTEDSCRIPTNATIVE_H_ |
|||
|
|||
#include <vector> |
|||
|
|||
#include "src/inspector/protocol/Protocol.h" |
|||
|
|||
#include "include/v8.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
class InjectedScriptNative final { |
|||
public: |
|||
explicit InjectedScriptNative(v8::Isolate*); |
|||
~InjectedScriptNative(); |
|||
|
|||
void setOnInjectedScriptHost(v8::Local<v8::Object>); |
|||
static InjectedScriptNative* fromInjectedScriptHost(v8::Isolate* isolate, |
|||
v8::Local<v8::Object>); |
|||
|
|||
int bind(v8::Local<v8::Value>, const String16& groupName); |
|||
void unbind(int id); |
|||
v8::Local<v8::Value> objectForId(int id); |
|||
|
|||
void releaseObjectGroup(const String16& groupName); |
|||
String16 groupName(int objectId) const; |
|||
|
|||
private: |
|||
void addObjectToGroup(int objectId, const String16& groupName); |
|||
|
|||
int m_lastBoundObjectId; |
|||
v8::Isolate* m_isolate; |
|||
protocol::HashMap<int, std::unique_ptr<v8::Global<v8::Value>>> |
|||
m_idToWrappedObject; |
|||
typedef protocol::HashMap<int, String16> IdToObjectGroupName; |
|||
IdToObjectGroupName m_idToObjectGroupName; |
|||
typedef protocol::HashMap<String16, std::vector<int>> NameToObjectGroup; |
|||
NameToObjectGroup m_nameToObjectGroup; |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_INJECTEDSCRIPTNATIVE_H_
|
@ -0,0 +1,575 @@ |
|||
/*
|
|||
* Copyright (C) 2012 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/injected-script.h" |
|||
|
|||
#include "src/inspector/injected-script-native.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-copier.h" |
|||
|
|||
#include "include/v8-inspector.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
using protocol::Array; |
|||
using protocol::Runtime::PropertyDescriptor; |
|||
using protocol::Runtime::InternalPropertyDescriptor; |
|||
using protocol::Runtime::RemoteObject; |
|||
using protocol::Maybe; |
|||
|
|||
static bool hasInternalError(ErrorString* errorString, bool hasError) { |
|||
if (hasError) *errorString = "Internal error"; |
|||
return hasError; |
|||
} |
|||
|
|||
std::unique_ptr<InjectedScript> InjectedScript::create( |
|||
InspectedContext* inspectedContext) { |
|||
v8::Isolate* isolate = inspectedContext->isolate(); |
|||
v8::HandleScope handles(isolate); |
|||
v8::Local<v8::Context> context = inspectedContext->context(); |
|||
v8::Context::Scope scope(context); |
|||
|
|||
std::unique_ptr<InjectedScriptNative> injectedScriptNative( |
|||
new InjectedScriptNative(isolate)); |
|||
v8::Local<v8::Object> scriptHostWrapper = |
|||
V8InjectedScriptHost::create(context, inspectedContext->inspector()); |
|||
injectedScriptNative->setOnInjectedScriptHost(scriptHostWrapper); |
|||
|
|||
// 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.
|
|||
String16 injectedScriptSource( |
|||
reinterpret_cast<const char*>(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::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())}; |
|||
v8::MicrotasksScope microtasksScope(isolate, |
|||
v8::MicrotasksScope::kDoNotRunMicrotasks); |
|||
v8::Local<v8::Value> injectedScriptValue; |
|||
if (!function->Call(context, windowGlobal, arraysize(info), info) |
|||
.ToLocal(&injectedScriptValue)) |
|||
return nullptr; |
|||
if (!injectedScriptValue->IsObject()) return nullptr; |
|||
return wrapUnique(new InjectedScript(inspectedContext, |
|||
injectedScriptValue.As<v8::Object>(), |
|||
std::move(injectedScriptNative))); |
|||
} |
|||
|
|||
InjectedScript::InjectedScript( |
|||
InspectedContext* context, v8::Local<v8::Object> object, |
|||
std::unique_ptr<InjectedScriptNative> injectedScriptNative) |
|||
: m_context(context), |
|||
m_value(context->isolate(), object), |
|||
m_native(std::move(injectedScriptNative)) {} |
|||
|
|||
InjectedScript::~InjectedScript() {} |
|||
|
|||
void InjectedScript::getProperties( |
|||
ErrorString* errorString, v8::Local<v8::Object> object, |
|||
const String16& groupName, bool ownProperties, bool accessorPropertiesOnly, |
|||
bool generatePreview, |
|||
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()) { |
|||
*exceptionDetails = createExceptionDetails(errorString, tryCatch, groupName, |
|||
generatePreview); |
|||
// FIXME: make properties optional
|
|||
*properties = Array<PropertyDescriptor>::create(); |
|||
return; |
|||
} |
|||
if (hasInternalError(errorString, resultValue.IsEmpty())) return; |
|||
std::unique_ptr<protocol::Value> protocolValue = |
|||
toProtocolValue(errorString, context, resultValue); |
|||
if (!protocolValue) return; |
|||
protocol::ErrorSupport errors(errorString); |
|||
std::unique_ptr<Array<PropertyDescriptor>> result = |
|||
Array<PropertyDescriptor>::parse(protocolValue.get(), &errors); |
|||
if (!hasInternalError(errorString, errors.hasErrors())) |
|||
*properties = std::move(result); |
|||
} |
|||
|
|||
void InjectedScript::releaseObject(const String16& objectId) { |
|||
std::unique_ptr<protocol::Value> parsedObjectId = |
|||
protocol::parseJSON(objectId); |
|||
if (!parsedObjectId) return; |
|||
protocol::DictionaryValue* object = |
|||
protocol::DictionaryValue::cast(parsedObjectId.get()); |
|||
if (!object) return; |
|||
int boundId = 0; |
|||
if (!object->getInteger("id", &boundId)) return; |
|||
m_native->unbind(boundId); |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Runtime::RemoteObject> InjectedScript::wrapObject( |
|||
ErrorString* errorString, v8::Local<v8::Value> value, |
|||
const String16& groupName, bool forceValueType, |
|||
bool generatePreview) const { |
|||
v8::HandleScope handles(m_context->isolate()); |
|||
v8::Local<v8::Value> wrappedObject; |
|||
v8::Local<v8::Context> context = m_context->context(); |
|||
if (!wrapValue(errorString, value, groupName, forceValueType, generatePreview) |
|||
.ToLocal(&wrappedObject)) |
|||
return nullptr; |
|||
protocol::ErrorSupport errors; |
|||
std::unique_ptr<protocol::Value> protocolValue = |
|||
toProtocolValue(errorString, context, wrappedObject); |
|||
if (!protocolValue) return nullptr; |
|||
std::unique_ptr<protocol::Runtime::RemoteObject> remoteObject = |
|||
protocol::Runtime::RemoteObject::parse(protocolValue.get(), &errors); |
|||
if (!remoteObject) *errorString = errors.errors(); |
|||
return remoteObject; |
|||
} |
|||
|
|||
bool InjectedScript::wrapObjectProperty(ErrorString* errorString, |
|||
v8::Local<v8::Object> object, |
|||
v8::Local<v8::Name> key, |
|||
const String16& groupName, |
|||
bool forceValueType, |
|||
bool generatePreview) const { |
|||
v8::Local<v8::Value> property; |
|||
v8::Local<v8::Context> context = m_context->context(); |
|||
if (hasInternalError(errorString, |
|||
!object->Get(context, key).ToLocal(&property))) |
|||
return false; |
|||
v8::Local<v8::Value> wrappedProperty; |
|||
if (!wrapValue(errorString, property, groupName, forceValueType, |
|||
generatePreview) |
|||
.ToLocal(&wrappedProperty)) |
|||
return false; |
|||
v8::Maybe<bool> success = |
|||
createDataProperty(context, object, key, wrappedProperty); |
|||
if (hasInternalError(errorString, success.IsNothing() || !success.FromJust())) |
|||
return false; |
|||
return true; |
|||
} |
|||
|
|||
bool InjectedScript::wrapPropertyInArray(ErrorString* errorString, |
|||
v8::Local<v8::Array> array, |
|||
v8::Local<v8::String> property, |
|||
const String16& groupName, |
|||
bool forceValueType, |
|||
bool generatePreview) const { |
|||
V8FunctionCall function(m_context->inspector(), m_context->context(), |
|||
v8Value(), "wrapPropertyInArray"); |
|||
function.appendArgument(array); |
|||
function.appendArgument(property); |
|||
function.appendArgument(groupName); |
|||
function.appendArgument(forceValueType); |
|||
function.appendArgument(generatePreview); |
|||
bool hadException = false; |
|||
function.call(hadException); |
|||
return !hasInternalError(errorString, hadException); |
|||
} |
|||
|
|||
bool InjectedScript::wrapObjectsInArray(ErrorString* errorString, |
|||
v8::Local<v8::Array> array, |
|||
const String16& groupName, |
|||
bool forceValueType, |
|||
bool generatePreview) const { |
|||
V8FunctionCall function(m_context->inspector(), m_context->context(), |
|||
v8Value(), "wrapObjectsInArray"); |
|||
function.appendArgument(array); |
|||
function.appendArgument(groupName); |
|||
function.appendArgument(forceValueType); |
|||
function.appendArgument(generatePreview); |
|||
bool hadException = false; |
|||
function.call(hadException); |
|||
return !hasInternalError(errorString, hadException); |
|||
} |
|||
|
|||
v8::MaybeLocal<v8::Value> InjectedScript::wrapValue( |
|||
ErrorString* errorString, v8::Local<v8::Value> value, |
|||
const String16& groupName, bool forceValueType, |
|||
bool generatePreview) 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; |
|||
v8::Local<v8::Value> r = function.call(hadException); |
|||
if (hasInternalError(errorString, hadException || r.IsEmpty())) |
|||
return v8::MaybeLocal<v8::Value>(); |
|||
return r; |
|||
} |
|||
|
|||
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::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; |
|||
protocol::ErrorString errorString; |
|||
std::unique_ptr<protocol::Value> protocolValue = |
|||
toProtocolValue(&errorString, context, r); |
|||
if (!protocolValue) return nullptr; |
|||
protocol::ErrorSupport errors; |
|||
return protocol::Runtime::RemoteObject::parse(protocolValue.get(), &errors); |
|||
} |
|||
|
|||
bool InjectedScript::findObject(ErrorString* errorString, |
|||
const RemoteObjectId& objectId, |
|||
v8::Local<v8::Value>* outObject) const { |
|||
*outObject = m_native->objectForId(objectId.id()); |
|||
if (outObject->IsEmpty()) |
|||
*errorString = "Could not find object with given id"; |
|||
return !outObject->IsEmpty(); |
|||
} |
|||
|
|||
String16 InjectedScript::objectGroupName(const RemoteObjectId& objectId) const { |
|||
return m_native->groupName(objectId.id()); |
|||
} |
|||
|
|||
void InjectedScript::releaseObjectGroup(const String16& objectGroup) { |
|||
m_native->releaseObjectGroup(objectGroup); |
|||
if (objectGroup == "console") m_lastEvaluationResult.Reset(); |
|||
} |
|||
|
|||
void InjectedScript::setCustomObjectFormatterEnabled(bool enabled) { |
|||
v8::HandleScope handles(m_context->isolate()); |
|||
V8FunctionCall function(m_context->inspector(), m_context->context(), |
|||
v8Value(), "setCustomObjectFormatterEnabled"); |
|||
function.appendArgument(enabled); |
|||
bool hadException = false; |
|||
function.call(hadException); |
|||
DCHECK(!hadException); |
|||
} |
|||
|
|||
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()); |
|||
return m_lastEvaluationResult.Get(m_context->isolate()); |
|||
} |
|||
|
|||
v8::MaybeLocal<v8::Value> InjectedScript::resolveCallArgument( |
|||
ErrorString* errorString, protocol::Runtime::CallArgument* callArgument) { |
|||
if (callArgument->hasObjectId()) { |
|||
std::unique_ptr<RemoteObjectId> remoteObjectId = |
|||
RemoteObjectId::parse(errorString, callArgument->getObjectId("")); |
|||
if (!remoteObjectId) return v8::MaybeLocal<v8::Value>(); |
|||
if (remoteObjectId->contextId() != m_context->contextId()) { |
|||
*errorString = |
|||
"Argument should belong to the same JavaScript world as target " |
|||
"object"; |
|||
return v8::MaybeLocal<v8::Value>(); |
|||
} |
|||
v8::Local<v8::Value> object; |
|||
if (!findObject(errorString, *remoteObjectId, &object)) |
|||
return v8::MaybeLocal<v8::Value>(); |
|||
return object; |
|||
} |
|||
if (callArgument->hasValue() || callArgument->hasUnserializableValue()) { |
|||
String16 value = |
|||
callArgument->hasValue() |
|||
? callArgument->getValue(nullptr)->toJSONString() |
|||
: "Number(\"" + callArgument->getUnserializableValue("") + "\")"; |
|||
v8::Local<v8::Value> object; |
|||
if (!m_context->inspector() |
|||
->compileAndRunInternalScript( |
|||
m_context->context(), toV8String(m_context->isolate(), value)) |
|||
.ToLocal(&object)) { |
|||
*errorString = "Couldn't parse value object in call argument"; |
|||
return v8::MaybeLocal<v8::Value>(); |
|||
} |
|||
return object; |
|||
} |
|||
return v8::Undefined(m_context->isolate()); |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Runtime::ExceptionDetails> |
|||
InjectedScript::createExceptionDetails(ErrorString* errorString, |
|||
const v8::TryCatch& tryCatch, |
|||
const String16& objectGroup, |
|||
bool generatePreview) { |
|||
if (!tryCatch.HasCaught()) return nullptr; |
|||
v8::Local<v8::Message> message = tryCatch.Message(); |
|||
v8::Local<v8::Value> exception = tryCatch.Exception(); |
|||
String16 messageText = |
|||
message.IsEmpty() ? String16() : toProtocolString(message->Get()); |
|||
std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails = |
|||
protocol::Runtime::ExceptionDetails::create() |
|||
.setExceptionId(m_context->inspector()->nextExceptionId()) |
|||
.setText(exception.IsEmpty() ? messageText : String16("Uncaught")) |
|||
.setLineNumber( |
|||
message.IsEmpty() |
|||
? 0 |
|||
: message->GetLineNumber(m_context->context()).FromMaybe(1) - |
|||
1) |
|||
.setColumnNumber( |
|||
message.IsEmpty() |
|||
? 0 |
|||
: message->GetStartColumn(m_context->context()).FromMaybe(0)) |
|||
.build(); |
|||
if (!message.IsEmpty()) { |
|||
exceptionDetails->setScriptId(String16::fromInteger( |
|||
static_cast<int>(message->GetScriptOrigin().ScriptID()->Value()))); |
|||
v8::Local<v8::StackTrace> stackTrace = message->GetStackTrace(); |
|||
if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0) |
|||
exceptionDetails->setStackTrace(m_context->inspector() |
|||
->debugger() |
|||
->createStackTrace(stackTrace) |
|||
->buildInspectorObjectImpl()); |
|||
} |
|||
if (!exception.IsEmpty()) { |
|||
std::unique_ptr<protocol::Runtime::RemoteObject> wrapped = wrapObject( |
|||
errorString, exception, objectGroup, false /* forceValueType */, |
|||
generatePreview && !exception->IsNativeError()); |
|||
if (!wrapped) return nullptr; |
|||
exceptionDetails->setException(std::move(wrapped)); |
|||
} |
|||
return exceptionDetails; |
|||
} |
|||
|
|||
void InjectedScript::wrapEvaluateResult( |
|||
ErrorString* errorString, v8::MaybeLocal<v8::Value> maybeResultValue, |
|||
const v8::TryCatch& tryCatch, const String16& objectGroup, |
|||
bool returnByValue, bool generatePreview, |
|||
std::unique_ptr<protocol::Runtime::RemoteObject>* result, |
|||
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) { |
|||
v8::Local<v8::Value> resultValue; |
|||
if (!tryCatch.HasCaught()) { |
|||
if (hasInternalError(errorString, !maybeResultValue.ToLocal(&resultValue))) |
|||
return; |
|||
std::unique_ptr<RemoteObject> remoteObject = wrapObject( |
|||
errorString, resultValue, objectGroup, returnByValue, generatePreview); |
|||
if (!remoteObject) return; |
|||
if (objectGroup == "console") |
|||
m_lastEvaluationResult.Reset(m_context->isolate(), resultValue); |
|||
*result = std::move(remoteObject); |
|||
} else { |
|||
v8::Local<v8::Value> exception = tryCatch.Exception(); |
|||
std::unique_ptr<RemoteObject> remoteObject = |
|||
wrapObject(errorString, exception, objectGroup, false, |
|||
generatePreview && !exception->IsNativeError()); |
|||
if (!remoteObject) return; |
|||
// We send exception in result for compatibility reasons, even though it's
|
|||
// accessible through exceptionDetails.exception.
|
|||
*result = std::move(remoteObject); |
|||
*exceptionDetails = createExceptionDetails(errorString, tryCatch, |
|||
objectGroup, generatePreview); |
|||
} |
|||
} |
|||
|
|||
v8::Local<v8::Object> InjectedScript::commandLineAPI() { |
|||
if (m_commandLineAPI.IsEmpty()) |
|||
m_commandLineAPI.Reset(m_context->isolate(), |
|||
V8Console::createCommandLineAPI(m_context)); |
|||
return m_commandLineAPI.Get(m_context->isolate()); |
|||
} |
|||
|
|||
InjectedScript::Scope::Scope(ErrorString* errorString, |
|||
V8InspectorImpl* inspector, int contextGroupId) |
|||
: m_errorString(errorString), |
|||
m_inspector(inspector), |
|||
m_contextGroupId(contextGroupId), |
|||
m_injectedScript(nullptr), |
|||
m_handleScope(inspector->isolate()), |
|||
m_tryCatch(inspector->isolate()), |
|||
m_ignoreExceptionsAndMuteConsole(false), |
|||
m_previousPauseOnExceptionsState(V8Debugger::DontPauseOnExceptions), |
|||
m_userGesture(false) {} |
|||
|
|||
bool InjectedScript::Scope::initialize() { |
|||
cleanup(); |
|||
// TODO(dgozman): what if we reattach to the same context group during
|
|||
// evaluate? Introduce a session id?
|
|||
V8InspectorSessionImpl* session = |
|||
m_inspector->sessionForContextGroup(m_contextGroupId); |
|||
if (!session) { |
|||
*m_errorString = "Internal error"; |
|||
return false; |
|||
} |
|||
findInjectedScript(session); |
|||
if (!m_injectedScript) return false; |
|||
m_context = m_injectedScript->context()->context(); |
|||
m_context->Enter(); |
|||
return true; |
|||
} |
|||
|
|||
bool InjectedScript::Scope::installCommandLineAPI() { |
|||
DCHECK(m_injectedScript && !m_context.IsEmpty() && |
|||
!m_commandLineAPIScope.get()); |
|||
m_commandLineAPIScope.reset(new V8Console::CommandLineAPIScope( |
|||
m_context, m_injectedScript->commandLineAPI(), m_context->Global())); |
|||
return true; |
|||
} |
|||
|
|||
void InjectedScript::Scope::ignoreExceptionsAndMuteConsole() { |
|||
DCHECK(!m_ignoreExceptionsAndMuteConsole); |
|||
m_ignoreExceptionsAndMuteConsole = true; |
|||
m_inspector->client()->muteMetrics(m_contextGroupId); |
|||
m_inspector->muteExceptions(m_contextGroupId); |
|||
m_previousPauseOnExceptionsState = |
|||
setPauseOnExceptionsState(V8Debugger::DontPauseOnExceptions); |
|||
} |
|||
|
|||
V8Debugger::PauseOnExceptionsState |
|||
InjectedScript::Scope::setPauseOnExceptionsState( |
|||
V8Debugger::PauseOnExceptionsState newState) { |
|||
if (!m_inspector->debugger()->enabled()) return newState; |
|||
V8Debugger::PauseOnExceptionsState presentState = |
|||
m_inspector->debugger()->getPauseOnExceptionsState(); |
|||
if (presentState != newState) |
|||
m_inspector->debugger()->setPauseOnExceptionsState(newState); |
|||
return presentState; |
|||
} |
|||
|
|||
void InjectedScript::Scope::pretendUserGesture() { |
|||
DCHECK(!m_userGesture); |
|||
m_userGesture = true; |
|||
m_inspector->client()->beginUserGesture(); |
|||
} |
|||
|
|||
void InjectedScript::Scope::cleanup() { |
|||
m_commandLineAPIScope.reset(); |
|||
if (!m_context.IsEmpty()) { |
|||
m_context->Exit(); |
|||
m_context.Clear(); |
|||
} |
|||
} |
|||
|
|||
InjectedScript::Scope::~Scope() { |
|||
if (m_ignoreExceptionsAndMuteConsole) { |
|||
setPauseOnExceptionsState(m_previousPauseOnExceptionsState); |
|||
m_inspector->client()->unmuteMetrics(m_contextGroupId); |
|||
m_inspector->unmuteExceptions(m_contextGroupId); |
|||
} |
|||
if (m_userGesture) m_inspector->client()->endUserGesture(); |
|||
cleanup(); |
|||
} |
|||
|
|||
InjectedScript::ContextScope::ContextScope(ErrorString* errorString, |
|||
V8InspectorImpl* inspector, |
|||
int contextGroupId, |
|||
int executionContextId) |
|||
: InjectedScript::Scope(errorString, inspector, contextGroupId), |
|||
m_executionContextId(executionContextId) {} |
|||
|
|||
InjectedScript::ContextScope::~ContextScope() {} |
|||
|
|||
void InjectedScript::ContextScope::findInjectedScript( |
|||
V8InspectorSessionImpl* session) { |
|||
m_injectedScript = |
|||
session->findInjectedScript(m_errorString, m_executionContextId); |
|||
} |
|||
|
|||
InjectedScript::ObjectScope::ObjectScope(ErrorString* errorString, |
|||
V8InspectorImpl* inspector, |
|||
int contextGroupId, |
|||
const String16& remoteObjectId) |
|||
: InjectedScript::Scope(errorString, inspector, contextGroupId), |
|||
m_remoteObjectId(remoteObjectId) {} |
|||
|
|||
InjectedScript::ObjectScope::~ObjectScope() {} |
|||
|
|||
void InjectedScript::ObjectScope::findInjectedScript( |
|||
V8InspectorSessionImpl* session) { |
|||
std::unique_ptr<RemoteObjectId> remoteId = |
|||
RemoteObjectId::parse(m_errorString, m_remoteObjectId); |
|||
if (!remoteId) return; |
|||
InjectedScript* injectedScript = |
|||
session->findInjectedScript(m_errorString, remoteId.get()); |
|||
if (!injectedScript) return; |
|||
m_objectGroupName = injectedScript->objectGroupName(*remoteId); |
|||
if (!injectedScript->findObject(m_errorString, *remoteId, &m_object)) return; |
|||
m_injectedScript = injectedScript; |
|||
} |
|||
|
|||
InjectedScript::CallFrameScope::CallFrameScope(ErrorString* errorString, |
|||
V8InspectorImpl* inspector, |
|||
int contextGroupId, |
|||
const String16& remoteObjectId) |
|||
: InjectedScript::Scope(errorString, inspector, contextGroupId), |
|||
m_remoteCallFrameId(remoteObjectId) {} |
|||
|
|||
InjectedScript::CallFrameScope::~CallFrameScope() {} |
|||
|
|||
void InjectedScript::CallFrameScope::findInjectedScript( |
|||
V8InspectorSessionImpl* session) { |
|||
std::unique_ptr<RemoteCallFrameId> remoteId = |
|||
RemoteCallFrameId::parse(m_errorString, m_remoteCallFrameId); |
|||
if (!remoteId) return; |
|||
m_frameOrdinal = static_cast<size_t>(remoteId->frameOrdinal()); |
|||
m_injectedScript = session->findInjectedScript(m_errorString, remoteId.get()); |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,207 @@ |
|||
/*
|
|||
* Copyright (C) 2012 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_INJECTEDSCRIPT_H_ |
|||
#define V8_INSPECTOR_INJECTEDSCRIPT_H_ |
|||
|
|||
#include "src/base/macros.h" |
|||
#include "src/inspector/injected-script-native.h" |
|||
#include "src/inspector/inspected-context.h" |
|||
#include "src/inspector/protocol/Forward.h" |
|||
#include "src/inspector/protocol/Runtime.h" |
|||
#include "src/inspector/v8-console.h" |
|||
#include "src/inspector/v8-debugger.h" |
|||
|
|||
#include "include/v8.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
class RemoteObjectId; |
|||
class V8FunctionCall; |
|||
class V8InspectorImpl; |
|||
class V8InspectorSessionImpl; |
|||
|
|||
using protocol::ErrorString; |
|||
using protocol::Maybe; |
|||
|
|||
class InjectedScript final { |
|||
public: |
|||
static std::unique_ptr<InjectedScript> create(InspectedContext*); |
|||
~InjectedScript(); |
|||
|
|||
InspectedContext* context() const { return m_context; } |
|||
|
|||
void getProperties( |
|||
ErrorString*, v8::Local<v8::Object>, const String16& groupName, |
|||
bool ownProperties, bool accessorPropertiesOnly, bool generatePreview, |
|||
std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>>* |
|||
result, |
|||
Maybe<protocol::Runtime::ExceptionDetails>*); |
|||
void releaseObject(const String16& objectId); |
|||
|
|||
std::unique_ptr<protocol::Runtime::RemoteObject> wrapObject( |
|||
ErrorString*, v8::Local<v8::Value>, const String16& groupName, |
|||
bool forceValueType = false, bool generatePreview = false) const; |
|||
bool wrapObjectProperty(ErrorString*, v8::Local<v8::Object>, |
|||
v8::Local<v8::Name> key, const String16& groupName, |
|||
bool forceValueType = false, |
|||
bool generatePreview = false) const; |
|||
bool wrapPropertyInArray(ErrorString*, v8::Local<v8::Array>, |
|||
v8::Local<v8::String> property, |
|||
const String16& groupName, |
|||
bool forceValueType = false, |
|||
bool generatePreview = false) const; |
|||
bool wrapObjectsInArray(ErrorString*, v8::Local<v8::Array>, |
|||
const String16& groupName, |
|||
bool forceValueType = false, |
|||
bool generatePreview = false) const; |
|||
std::unique_ptr<protocol::Runtime::RemoteObject> wrapTable( |
|||
v8::Local<v8::Value> table, v8::Local<v8::Value> columns) const; |
|||
|
|||
bool findObject(ErrorString*, const RemoteObjectId&, |
|||
v8::Local<v8::Value>*) const; |
|||
String16 objectGroupName(const RemoteObjectId&) const; |
|||
void releaseObjectGroup(const String16&); |
|||
void setCustomObjectFormatterEnabled(bool); |
|||
v8::MaybeLocal<v8::Value> resolveCallArgument( |
|||
ErrorString*, protocol::Runtime::CallArgument*); |
|||
|
|||
std::unique_ptr<protocol::Runtime::ExceptionDetails> createExceptionDetails( |
|||
ErrorString*, const v8::TryCatch&, const String16& groupName, |
|||
bool generatePreview); |
|||
void wrapEvaluateResult( |
|||
ErrorString*, v8::MaybeLocal<v8::Value> maybeResultValue, |
|||
const v8::TryCatch&, const String16& objectGroup, bool returnByValue, |
|||
bool generatePreview, |
|||
std::unique_ptr<protocol::Runtime::RemoteObject>* result, |
|||
Maybe<protocol::Runtime::ExceptionDetails>*); |
|||
v8::Local<v8::Value> lastEvaluationResult() const; |
|||
|
|||
class Scope { |
|||
public: |
|||
bool initialize(); |
|||
bool installCommandLineAPI(); |
|||
void ignoreExceptionsAndMuteConsole(); |
|||
void pretendUserGesture(); |
|||
v8::Local<v8::Context> context() const { return m_context; } |
|||
InjectedScript* injectedScript() const { return m_injectedScript; } |
|||
const v8::TryCatch& tryCatch() const { return m_tryCatch; } |
|||
|
|||
protected: |
|||
Scope(ErrorString*, V8InspectorImpl*, int contextGroupId); |
|||
virtual ~Scope(); |
|||
virtual void findInjectedScript(V8InspectorSessionImpl*) = 0; |
|||
|
|||
ErrorString* m_errorString; |
|||
V8InspectorImpl* m_inspector; |
|||
int m_contextGroupId; |
|||
InjectedScript* m_injectedScript; |
|||
|
|||
private: |
|||
void cleanup(); |
|||
V8Debugger::PauseOnExceptionsState setPauseOnExceptionsState( |
|||
V8Debugger::PauseOnExceptionsState); |
|||
|
|||
v8::HandleScope m_handleScope; |
|||
v8::TryCatch m_tryCatch; |
|||
v8::Local<v8::Context> m_context; |
|||
std::unique_ptr<V8Console::CommandLineAPIScope> m_commandLineAPIScope; |
|||
bool m_ignoreExceptionsAndMuteConsole; |
|||
V8Debugger::PauseOnExceptionsState m_previousPauseOnExceptionsState; |
|||
bool m_userGesture; |
|||
}; |
|||
|
|||
class ContextScope : public Scope { |
|||
public: |
|||
ContextScope(ErrorString*, V8InspectorImpl*, int contextGroupId, |
|||
int executionContextId); |
|||
~ContextScope(); |
|||
|
|||
private: |
|||
void findInjectedScript(V8InspectorSessionImpl*) override; |
|||
int m_executionContextId; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(ContextScope); |
|||
}; |
|||
|
|||
class ObjectScope : public Scope { |
|||
public: |
|||
ObjectScope(ErrorString*, V8InspectorImpl*, int contextGroupId, |
|||
const String16& remoteObjectId); |
|||
~ObjectScope(); |
|||
const String16& objectGroupName() const { return m_objectGroupName; } |
|||
v8::Local<v8::Value> object() const { return m_object; } |
|||
|
|||
private: |
|||
void findInjectedScript(V8InspectorSessionImpl*) override; |
|||
String16 m_remoteObjectId; |
|||
String16 m_objectGroupName; |
|||
v8::Local<v8::Value> m_object; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(ObjectScope); |
|||
}; |
|||
|
|||
class CallFrameScope : public Scope { |
|||
public: |
|||
CallFrameScope(ErrorString*, V8InspectorImpl*, int contextGroupId, |
|||
const String16& remoteCallFrameId); |
|||
~CallFrameScope(); |
|||
size_t frameOrdinal() const { return m_frameOrdinal; } |
|||
|
|||
private: |
|||
void findInjectedScript(V8InspectorSessionImpl*) override; |
|||
String16 m_remoteCallFrameId; |
|||
size_t m_frameOrdinal; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(CallFrameScope); |
|||
}; |
|||
|
|||
private: |
|||
InjectedScript(InspectedContext*, v8::Local<v8::Object>, |
|||
std::unique_ptr<InjectedScriptNative>); |
|||
v8::Local<v8::Value> v8Value() const; |
|||
v8::MaybeLocal<v8::Value> wrapValue(ErrorString*, v8::Local<v8::Value>, |
|||
const String16& groupName, |
|||
bool forceValueType, |
|||
bool generatePreview) const; |
|||
v8::Local<v8::Object> commandLineAPI(); |
|||
|
|||
InspectedContext* m_context; |
|||
v8::Global<v8::Value> m_value; |
|||
v8::Global<v8::Value> m_lastEvaluationResult; |
|||
std::unique_ptr<InjectedScriptNative> m_native; |
|||
v8::Global<v8::Object> m_commandLineAPI; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(InjectedScript); |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_INJECTEDSCRIPT_H_
|
@ -1,4 +1,4 @@ |
|||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
|||
// 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.
|
|||
|
@ -0,0 +1,84 @@ |
|||
// 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/inspected-context.h" |
|||
|
|||
#include "src/inspector/injected-script.h" |
|||
#include "src/inspector/string-util.h" |
|||
#include "src/inspector/v8-console.h" |
|||
#include "src/inspector/v8-inspector-impl.h" |
|||
#include "src/inspector/v8-value-copier.h" |
|||
|
|||
#include "include/v8-inspector.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
void InspectedContext::weakCallback( |
|||
const v8::WeakCallbackInfo<InspectedContext>& data) { |
|||
InspectedContext* context = data.GetParameter(); |
|||
if (!context->m_context.IsEmpty()) { |
|||
context->m_context.Reset(); |
|||
data.SetSecondPassCallback(&InspectedContext::weakCallback); |
|||
} else { |
|||
context->m_inspector->discardInspectedContext(context->m_contextGroupId, |
|||
context->m_contextId); |
|||
} |
|||
} |
|||
|
|||
void InspectedContext::consoleWeakCallback( |
|||
const v8::WeakCallbackInfo<InspectedContext>& data) { |
|||
data.GetParameter()->m_console.Reset(); |
|||
} |
|||
|
|||
InspectedContext::InspectedContext(V8InspectorImpl* inspector, |
|||
const V8ContextInfo& info, int contextId) |
|||
: m_inspector(inspector), |
|||
m_context(info.context->GetIsolate(), info.context), |
|||
m_contextId(contextId), |
|||
m_contextGroupId(info.contextGroupId), |
|||
m_origin(toString16(info.origin)), |
|||
m_humanReadableName(toString16(info.humanReadableName)), |
|||
m_auxData(toString16(info.auxData)), |
|||
m_reported(false) { |
|||
m_context.SetWeak(this, &InspectedContext::weakCallback, |
|||
v8::WeakCallbackType::kParameter); |
|||
|
|||
v8::Isolate* isolate = m_inspector->isolate(); |
|||
v8::Local<v8::Object> global = info.context->Global(); |
|||
v8::Local<v8::Object> console = |
|||
V8Console::createConsole(this, info.hasMemoryOnConsole); |
|||
if (!global |
|||
->Set(info.context, toV8StringInternalized(isolate, "console"), |
|||
console) |
|||
.FromMaybe(false)) |
|||
return; |
|||
m_console.Reset(isolate, console); |
|||
m_console.SetWeak(this, &InspectedContext::consoleWeakCallback, |
|||
v8::WeakCallbackType::kParameter); |
|||
} |
|||
|
|||
InspectedContext::~InspectedContext() { |
|||
if (!m_context.IsEmpty() && !m_console.IsEmpty()) { |
|||
v8::HandleScope scope(isolate()); |
|||
V8Console::clearInspectedContextIfNeeded(context(), |
|||
m_console.Get(isolate())); |
|||
} |
|||
} |
|||
|
|||
v8::Local<v8::Context> InspectedContext::context() const { |
|||
return m_context.Get(isolate()); |
|||
} |
|||
|
|||
v8::Isolate* InspectedContext::isolate() const { |
|||
return m_inspector->isolate(); |
|||
} |
|||
|
|||
void InspectedContext::createInjectedScript() { |
|||
DCHECK(!m_injectedScript); |
|||
m_injectedScript = InjectedScript::create(this); |
|||
} |
|||
|
|||
void InspectedContext::discardInjectedScript() { m_injectedScript.reset(); } |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,64 @@ |
|||
// 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_INSPECTEDCONTEXT_H_ |
|||
#define V8_INSPECTOR_INSPECTEDCONTEXT_H_ |
|||
|
|||
#include "src/base/macros.h" |
|||
#include "src/inspector/string-16.h" |
|||
|
|||
#include "include/v8.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
class InjectedScript; |
|||
class InjectedScriptHost; |
|||
class V8ContextInfo; |
|||
class V8InspectorImpl; |
|||
|
|||
class InspectedContext { |
|||
public: |
|||
~InspectedContext(); |
|||
|
|||
v8::Local<v8::Context> context() const; |
|||
int contextId() const { return m_contextId; } |
|||
int contextGroupId() const { return m_contextGroupId; } |
|||
String16 origin() const { return m_origin; } |
|||
String16 humanReadableName() const { return m_humanReadableName; } |
|||
String16 auxData() const { return m_auxData; } |
|||
|
|||
bool isReported() const { return m_reported; } |
|||
void setReported(bool reported) { m_reported = reported; } |
|||
|
|||
v8::Isolate* isolate() const; |
|||
V8InspectorImpl* inspector() const { return m_inspector; } |
|||
|
|||
InjectedScript* getInjectedScript() { return m_injectedScript.get(); } |
|||
void createInjectedScript(); |
|||
void discardInjectedScript(); |
|||
|
|||
private: |
|||
friend class V8InspectorImpl; |
|||
InspectedContext(V8InspectorImpl*, const V8ContextInfo&, int contextId); |
|||
static void weakCallback(const v8::WeakCallbackInfo<InspectedContext>&); |
|||
static void consoleWeakCallback( |
|||
const v8::WeakCallbackInfo<InspectedContext>&); |
|||
|
|||
V8InspectorImpl* m_inspector; |
|||
v8::Global<v8::Context> m_context; |
|||
int m_contextId; |
|||
int m_contextGroupId; |
|||
const String16 m_origin; |
|||
const String16 m_humanReadableName; |
|||
const String16 m_auxData; |
|||
bool m_reported; |
|||
std::unique_ptr<InjectedScript> m_injectedScript; |
|||
v8::Global<v8::Object> m_console; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(InspectedContext); |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_INSPECTEDCONTEXT_H_
|
@ -0,0 +1,131 @@ |
|||
# 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. |
|||
|
|||
{ |
|||
'variables': { |
|||
'protocol_path': '../../third_party/WebKit/Source/platform/inspector_protocol', |
|||
}, |
|||
'includes': [ |
|||
'../../../v8/gypfiles/toolchain.gypi', |
|||
'../../../v8/gypfiles/features.gypi', |
|||
'inspector.gypi', |
|||
'../../third_party/WebKit/Source/platform/inspector_protocol/inspector_protocol.gypi', |
|||
], |
|||
'targets': [ |
|||
{ 'target_name': 'inspector_injected_script', |
|||
'type': 'none', |
|||
'actions': [ |
|||
{ |
|||
'action_name': 'convert_js_to_cpp_char_array', |
|||
'inputs': [ |
|||
'build/xxd.py', |
|||
'<(inspector_injected_script_source)', |
|||
], |
|||
'outputs': [ |
|||
'<(inspector_generated_injected_script)', |
|||
], |
|||
'action': [ |
|||
'python', |
|||
'build/xxd.py', |
|||
'InjectedScriptSource_js', |
|||
'injected-script-source.js', |
|||
'<@(_outputs)' |
|||
], |
|||
}, |
|||
], |
|||
# Since this target generates header files, it needs to be a hard dependency. |
|||
'hard_dependency': 1, |
|||
}, |
|||
{ 'target_name': 'inspector_debugger_script', |
|||
'type': 'none', |
|||
'actions': [ |
|||
{ |
|||
'action_name': 'convert_js_to_cpp_char_array', |
|||
'inputs': [ |
|||
'build/xxd.py', |
|||
'<(inspector_debugger_script_source)', |
|||
], |
|||
'outputs': [ |
|||
'<(inspector_generated_debugger_script)', |
|||
], |
|||
'action': [ |
|||
'python', |
|||
'build/xxd.py', |
|||
'DebuggerScript_js', |
|||
'debugger-script.js', |
|||
'<@(_outputs)' |
|||
], |
|||
}, |
|||
], |
|||
# Since this target generates header files, it needs to be a hard dependency. |
|||
'hard_dependency': 1, |
|||
}, |
|||
{ 'target_name': 'protocol_compatibility', |
|||
'type': 'none', |
|||
'actions': [ |
|||
{ |
|||
'action_name': 'protocol_compatibility', |
|||
'inputs': [ |
|||
'js_protocol.json', |
|||
], |
|||
'outputs': [ |
|||
'<@(SHARED_INTERMEDIATE_DIR)/src/js_protocol.stamp', |
|||
], |
|||
'action': [ |
|||
'python', |
|||
'<(protocol_path)/CheckProtocolCompatibility.py', |
|||
'--stamp', '<@(_outputs)', |
|||
'js_protocol.json', |
|||
], |
|||
'message': 'Generating inspector protocol sources from protocol json definition', |
|||
}, |
|||
] |
|||
}, |
|||
{ 'target_name': 'protocol_generated_sources', |
|||
'type': 'none', |
|||
'dependencies': [ 'protocol_compatibility' ], |
|||
'actions': [ |
|||
{ |
|||
'action_name': 'protocol_generated_sources', |
|||
'inputs': [ |
|||
'js_protocol.json', |
|||
'inspector_protocol_config.json', |
|||
'<@(inspector_protocol_files)', |
|||
], |
|||
'outputs': [ |
|||
'<@(inspector_generated_sources)', |
|||
], |
|||
'action': [ |
|||
'python', |
|||
'<(protocol_path)/CodeGenerator.py', |
|||
'--jinja_dir', '../../third_party', |
|||
'--output_base', '<(SHARED_INTERMEDIATE_DIR)/src/inspector', |
|||
'--config', 'inspector_protocol_config.json', |
|||
], |
|||
'message': 'Generating inspector protocol sources from protocol json', |
|||
}, |
|||
] |
|||
}, |
|||
{ |
|||
'target_name': 'standalone_inspector', |
|||
'type': 'static_library', |
|||
'include_dirs': [ |
|||
'<(SHARED_INTERMEDIATE_DIR)', |
|||
'<(SHARED_INTERMEDIATE_DIR)/include', |
|||
'../../include', |
|||
'../..', |
|||
'../../../v8' |
|||
], |
|||
'sources': [ |
|||
'<@(inspector_all_sources)' |
|||
], |
|||
'dependencies': [ |
|||
'protocol_generated_sources', |
|||
'inspector_injected_script', |
|||
'inspector_debugger_script', |
|||
'../../../v8/src/v8.gyp:v8_libplatform', |
|||
], |
|||
}, |
|||
], |
|||
} |
@ -0,0 +1,95 @@ |
|||
# 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. |
|||
|
|||
{ |
|||
'variables': { |
|||
'inspector_generated_sources': [ |
|||
'<(SHARED_INTERMEDIATE_DIR)/src/inspector/protocol/Forward.h', |
|||
'<(SHARED_INTERMEDIATE_DIR)/src/inspector/protocol/Protocol.cpp', |
|||
'<(SHARED_INTERMEDIATE_DIR)/src/inspector/protocol/Protocol.h', |
|||
'<(SHARED_INTERMEDIATE_DIR)/src/inspector/protocol/Console.cpp', |
|||
'<(SHARED_INTERMEDIATE_DIR)/src/inspector/protocol/Console.h', |
|||
'<(SHARED_INTERMEDIATE_DIR)/src/inspector/protocol/Debugger.cpp', |
|||
'<(SHARED_INTERMEDIATE_DIR)/src/inspector/protocol/Debugger.h', |
|||
'<(SHARED_INTERMEDIATE_DIR)/src/inspector/protocol/HeapProfiler.cpp', |
|||
'<(SHARED_INTERMEDIATE_DIR)/src/inspector/protocol/HeapProfiler.h', |
|||
'<(SHARED_INTERMEDIATE_DIR)/src/inspector/protocol/Profiler.cpp', |
|||
'<(SHARED_INTERMEDIATE_DIR)/src/inspector/protocol/Profiler.h', |
|||
'<(SHARED_INTERMEDIATE_DIR)/src/inspector/protocol/Runtime.cpp', |
|||
'<(SHARED_INTERMEDIATE_DIR)/src/inspector/protocol/Runtime.h', |
|||
'<(SHARED_INTERMEDIATE_DIR)/src/inspector/protocol/Schema.cpp', |
|||
'<(SHARED_INTERMEDIATE_DIR)/src/inspector/protocol/Schema.h', |
|||
'<(SHARED_INTERMEDIATE_DIR)/include/inspector/Debugger.h', |
|||
'<(SHARED_INTERMEDIATE_DIR)/include/inspector/Runtime.h', |
|||
'<(SHARED_INTERMEDIATE_DIR)/include/inspector/Schema.h', |
|||
], |
|||
|
|||
'inspector_injected_script_source': 'injected-script-source.js', |
|||
'inspector_generated_injected_script': '<(SHARED_INTERMEDIATE_DIR)/src/inspector/injected-script-source.h', |
|||
'inspector_debugger_script_source': 'debugger-script.js', |
|||
'inspector_generated_debugger_script': '<(SHARED_INTERMEDIATE_DIR)/src/inspector/debugger-script.h', |
|||
|
|||
'inspector_all_sources': [ |
|||
'<@(inspector_generated_sources)', |
|||
'<(inspector_generated_injected_script)', |
|||
'<(inspector_generated_debugger_script)', |
|||
'../../include/v8-inspector.h', |
|||
'../../include/v8-inspector-protocol.h', |
|||
'injected-script.cc', |
|||
'injected-script.h', |
|||
'injected-script-native.cc', |
|||
'injected-script-native.h', |
|||
'inspected-context.cc', |
|||
'inspected-context.h', |
|||
'java-script-call-frame.cc', |
|||
'java-script-call-frame.h', |
|||
'protocol-platform.h', |
|||
'remote-object-id.cc', |
|||
'remote-object-id.h', |
|||
'script-breakpoint.h', |
|||
'search-util.cc', |
|||
'search-util.h', |
|||
'string-16.cc', |
|||
'string-16.h', |
|||
'string-util.cc', |
|||
'string-util.h', |
|||
'v8-console.cc', |
|||
'v8-console.h', |
|||
'v8-console-agent-impl.cc', |
|||
'v8-console-agent-impl.h', |
|||
'v8-console-message.cc', |
|||
'v8-console-message.h', |
|||
'v8-debugger.cc', |
|||
'v8-debugger.h', |
|||
'v8-debugger-agent-impl.cc', |
|||
'v8-debugger-agent-impl.h', |
|||
'v8-debugger-script.cc', |
|||
'v8-debugger-script.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', |
|||
'v8-regex.h', |
|||
'v8-runtime-agent-impl.cc', |
|||
'v8-runtime-agent-impl.h', |
|||
'v8-schema-agent-impl.cc', |
|||
'v8-schema-agent-impl.h', |
|||
'v8-stack-trace-impl.cc', |
|||
'v8-stack-trace-impl.h', |
|||
'v8-value-copier.cc', |
|||
'v8-value-copier.h', |
|||
] |
|||
} |
|||
} |
@ -0,0 +1,25 @@ |
|||
{ |
|||
"protocol": { |
|||
"path": "js_protocol.json", |
|||
"package": "src/inspector/protocol", |
|||
"output": "protocol", |
|||
"namespace": ["v8_inspector", "protocol"] |
|||
}, |
|||
|
|||
"exported": { |
|||
"package": "include/inspector", |
|||
"output": "../../include/inspector", |
|||
"string_header": "v8-inspector.h", |
|||
"string_in": "StringView", |
|||
"string_out": "std::unique_ptr<StringBuffer>", |
|||
"to_string_out": "StringBufferImpl::adopt(%s)", |
|||
"export_macro": "V8_EXPORT" |
|||
}, |
|||
|
|||
"lib": { |
|||
"package": "src/inspector/protocol", |
|||
"output": "protocol", |
|||
"string_header": "src/inspector/string-util.h", |
|||
"platform_header": "src/inspector/protocol-platform.h" |
|||
} |
|||
} |
@ -0,0 +1,162 @@ |
|||
/*
|
|||
* Copyright (c) 2010, 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/java-script-call-frame.h" |
|||
|
|||
#include "src/inspector/string-util.h" |
|||
|
|||
#include "include/v8-debug.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
JavaScriptCallFrame::JavaScriptCallFrame(v8::Local<v8::Context> debuggerContext, |
|||
v8::Local<v8::Object> callFrame) |
|||
: m_isolate(debuggerContext->GetIsolate()), |
|||
m_debuggerContext(m_isolate, debuggerContext), |
|||
m_callFrame(m_isolate, callFrame) {} |
|||
|
|||
JavaScriptCallFrame::~JavaScriptCallFrame() {} |
|||
|
|||
int JavaScriptCallFrame::callV8FunctionReturnInt(const char* name) const { |
|||
v8::HandleScope handleScope(m_isolate); |
|||
v8::MicrotasksScope microtasks(m_isolate, |
|||
v8::MicrotasksScope::kDoNotRunMicrotasks); |
|||
v8::Local<v8::Context> context = |
|||
v8::Local<v8::Context>::New(m_isolate, m_debuggerContext); |
|||
v8::Local<v8::Object> callFrame = |
|||
v8::Local<v8::Object>::New(m_isolate, m_callFrame); |
|||
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast( |
|||
callFrame->Get(context, toV8StringInternalized(m_isolate, name)) |
|||
.ToLocalChecked()); |
|||
v8::Local<v8::Value> result; |
|||
if (!func->Call(context, callFrame, 0, nullptr).ToLocal(&result) || |
|||
!result->IsInt32()) |
|||
return 0; |
|||
return result.As<v8::Int32>()->Value(); |
|||
} |
|||
|
|||
int JavaScriptCallFrame::sourceID() const { |
|||
return callV8FunctionReturnInt("sourceID"); |
|||
} |
|||
|
|||
int JavaScriptCallFrame::line() const { |
|||
return callV8FunctionReturnInt("line"); |
|||
} |
|||
|
|||
int JavaScriptCallFrame::column() const { |
|||
return callV8FunctionReturnInt("column"); |
|||
} |
|||
|
|||
int JavaScriptCallFrame::contextId() const { |
|||
return callV8FunctionReturnInt("contextId"); |
|||
} |
|||
|
|||
bool JavaScriptCallFrame::isAtReturn() const { |
|||
v8::HandleScope handleScope(m_isolate); |
|||
v8::Local<v8::Context> context = |
|||
v8::Local<v8::Context>::New(m_isolate, m_debuggerContext); |
|||
v8::Local<v8::Object> callFrame = |
|||
v8::Local<v8::Object>::New(m_isolate, m_callFrame); |
|||
v8::Local<v8::Value> result; |
|||
if (!callFrame->Get(context, toV8StringInternalized(m_isolate, "isAtReturn")) |
|||
.ToLocal(&result) || |
|||
!result->IsBoolean()) |
|||
return false; |
|||
return result.As<v8::Boolean>()->BooleanValue(context).FromMaybe(false); |
|||
} |
|||
|
|||
v8::Local<v8::Object> JavaScriptCallFrame::details() const { |
|||
v8::MicrotasksScope microtasks(m_isolate, |
|||
v8::MicrotasksScope::kDoNotRunMicrotasks); |
|||
v8::Local<v8::Context> context = |
|||
v8::Local<v8::Context>::New(m_isolate, m_debuggerContext); |
|||
v8::Local<v8::Object> callFrame = |
|||
v8::Local<v8::Object>::New(m_isolate, m_callFrame); |
|||
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast( |
|||
callFrame->Get(context, toV8StringInternalized(m_isolate, "details")) |
|||
.ToLocalChecked()); |
|||
return v8::Local<v8::Object>::Cast( |
|||
func->Call(context, callFrame, 0, nullptr).ToLocalChecked()); |
|||
} |
|||
|
|||
v8::MaybeLocal<v8::Value> JavaScriptCallFrame::evaluate( |
|||
v8::Local<v8::Value> expression) { |
|||
v8::MicrotasksScope microtasks(m_isolate, |
|||
v8::MicrotasksScope::kRunMicrotasks); |
|||
v8::Local<v8::Context> context = |
|||
v8::Local<v8::Context>::New(m_isolate, m_debuggerContext); |
|||
v8::Local<v8::Object> callFrame = |
|||
v8::Local<v8::Object>::New(m_isolate, m_callFrame); |
|||
v8::Local<v8::Function> evalFunction = v8::Local<v8::Function>::Cast( |
|||
callFrame->Get(context, toV8StringInternalized(m_isolate, "evaluate")) |
|||
.ToLocalChecked()); |
|||
return evalFunction->Call(context, callFrame, 1, &expression); |
|||
} |
|||
|
|||
v8::MaybeLocal<v8::Value> JavaScriptCallFrame::restart() { |
|||
v8::MicrotasksScope microtasks(m_isolate, |
|||
v8::MicrotasksScope::kDoNotRunMicrotasks); |
|||
v8::Local<v8::Context> context = |
|||
v8::Local<v8::Context>::New(m_isolate, m_debuggerContext); |
|||
v8::Local<v8::Object> callFrame = |
|||
v8::Local<v8::Object>::New(m_isolate, m_callFrame); |
|||
v8::Local<v8::Function> restartFunction = v8::Local<v8::Function>::Cast( |
|||
callFrame->Get(context, toV8StringInternalized(m_isolate, "restart")) |
|||
.ToLocalChecked()); |
|||
v8::Debug::SetLiveEditEnabled(m_isolate, true); |
|||
v8::MaybeLocal<v8::Value> result = restartFunction->Call( |
|||
m_debuggerContext.Get(m_isolate), callFrame, 0, nullptr); |
|||
v8::Debug::SetLiveEditEnabled(m_isolate, false); |
|||
return result; |
|||
} |
|||
|
|||
v8::MaybeLocal<v8::Value> JavaScriptCallFrame::setVariableValue( |
|||
int scopeNumber, v8::Local<v8::Value> variableName, |
|||
v8::Local<v8::Value> newValue) { |
|||
v8::MicrotasksScope microtasks(m_isolate, |
|||
v8::MicrotasksScope::kDoNotRunMicrotasks); |
|||
v8::Local<v8::Context> context = |
|||
v8::Local<v8::Context>::New(m_isolate, m_debuggerContext); |
|||
v8::Local<v8::Object> callFrame = |
|||
v8::Local<v8::Object>::New(m_isolate, m_callFrame); |
|||
v8::Local<v8::Function> setVariableValueFunction = |
|||
v8::Local<v8::Function>::Cast( |
|||
callFrame |
|||
->Get(context, |
|||
toV8StringInternalized(m_isolate, "setVariableValue")) |
|||
.ToLocalChecked()); |
|||
v8::Local<v8::Value> argv[] = { |
|||
v8::Local<v8::Value>(v8::Integer::New(m_isolate, scopeNumber)), |
|||
variableName, newValue}; |
|||
return setVariableValueFunction->Call(context, callFrame, arraysize(argv), |
|||
argv); |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,997 @@ |
|||
{ |
|||
"version": { "major": "1", "minor": "2" }, |
|||
"domains": [ |
|||
{ |
|||
"domain": "Schema", |
|||
"description": "Provides information about the protocol schema.", |
|||
"types": [ |
|||
{ |
|||
"id": "Domain", |
|||
"type": "object", |
|||
"description": "Description of the protocol domain.", |
|||
"exported": true, |
|||
"properties": [ |
|||
{ "name": "name", "type": "string", "description": "Domain name." }, |
|||
{ "name": "version", "type": "string", "description": "Domain version." } |
|||
] |
|||
} |
|||
], |
|||
"commands": [ |
|||
{ |
|||
"name": "getDomains", |
|||
"description": "Returns supported domains.", |
|||
"handlers": ["browser", "renderer"], |
|||
"returns": [ |
|||
{ "name": "domains", "type": "array", "items": { "$ref": "Domain" }, "description": "List of supported domains." } |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"domain": "Runtime", |
|||
"description": "Runtime domain exposes JavaScript runtime by means of remote evaluation and mirror objects. Evaluation results are returned as mirror object that expose object type, string representation and unique identifier that can be used for further object reference. Original objects are maintained in memory unless they are either explicitly released or are released along with the other objects in their object group.", |
|||
"types": [ |
|||
{ |
|||
"id": "ScriptId", |
|||
"type": "string", |
|||
"description": "Unique script identifier." |
|||
}, |
|||
{ |
|||
"id": "RemoteObjectId", |
|||
"type": "string", |
|||
"description": "Unique object identifier." |
|||
}, |
|||
{ |
|||
"id": "UnserializableValue", |
|||
"type": "string", |
|||
"enum": ["Infinity", "NaN", "-Infinity", "-0"], |
|||
"description": "Primitive value which cannot be JSON-stringified." |
|||
}, |
|||
{ |
|||
"id": "RemoteObject", |
|||
"type": "object", |
|||
"description": "Mirror object referencing original JavaScript object.", |
|||
"exported": true, |
|||
"properties": [ |
|||
{ "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." }, |
|||
{ "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error", "proxy", "promise", "typedarray"], "description": "Object subtype hint. Specified for <code>object</code> type values only." }, |
|||
{ "name": "className", "type": "string", "optional": true, "description": "Object class (constructor) name. Specified for <code>object</code> type values only." }, |
|||
{ "name": "value", "type": "any", "optional": true, "description": "Remote object value in case of primitive values or JSON values (if it was requested)." }, |
|||
{ "name": "unserializableValue", "$ref": "UnserializableValue", "optional": true, "description": "Primitive value which can not be JSON-stringified does not have <code>value</code>, but gets this property." }, |
|||
{ "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, |
|||
{ "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Unique object identifier (for non-primitive values)." }, |
|||
{ "name": "preview", "$ref": "ObjectPreview", "optional": true, "description": "Preview containing abbreviated property values. Specified for <code>object</code> type values only.", "experimental": true }, |
|||
{ "name": "customPreview", "$ref": "CustomPreview", "optional": true, "experimental": true} |
|||
] |
|||
}, |
|||
{ |
|||
"id": "CustomPreview", |
|||
"type": "object", |
|||
"experimental": true, |
|||
"properties": [ |
|||
{ "name": "header", "type": "string"}, |
|||
{ "name": "hasBody", "type": "boolean"}, |
|||
{ "name": "formatterObjectId", "$ref": "RemoteObjectId"}, |
|||
{ "name": "bindRemoteObjectFunctionId", "$ref": "RemoteObjectId" }, |
|||
{ "name": "configObjectId", "$ref": "RemoteObjectId", "optional": true } |
|||
] |
|||
}, |
|||
{ |
|||
"id": "ObjectPreview", |
|||
"type": "object", |
|||
"experimental": true, |
|||
"description": "Object containing abbreviated remote object value.", |
|||
"properties": [ |
|||
{ "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol"], "description": "Object type." }, |
|||
{ "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for <code>object</code> type values only." }, |
|||
{ "name": "description", "type": "string", "optional": true, "description": "String representation of the object." }, |
|||
{ "name": "overflow", "type": "boolean", "description": "True iff some of the properties or entries of the original object did not fit." }, |
|||
{ "name": "properties", "type": "array", "items": { "$ref": "PropertyPreview" }, "description": "List of the properties." }, |
|||
{ "name": "entries", "type": "array", "items": { "$ref": "EntryPreview" }, "optional": true, "description": "List of the entries. Specified for <code>map</code> and <code>set</code> subtype values only." } |
|||
] |
|||
}, |
|||
{ |
|||
"id": "PropertyPreview", |
|||
"type": "object", |
|||
"experimental": true, |
|||
"properties": [ |
|||
{ "name": "name", "type": "string", "description": "Property name." }, |
|||
{ "name": "type", "type": "string", "enum": ["object", "function", "undefined", "string", "number", "boolean", "symbol", "accessor"], "description": "Object type. Accessor means that the property itself is an accessor property." }, |
|||
{ "name": "value", "type": "string", "optional": true, "description": "User-friendly property value string." }, |
|||
{ "name": "valuePreview", "$ref": "ObjectPreview", "optional": true, "description": "Nested value preview." }, |
|||
{ "name": "subtype", "type": "string", "optional": true, "enum": ["array", "null", "node", "regexp", "date", "map", "set", "iterator", "generator", "error"], "description": "Object subtype hint. Specified for <code>object</code> type values only." } |
|||
] |
|||
}, |
|||
{ |
|||
"id": "EntryPreview", |
|||
"type": "object", |
|||
"experimental": true, |
|||
"properties": [ |
|||
{ "name": "key", "$ref": "ObjectPreview", "optional": true, "description": "Preview of the key. Specified for map-like collection entries." }, |
|||
{ "name": "value", "$ref": "ObjectPreview", "description": "Preview of the value." } |
|||
] |
|||
}, |
|||
{ |
|||
"id": "PropertyDescriptor", |
|||
"type": "object", |
|||
"description": "Object property descriptor.", |
|||
"properties": [ |
|||
{ "name": "name", "type": "string", "description": "Property name or symbol description." }, |
|||
{ "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." }, |
|||
{ "name": "writable", "type": "boolean", "optional": true, "description": "True if the value associated with the property may be changed (data descriptors only)." }, |
|||
{ "name": "get", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a getter for the property, or <code>undefined</code> if there is no getter (accessor descriptors only)." }, |
|||
{ "name": "set", "$ref": "RemoteObject", "optional": true, "description": "A function which serves as a setter for the property, or <code>undefined</code> if there is no setter (accessor descriptors only)." }, |
|||
{ "name": "configurable", "type": "boolean", "description": "True if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object." }, |
|||
{ "name": "enumerable", "type": "boolean", "description": "True if this property shows up during enumeration of the properties on the corresponding object." }, |
|||
{ "name": "wasThrown", "type": "boolean", "optional": true, "description": "True if the result was thrown during the evaluation." }, |
|||
{ "name": "isOwn", "optional": true, "type": "boolean", "description": "True if the property is owned for the object." }, |
|||
{ "name": "symbol", "$ref": "RemoteObject", "optional": true, "description": "Property symbol object, if the property is of the <code>symbol</code> type." } |
|||
] |
|||
}, |
|||
{ |
|||
"id": "InternalPropertyDescriptor", |
|||
"type": "object", |
|||
"description": "Object internal property descriptor. This property isn't normally visible in JavaScript code.", |
|||
"properties": [ |
|||
{ "name": "name", "type": "string", "description": "Conventional property name." }, |
|||
{ "name": "value", "$ref": "RemoteObject", "optional": true, "description": "The value associated with the property." } |
|||
] |
|||
}, |
|||
{ |
|||
"id": "CallArgument", |
|||
"type": "object", |
|||
"description": "Represents function call argument. Either remote object id <code>objectId</code>, primitive <code>value</code>, unserializable primitive value or neither of (for undefined) them should be specified.", |
|||
"properties": [ |
|||
{ "name": "value", "type": "any", "optional": true, "description": "Primitive value." }, |
|||
{ "name": "unserializableValue", "$ref": "UnserializableValue", "optional": true, "description": "Primitive value which can not be JSON-stringified." }, |
|||
{ "name": "objectId", "$ref": "RemoteObjectId", "optional": true, "description": "Remote object handle." } |
|||
] |
|||
}, |
|||
{ |
|||
"id": "ExecutionContextId", |
|||
"type": "integer", |
|||
"description": "Id of an execution context." |
|||
}, |
|||
{ |
|||
"id": "ExecutionContextDescription", |
|||
"type": "object", |
|||
"description": "Description of an isolated world.", |
|||
"properties": [ |
|||
{ "name": "id", "$ref": "ExecutionContextId", "description": "Unique id of the execution context. It can be used to specify in which execution context script evaluation should be performed." }, |
|||
{ "name": "origin", "type": "string", "description": "Execution context origin." }, |
|||
{ "name": "name", "type": "string", "description": "Human readable name describing given context." }, |
|||
{ "name": "auxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." } |
|||
] |
|||
}, |
|||
{ |
|||
"id": "ExceptionDetails", |
|||
"type": "object", |
|||
"description": "Detailed information about exception (or error) that was thrown during script compilation or execution.", |
|||
"properties": [ |
|||
{ "name": "exceptionId", "type": "integer", "description": "Exception id." }, |
|||
{ "name": "text", "type": "string", "description": "Exception text, which should be used together with exception object when available." }, |
|||
{ "name": "lineNumber", "type": "integer", "description": "Line number of the exception location (0-based)." }, |
|||
{ "name": "columnNumber", "type": "integer", "description": "Column number of the exception location (0-based)." }, |
|||
{ "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Script ID of the exception location." }, |
|||
{ "name": "url", "type": "string", "optional": true, "description": "URL of the exception location, to be used when the script was not reported." }, |
|||
{ "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace if available." }, |
|||
{ "name": "exception", "$ref": "RemoteObject", "optional": true, "description": "Exception object if available." }, |
|||
{ "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Identifier of the context where exception happened." } |
|||
] |
|||
}, |
|||
{ |
|||
"id": "Timestamp", |
|||
"type": "number", |
|||
"description": "Number of milliseconds since epoch." |
|||
}, |
|||
{ |
|||
"id": "CallFrame", |
|||
"type": "object", |
|||
"description": "Stack entry for runtime errors and assertions.", |
|||
"properties": [ |
|||
{ "name": "functionName", "type": "string", "description": "JavaScript function name." }, |
|||
{ "name": "scriptId", "$ref": "ScriptId", "description": "JavaScript script id." }, |
|||
{ "name": "url", "type": "string", "description": "JavaScript script name or url." }, |
|||
{ "name": "lineNumber", "type": "integer", "description": "JavaScript script line number (0-based)." }, |
|||
{ "name": "columnNumber", "type": "integer", "description": "JavaScript script column number (0-based)." } |
|||
] |
|||
}, |
|||
{ |
|||
"id": "StackTrace", |
|||
"type": "object", |
|||
"description": "Call frames for assertions or error messages.", |
|||
"exported": true, |
|||
"properties": [ |
|||
{ "name": "description", "type": "string", "optional": true, "description": "String label of this stack trace. For async traces this may be a name of the function that initiated the async call." }, |
|||
{ "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "JavaScript function name." }, |
|||
{ "name": "parent", "$ref": "StackTrace", "optional": true, "description": "Asynchronous JavaScript stack trace that preceded this stack, if available." } |
|||
] |
|||
} |
|||
], |
|||
"commands": [ |
|||
{ |
|||
"name": "evaluate", |
|||
"async": true, |
|||
"parameters": [ |
|||
{ "name": "expression", "type": "string", "description": "Expression to evaluate." }, |
|||
{ "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, |
|||
{ "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." }, |
|||
{ "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides <code>setPauseOnException</code> state." }, |
|||
{ "name": "contextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform evaluation. If the parameter is omitted the evaluation will be performed in the context of the inspected page." }, |
|||
{ "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, |
|||
{ "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." }, |
|||
{ "name": "userGesture", "type": "boolean", "optional": true, "experimental": true, "description": "Whether execution should be treated as initiated by user in the UI." }, |
|||
{ "name": "awaitPromise", "type": "boolean", "optional":true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." } |
|||
], |
|||
"returns": [ |
|||
{ "name": "result", "$ref": "RemoteObject", "description": "Evaluation result." }, |
|||
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} |
|||
], |
|||
"description": "Evaluates expression on global object." |
|||
}, |
|||
{ |
|||
"name": "awaitPromise", |
|||
"async": true, |
|||
"parameters": [ |
|||
{ "name": "promiseObjectId", "$ref": "RemoteObjectId", "description": "Identifier of the promise." }, |
|||
{ "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, |
|||
{ "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." } |
|||
], |
|||
"returns": [ |
|||
{ "name": "result", "$ref": "RemoteObject", "description": "Promise result. Will contain rejected value if promise was rejected." }, |
|||
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details if stack strace is available."} |
|||
], |
|||
"description": "Add handler to promise with given promise object id." |
|||
}, |
|||
{ |
|||
"name": "callFunctionOn", |
|||
"async": true, |
|||
"parameters": [ |
|||
{ "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to call function on." }, |
|||
{ "name": "functionDeclaration", "type": "string", "description": "Declaration of the function to call." }, |
|||
{ "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." }, |
|||
{ "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides <code>setPauseOnException</code> state." }, |
|||
{ "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }, |
|||
{ "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." }, |
|||
{ "name": "userGesture", "type": "boolean", "optional": true, "experimental": true, "description": "Whether execution should be treated as initiated by user in the UI." }, |
|||
{ "name": "awaitPromise", "type": "boolean", "optional":true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." } |
|||
], |
|||
"returns": [ |
|||
{ "name": "result", "$ref": "RemoteObject", "description": "Call result." }, |
|||
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} |
|||
], |
|||
"description": "Calls function with given declaration on the given object. Object group of the result is inherited from the target object." |
|||
}, |
|||
{ |
|||
"name": "getProperties", |
|||
"parameters": [ |
|||
{ "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to return properties for." }, |
|||
{ "name": "ownProperties", "optional": true, "type": "boolean", "description": "If true, returns properties belonging only to the element itself, not to its prototype chain." }, |
|||
{ "name": "accessorPropertiesOnly", "optional": true, "type": "boolean", "description": "If true, returns accessor properties (with getter/setter) only; internal properties are not returned either.", "experimental": true }, |
|||
{ "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the results." } |
|||
], |
|||
"returns": [ |
|||
{ "name": "result", "type": "array", "items": { "$ref": "PropertyDescriptor" }, "description": "Object properties." }, |
|||
{ "name": "internalProperties", "optional": true, "type": "array", "items": { "$ref": "InternalPropertyDescriptor" }, "description": "Internal object properties (only of the element itself)." }, |
|||
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} |
|||
], |
|||
"description": "Returns properties of a given object. Object group of the result is inherited from the target object." |
|||
}, |
|||
{ |
|||
"name": "releaseObject", |
|||
"parameters": [ |
|||
{ "name": "objectId", "$ref": "RemoteObjectId", "description": "Identifier of the object to release." } |
|||
], |
|||
"description": "Releases remote object with given id." |
|||
}, |
|||
{ |
|||
"name": "releaseObjectGroup", |
|||
"parameters": [ |
|||
{ "name": "objectGroup", "type": "string", "description": "Symbolic object group name." } |
|||
], |
|||
"description": "Releases all remote objects that belong to a given group." |
|||
}, |
|||
{ |
|||
"name": "runIfWaitingForDebugger", |
|||
"description": "Tells inspected instance to run if it was waiting for debugger to attach." |
|||
}, |
|||
{ |
|||
"name": "enable", |
|||
"description": "Enables reporting of execution contexts creation by means of <code>executionContextCreated</code> event. When the reporting gets enabled the event will be sent immediately for each existing execution context." |
|||
}, |
|||
{ |
|||
"name": "disable", |
|||
"description": "Disables reporting of execution contexts creation." |
|||
}, |
|||
{ |
|||
"name": "discardConsoleEntries", |
|||
"description": "Discards collected exceptions and console API calls." |
|||
}, |
|||
{ |
|||
"name": "setCustomObjectFormatterEnabled", |
|||
"parameters": [ |
|||
{ |
|||
"name": "enabled", |
|||
"type": "boolean" |
|||
} |
|||
], |
|||
"experimental": true |
|||
}, |
|||
{ |
|||
"name": "compileScript", |
|||
"parameters": [ |
|||
{ "name": "expression", "type": "string", "description": "Expression to compile." }, |
|||
{ "name": "sourceURL", "type": "string", "description": "Source url to be set for the script." }, |
|||
{ "name": "persistScript", "type": "boolean", "description": "Specifies whether the compiled script should be persisted." }, |
|||
{ "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform script run. If the parameter is omitted the evaluation will be performed in the context of the inspected page." } |
|||
], |
|||
"returns": [ |
|||
{ "name": "scriptId", "$ref": "ScriptId", "optional": true, "description": "Id of the script." }, |
|||
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} |
|||
], |
|||
"description": "Compiles expression." |
|||
}, |
|||
{ |
|||
"name": "runScript", |
|||
"async": true, |
|||
"parameters": [ |
|||
{ "name": "scriptId", "$ref": "ScriptId", "description": "Id of the script to run." }, |
|||
{ "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Specifies in which execution context to perform script run. If the parameter is omitted the evaluation will be performed in the context of the inspected page." }, |
|||
{ "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." }, |
|||
{ "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides <code>setPauseOnException</code> state." }, |
|||
{ "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Determines whether Command Line API should be available during the evaluation." }, |
|||
{ "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." }, |
|||
{ "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." }, |
|||
{ "name": "awaitPromise", "type": "boolean", "optional": true, "description": "Whether execution should wait for promise to be resolved. If the result of evaluation is not a Promise, it's considered to be an error." } |
|||
], |
|||
"returns": [ |
|||
{ "name": "result", "$ref": "RemoteObject", "description": "Run result." }, |
|||
{ "name": "exceptionDetails", "$ref": "ExceptionDetails", "optional": true, "description": "Exception details."} |
|||
], |
|||
"description": "Runs script with given id in a given context." |
|||
} |
|||
], |
|||
"events": [ |
|||
{ |
|||
"name": "executionContextCreated", |
|||
"parameters": [ |
|||
{ "name": "context", "$ref": "ExecutionContextDescription", "description": "A newly created execution contex." } |
|||
], |
|||
"description": "Issued when new execution context is created." |
|||
}, |
|||
{ |
|||
"name": "executionContextDestroyed", |
|||
"parameters": [ |
|||
{ "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Id of the destroyed context" } |
|||
], |
|||
"description": "Issued when execution context is destroyed." |
|||
}, |
|||
{ |
|||
"name": "executionContextsCleared", |
|||
"description": "Issued when all executionContexts were cleared in browser" |
|||
}, |
|||
{ |
|||
"name": "exceptionThrown", |
|||
"description": "Issued when exception was thrown and unhandled.", |
|||
"parameters": [ |
|||
{ "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp of the exception." }, |
|||
{ "name": "exceptionDetails", "$ref": "ExceptionDetails" } |
|||
] |
|||
}, |
|||
{ |
|||
"name": "exceptionRevoked", |
|||
"description": "Issued when unhandled exception was revoked.", |
|||
"parameters": [ |
|||
{ "name": "reason", "type": "string", "description": "Reason describing why exception was revoked." }, |
|||
{ "name": "exceptionId", "type": "integer", "description": "The id of revoked exception, as reported in <code>exceptionUnhandled</code>." } |
|||
] |
|||
}, |
|||
{ |
|||
"name": "consoleAPICalled", |
|||
"description": "Issued when console API was called.", |
|||
"parameters": [ |
|||
{ "name": "type", "type": "string", "enum": ["log", "debug", "info", "error", "warning", "dir", "dirxml", "table", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert", "profile", "profileEnd"], "description": "Type of the call." }, |
|||
{ "name": "args", "type": "array", "items": { "$ref": "RemoteObject" }, "description": "Call arguments." }, |
|||
{ "name": "executionContextId", "$ref": "ExecutionContextId", "description": "Identifier of the context where the call was made." }, |
|||
{ "name": "timestamp", "$ref": "Timestamp", "description": "Call timestamp." }, |
|||
{ "name": "stackTrace", "$ref": "StackTrace", "optional": true, "description": "Stack trace captured when the call was made." } |
|||
] |
|||
}, |
|||
{ |
|||
"name": "inspectRequested", |
|||
"description": "Issued when object should be inspected (for example, as a result of inspect() command line API call).", |
|||
"parameters": [ |
|||
{ "name": "object", "$ref": "RemoteObject" }, |
|||
{ "name": "hints", "type": "object" } |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"domain": "Debugger", |
|||
"description": "Debugger domain exposes JavaScript debugging capabilities. It allows setting and removing breakpoints, stepping through execution, exploring stack traces, etc.", |
|||
"dependencies": ["Runtime"], |
|||
"types": [ |
|||
{ |
|||
"id": "BreakpointId", |
|||
"type": "string", |
|||
"description": "Breakpoint identifier." |
|||
}, |
|||
{ |
|||
"id": "CallFrameId", |
|||
"type": "string", |
|||
"description": "Call frame identifier." |
|||
}, |
|||
{ |
|||
"id": "Location", |
|||
"type": "object", |
|||
"properties": [ |
|||
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Script identifier as reported in the <code>Debugger.scriptParsed</code>." }, |
|||
{ "name": "lineNumber", "type": "integer", "description": "Line number in the script (0-based)." }, |
|||
{ "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the script (0-based)." } |
|||
], |
|||
"description": "Location in the source code." |
|||
}, |
|||
{ |
|||
"id": "ScriptPosition", |
|||
"experimental": true, |
|||
"type": "object", |
|||
"properties": [ |
|||
{ "name": "lineNumber", "type": "integer" }, |
|||
{ "name": "columnNumber", "type": "integer" } |
|||
], |
|||
"description": "Location in the source code." |
|||
}, |
|||
{ |
|||
"id": "CallFrame", |
|||
"type": "object", |
|||
"properties": [ |
|||
{ "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier. This identifier is only valid while the virtual machine is paused." }, |
|||
{ "name": "functionName", "type": "string", "description": "Name of the JavaScript function called on this call frame." }, |
|||
{ "name": "functionLocation", "$ref": "Location", "optional": true, "experimental": true, "description": "Location in the source code." }, |
|||
{ "name": "location", "$ref": "Location", "description": "Location in the source code." }, |
|||
{ "name": "scopeChain", "type": "array", "items": { "$ref": "Scope" }, "description": "Scope chain for this call frame." }, |
|||
{ "name": "this", "$ref": "Runtime.RemoteObject", "description": "<code>this</code> object for this call frame." }, |
|||
{ "name": "returnValue", "$ref": "Runtime.RemoteObject", "optional": true, "description": "The value being returned, if the function is at return point." } |
|||
], |
|||
"description": "JavaScript call frame. Array of call frames form the call stack." |
|||
}, |
|||
{ |
|||
"id": "Scope", |
|||
"type": "object", |
|||
"properties": [ |
|||
{ "name": "type", "type": "string", "enum": ["global", "local", "with", "closure", "catch", "block", "script"], "description": "Scope type." }, |
|||
{ "name": "object", "$ref": "Runtime.RemoteObject", "description": "Object representing the scope. For <code>global</code> and <code>with</code> scopes it represents the actual object; for the rest of the scopes, it is artificial transient object enumerating scope variables as its properties." }, |
|||
{ "name": "name", "type": "string", "optional": true }, |
|||
{ "name": "startLocation", "$ref": "Location", "optional": true, "description": "Location in the source code where scope starts" }, |
|||
{ "name": "endLocation", "$ref": "Location", "optional": true, "description": "Location in the source code where scope ends" } |
|||
], |
|||
"description": "Scope description." |
|||
}, |
|||
{ |
|||
"id": "SearchMatch", |
|||
"type": "object", |
|||
"description": "Search match for resource.", |
|||
"exported": true, |
|||
"properties": [ |
|||
{ "name": "lineNumber", "type": "number", "description": "Line number in resource content." }, |
|||
{ "name": "lineContent", "type": "string", "description": "Line with match content." } |
|||
], |
|||
"experimental": true |
|||
} |
|||
], |
|||
"commands": [ |
|||
{ |
|||
"name": "enable", |
|||
"description": "Enables debugger for the given page. Clients should not assume that the debugging has been enabled until the result for this command is received." |
|||
}, |
|||
{ |
|||
"name": "disable", |
|||
"description": "Disables debugger for given page." |
|||
}, |
|||
{ |
|||
"name": "setBreakpointsActive", |
|||
"parameters": [ |
|||
{ "name": "active", "type": "boolean", "description": "New value for breakpoints active state." } |
|||
], |
|||
"description": "Activates / deactivates all breakpoints on the page." |
|||
}, |
|||
{ |
|||
"name": "setSkipAllPauses", |
|||
"parameters": [ |
|||
{ "name": "skip", "type": "boolean", "description": "New value for skip pauses state." } |
|||
], |
|||
"description": "Makes page not interrupt on any pauses (breakpoint, exception, dom exception etc)." |
|||
}, |
|||
{ |
|||
"name": "setBreakpointByUrl", |
|||
"parameters": [ |
|||
{ "name": "lineNumber", "type": "integer", "description": "Line number to set breakpoint at." }, |
|||
{ "name": "url", "type": "string", "optional": true, "description": "URL of the resources to set breakpoint on." }, |
|||
{ "name": "urlRegex", "type": "string", "optional": true, "description": "Regex pattern for the URLs of the resources to set breakpoints on. Either <code>url</code> or <code>urlRegex</code> must be specified." }, |
|||
{ "name": "columnNumber", "type": "integer", "optional": true, "description": "Offset in the line to set breakpoint at." }, |
|||
{ "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } |
|||
], |
|||
"returns": [ |
|||
{ "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, |
|||
{ "name": "locations", "type": "array", "items": { "$ref": "Location" }, "description": "List of the locations this breakpoint resolved into upon addition." } |
|||
], |
|||
"description": "Sets JavaScript breakpoint at given location specified either by URL or URL regex. Once this command is issued, all existing parsed scripts will have breakpoints resolved and returned in <code>locations</code> property. Further matching script parsing will result in subsequent <code>breakpointResolved</code> events issued. This logical breakpoint will survive page reloads." |
|||
}, |
|||
{ |
|||
"name": "setBreakpoint", |
|||
"parameters": [ |
|||
{ "name": "location", "$ref": "Location", "description": "Location to set breakpoint in." }, |
|||
{ "name": "condition", "type": "string", "optional": true, "description": "Expression to use as a breakpoint condition. When specified, debugger will only stop on the breakpoint if this expression evaluates to true." } |
|||
], |
|||
"returns": [ |
|||
{ "name": "breakpointId", "$ref": "BreakpointId", "description": "Id of the created breakpoint for further reference." }, |
|||
{ "name": "actualLocation", "$ref": "Location", "description": "Location this breakpoint resolved into." } |
|||
], |
|||
"description": "Sets JavaScript breakpoint at a given location." |
|||
}, |
|||
{ |
|||
"name": "removeBreakpoint", |
|||
"parameters": [ |
|||
{ "name": "breakpointId", "$ref": "BreakpointId" } |
|||
], |
|||
"description": "Removes JavaScript breakpoint." |
|||
}, |
|||
{ |
|||
"name": "continueToLocation", |
|||
"parameters": [ |
|||
{ "name": "location", "$ref": "Location", "description": "Location to continue to." } |
|||
], |
|||
"description": "Continues execution until specific location is reached." |
|||
}, |
|||
{ |
|||
"name": "stepOver", |
|||
"description": "Steps over the statement." |
|||
}, |
|||
{ |
|||
"name": "stepInto", |
|||
"description": "Steps into the function call." |
|||
}, |
|||
{ |
|||
"name": "stepOut", |
|||
"description": "Steps out of the function call." |
|||
}, |
|||
{ |
|||
"name": "pause", |
|||
"description": "Stops on the next JavaScript statement." |
|||
}, |
|||
{ |
|||
"name": "resume", |
|||
"description": "Resumes JavaScript execution." |
|||
}, |
|||
{ |
|||
"name": "searchInContent", |
|||
"parameters": [ |
|||
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to search in." }, |
|||
{ "name": "query", "type": "string", "description": "String to search for." }, |
|||
{ "name": "caseSensitive", "type": "boolean", "optional": true, "description": "If true, search is case sensitive." }, |
|||
{ "name": "isRegex", "type": "boolean", "optional": true, "description": "If true, treats string parameter as regex." } |
|||
], |
|||
"returns": [ |
|||
{ "name": "result", "type": "array", "items": { "$ref": "SearchMatch" }, "description": "List of search matches." } |
|||
], |
|||
"experimental": true, |
|||
"description": "Searches for given string in script content." |
|||
}, |
|||
{ |
|||
"name": "setScriptSource", |
|||
"parameters": [ |
|||
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to edit." }, |
|||
{ "name": "scriptSource", "type": "string", "description": "New content of the script." }, |
|||
{ "name": "dryRun", "type": "boolean", "optional": true, "description": " If true the change will not actually be applied. Dry run may be used to get result description without actually modifying the code." } |
|||
], |
|||
"returns": [ |
|||
{ "name": "callFrames", "type": "array", "optional": true, "items": { "$ref": "CallFrame" }, "description": "New stack trace in case editing has happened while VM was stopped." }, |
|||
{ "name": "stackChanged", "type": "boolean", "optional": true, "description": "Whether current call stack was modified after applying the changes." }, |
|||
{ "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." }, |
|||
{ "name": "exceptionDetails", "optional": true, "$ref": "Runtime.ExceptionDetails", "description": "Exception details if any." } |
|||
], |
|||
"description": "Edits JavaScript source live." |
|||
}, |
|||
{ |
|||
"name": "restartFrame", |
|||
"parameters": [ |
|||
{ "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." } |
|||
], |
|||
"returns": [ |
|||
{ "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "New stack trace." }, |
|||
{ "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." } |
|||
], |
|||
"description": "Restarts particular call frame from the beginning." |
|||
}, |
|||
{ |
|||
"name": "getScriptSource", |
|||
"parameters": [ |
|||
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script to get source for." } |
|||
], |
|||
"returns": [ |
|||
{ "name": "scriptSource", "type": "string", "description": "Script source." } |
|||
], |
|||
"description": "Returns source for the script with given id." |
|||
}, |
|||
{ |
|||
"name": "setPauseOnExceptions", |
|||
"parameters": [ |
|||
{ "name": "state", "type": "string", "enum": ["none", "uncaught", "all"], "description": "Pause on exceptions mode." } |
|||
], |
|||
"description": "Defines pause on exceptions state. Can be set to stop on all exceptions, uncaught exceptions or no exceptions. Initial pause on exceptions state is <code>none</code>." |
|||
}, |
|||
{ |
|||
"name": "evaluateOnCallFrame", |
|||
"parameters": [ |
|||
{ "name": "callFrameId", "$ref": "CallFrameId", "description": "Call frame identifier to evaluate on." }, |
|||
{ "name": "expression", "type": "string", "description": "Expression to evaluate." }, |
|||
{ "name": "objectGroup", "type": "string", "optional": true, "description": "String object group name to put result into (allows rapid releasing resulting object handles using <code>releaseObjectGroup</code>)." }, |
|||
{ "name": "includeCommandLineAPI", "type": "boolean", "optional": true, "description": "Specifies whether command line API should be available to the evaluated expression, defaults to false." }, |
|||
{ "name": "silent", "type": "boolean", "optional": true, "description": "In silent mode exceptions thrown during evaluation are not reported and do not pause execution. Overrides <code>setPauseOnException</code> state." }, |
|||
{ "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object that should be sent by value." }, |
|||
{ "name": "generatePreview", "type": "boolean", "optional": true, "experimental": true, "description": "Whether preview should be generated for the result." } |
|||
], |
|||
"returns": [ |
|||
{ "name": "result", "$ref": "Runtime.RemoteObject", "description": "Object wrapper for the evaluation result." }, |
|||
{ "name": "exceptionDetails", "$ref": "Runtime.ExceptionDetails", "optional": true, "description": "Exception details."} |
|||
], |
|||
"description": "Evaluates expression on a given call frame." |
|||
}, |
|||
{ |
|||
"name": "setVariableValue", |
|||
"parameters": [ |
|||
{ "name": "scopeNumber", "type": "integer", "description": "0-based number of scope as was listed in scope chain. Only 'local', 'closure' and 'catch' scope types are allowed. Other scopes could be manipulated manually." }, |
|||
{ "name": "variableName", "type": "string", "description": "Variable name." }, |
|||
{ "name": "newValue", "$ref": "Runtime.CallArgument", "description": "New variable value." }, |
|||
{ "name": "callFrameId", "$ref": "CallFrameId", "description": "Id of callframe that holds variable." } |
|||
], |
|||
"description": "Changes value of variable in a callframe. Object-based scopes are not supported and must be mutated manually." |
|||
}, |
|||
{ |
|||
"name": "setAsyncCallStackDepth", |
|||
"parameters": [ |
|||
{ "name": "maxDepth", "type": "integer", "description": "Maximum depth of async call stacks. Setting to <code>0</code> will effectively disable collecting async call stacks (default)." } |
|||
], |
|||
"description": "Enables or disables async call stacks tracking." |
|||
}, |
|||
{ |
|||
"name": "setBlackboxPatterns", |
|||
"parameters": [ |
|||
{ "name": "patterns", "type": "array", "items": { "type": "string" }, "description": "Array of regexps that will be used to check script url for blackbox state." } |
|||
], |
|||
"experimental": true, |
|||
"description": "Replace previous blackbox patterns with passed ones. Forces backend to skip stepping/pausing in scripts with url matching one of the patterns. VM will try to leave blackboxed script by performing 'step in' several times, finally resorting to 'step out' if unsuccessful." |
|||
}, |
|||
{ |
|||
"name": "setBlackboxedRanges", |
|||
"parameters": [ |
|||
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Id of the script." }, |
|||
{ "name": "positions", "type": "array", "items": { "$ref": "ScriptPosition" } } |
|||
], |
|||
"experimental": true, |
|||
"description": "Makes backend skip steps in the script in blackboxed ranges. VM will try leave blacklisted scripts by performing 'step in' several times, finally resorting to 'step out' if unsuccessful. Positions array contains positions where blackbox state is changed. First interval isn't blackboxed. Array should be sorted." |
|||
} |
|||
], |
|||
"events": [ |
|||
{ |
|||
"name": "scriptParsed", |
|||
"parameters": [ |
|||
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." }, |
|||
{ "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, |
|||
{ "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, |
|||
{ "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, |
|||
{ "name": "endLine", "type": "integer", "description": "Last line of the script." }, |
|||
{ "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, |
|||
{ "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context." }, |
|||
{ "name": "hash", "type": "string", "description": "Content hash of the script."}, |
|||
{ "name": "executionContextAuxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." }, |
|||
{ "name": "isLiveEdit", "type": "boolean", "optional": true, "description": "True, if this script is generated as a result of the live edit operation.", "experimental": true }, |
|||
{ "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, |
|||
{ "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "experimental": true } |
|||
], |
|||
"description": "Fired when virtual machine parses script. This event is also fired for all known and uncollected scripts upon enabling debugger." |
|||
}, |
|||
{ |
|||
"name": "scriptFailedToParse", |
|||
"parameters": [ |
|||
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "Identifier of the script parsed." }, |
|||
{ "name": "url", "type": "string", "description": "URL or name of the script parsed (if any)." }, |
|||
{ "name": "startLine", "type": "integer", "description": "Line offset of the script within the resource with given URL (for script tags)." }, |
|||
{ "name": "startColumn", "type": "integer", "description": "Column offset of the script within the resource with given URL." }, |
|||
{ "name": "endLine", "type": "integer", "description": "Last line of the script." }, |
|||
{ "name": "endColumn", "type": "integer", "description": "Length of the last line of the script." }, |
|||
{ "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "description": "Specifies script creation context." }, |
|||
{ "name": "hash", "type": "string", "description": "Content hash of the script."}, |
|||
{ "name": "executionContextAuxData", "type": "object", "optional": true, "description": "Embedder-specific auxiliary data." }, |
|||
{ "name": "sourceMapURL", "type": "string", "optional": true, "description": "URL of source map associated with script (if any)." }, |
|||
{ "name": "hasSourceURL", "type": "boolean", "optional": true, "description": "True, if this script has sourceURL.", "experimental": true } |
|||
], |
|||
"description": "Fired when virtual machine fails to parse the script." |
|||
}, |
|||
{ |
|||
"name": "breakpointResolved", |
|||
"parameters": [ |
|||
{ "name": "breakpointId", "$ref": "BreakpointId", "description": "Breakpoint unique identifier." }, |
|||
{ "name": "location", "$ref": "Location", "description": "Actual breakpoint location." } |
|||
], |
|||
"description": "Fired when breakpoint is resolved to an actual script and location." |
|||
}, |
|||
{ |
|||
"name": "paused", |
|||
"parameters": [ |
|||
{ "name": "callFrames", "type": "array", "items": { "$ref": "CallFrame" }, "description": "Call stack the virtual machine stopped on." }, |
|||
{ "name": "reason", "type": "string", "enum": [ "XHR", "DOM", "EventListener", "exception", "assert", "debugCommand", "promiseRejection", "other" ], "description": "Pause reason.", "exported": true }, |
|||
{ "name": "data", "type": "object", "optional": true, "description": "Object containing break-specific auxiliary properties." }, |
|||
{ "name": "hitBreakpoints", "type": "array", "optional": true, "items": { "type": "string" }, "description": "Hit breakpoints IDs" }, |
|||
{ "name": "asyncStackTrace", "$ref": "Runtime.StackTrace", "optional": true, "description": "Async stack trace, if any." } |
|||
], |
|||
"description": "Fired when the virtual machine stopped on breakpoint or exception or any other stop criteria." |
|||
}, |
|||
{ |
|||
"name": "resumed", |
|||
"description": "Fired when the virtual machine resumed execution." |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"domain": "Console", |
|||
"description": "This domain is deprecated - use Runtime or Log instead.", |
|||
"dependencies": ["Runtime"], |
|||
"deprecated": true, |
|||
"types": [ |
|||
{ |
|||
"id": "ConsoleMessage", |
|||
"type": "object", |
|||
"description": "Console message.", |
|||
"properties": [ |
|||
{ "name": "source", "type": "string", "enum": ["xml", "javascript", "network", "console-api", "storage", "appcache", "rendering", "security", "other", "deprecation", "worker"], "description": "Message source." }, |
|||
{ "name": "level", "type": "string", "enum": ["log", "warning", "error", "debug", "info"], "description": "Message severity." }, |
|||
{ "name": "text", "type": "string", "description": "Message text." }, |
|||
{ "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." }, |
|||
{ "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message (1-based)." }, |
|||
{ "name": "column", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message (1-based)." } |
|||
] |
|||
} |
|||
], |
|||
"commands": [ |
|||
{ |
|||
"name": "enable", |
|||
"description": "Enables console domain, sends the messages collected so far to the client by means of the <code>messageAdded</code> notification." |
|||
}, |
|||
{ |
|||
"name": "disable", |
|||
"description": "Disables console domain, prevents further console messages from being reported to the client." |
|||
}, |
|||
{ |
|||
"name": "clearMessages", |
|||
"description": "Does nothing." |
|||
} |
|||
], |
|||
"events": [ |
|||
{ |
|||
"name": "messageAdded", |
|||
"parameters": [ |
|||
{ "name": "message", "$ref": "ConsoleMessage", "description": "Console message that has been added." } |
|||
], |
|||
"description": "Issued when new console message is added." |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"domain": "Profiler", |
|||
"dependencies": ["Runtime", "Debugger"], |
|||
"types": [ |
|||
{ |
|||
"id": "ProfileNode", |
|||
"type": "object", |
|||
"description": "Profile node. Holds callsite information, execution statistics and child nodes.", |
|||
"properties": [ |
|||
{ "name": "id", "type": "integer", "description": "Unique id of the node." }, |
|||
{ "name": "callFrame", "$ref": "Runtime.CallFrame", "description": "Function location." }, |
|||
{ "name": "hitCount", "type": "integer", "optional": true, "experimental": true, "description": "Number of samples where this node was on top of the call stack." }, |
|||
{ "name": "children", "type": "array", "items": { "type": "integer" }, "optional": true, "description": "Child node ids." }, |
|||
{ "name": "deoptReason", "type": "string", "optional": true, "description": "The reason of being not optimized. The function may be deoptimized or marked as don't optimize."}, |
|||
{ "name": "positionTicks", "type": "array", "items": { "$ref": "PositionTickInfo" }, "optional": true, "experimental": true, "description": "An array of source position ticks." } |
|||
] |
|||
}, |
|||
{ |
|||
"id": "Profile", |
|||
"type": "object", |
|||
"description": "Profile.", |
|||
"properties": [ |
|||
{ "name": "nodes", "type": "array", "items": { "$ref": "ProfileNode" }, "description": "The list of profile nodes. First item is the root node." }, |
|||
{ "name": "startTime", "type": "number", "description": "Profiling start timestamp in microseconds." }, |
|||
{ "name": "endTime", "type": "number", "description": "Profiling end timestamp in microseconds." }, |
|||
{ "name": "samples", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Ids of samples top nodes." }, |
|||
{ "name": "timeDeltas", "optional": true, "type": "array", "items": { "type": "integer" }, "description": "Time intervals between adjacent samples in microseconds. The first delta is relative to the profile startTime." } |
|||
] |
|||
}, |
|||
{ |
|||
"id": "PositionTickInfo", |
|||
"type": "object", |
|||
"experimental": true, |
|||
"description": "Specifies a number of samples attributed to a certain source position.", |
|||
"properties": [ |
|||
{ "name": "line", "type": "integer", "description": "Source line number (1-based)." }, |
|||
{ "name": "ticks", "type": "integer", "description": "Number of samples attributed to the source line." } |
|||
] |
|||
} |
|||
], |
|||
"commands": [ |
|||
{ |
|||
"name": "enable" |
|||
}, |
|||
{ |
|||
"name": "disable" |
|||
}, |
|||
{ |
|||
"name": "setSamplingInterval", |
|||
"parameters": [ |
|||
{ "name": "interval", "type": "integer", "description": "New sampling interval in microseconds." } |
|||
], |
|||
"description": "Changes CPU profiler sampling interval. Must be called before CPU profiles recording started." |
|||
}, |
|||
{ |
|||
"name": "start" |
|||
}, |
|||
{ |
|||
"name": "stop", |
|||
"returns": [ |
|||
{ "name": "profile", "$ref": "Profile", "description": "Recorded profile." } |
|||
] |
|||
} |
|||
], |
|||
"events": [ |
|||
{ |
|||
"name": "consoleProfileStarted", |
|||
"parameters": [ |
|||
{ "name": "id", "type": "string" }, |
|||
{ "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profile()." }, |
|||
{ "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." } |
|||
], |
|||
"description": "Sent when new profile recodring is started using console.profile() call." |
|||
}, |
|||
{ |
|||
"name": "consoleProfileFinished", |
|||
"parameters": [ |
|||
{ "name": "id", "type": "string" }, |
|||
{ "name": "location", "$ref": "Debugger.Location", "description": "Location of console.profileEnd()." }, |
|||
{ "name": "profile", "$ref": "Profile" }, |
|||
{ "name": "title", "type": "string", "optional": true, "description": "Profile title passed as an argument to console.profile()." } |
|||
] |
|||
} |
|||
] |
|||
}, |
|||
{ |
|||
"domain": "HeapProfiler", |
|||
"dependencies": ["Runtime"], |
|||
"experimental": true, |
|||
"types": [ |
|||
{ |
|||
"id": "HeapSnapshotObjectId", |
|||
"type": "string", |
|||
"description": "Heap snapshot object id." |
|||
}, |
|||
{ |
|||
"id": "SamplingHeapProfileNode", |
|||
"type": "object", |
|||
"description": "Sampling Heap Profile node. Holds callsite information, allocation statistics and child nodes.", |
|||
"properties": [ |
|||
{ "name": "callFrame", "$ref": "Runtime.CallFrame", "description": "Function location." }, |
|||
{ "name": "selfSize", "type": "number", "description": "Allocations size in bytes for the node excluding children." }, |
|||
{ "name": "children", "type": "array", "items": { "$ref": "SamplingHeapProfileNode" }, "description": "Child nodes." } |
|||
] |
|||
}, |
|||
{ |
|||
"id": "SamplingHeapProfile", |
|||
"type": "object", |
|||
"description": "Profile.", |
|||
"properties": [ |
|||
{ "name": "head", "$ref": "SamplingHeapProfileNode" } |
|||
] |
|||
} |
|||
], |
|||
"commands": [ |
|||
{ |
|||
"name": "enable" |
|||
}, |
|||
{ |
|||
"name": "disable" |
|||
}, |
|||
{ |
|||
"name": "startTrackingHeapObjects", |
|||
"parameters": [ |
|||
{ "name": "trackAllocations", "type": "boolean", "optional": true } |
|||
] |
|||
}, |
|||
{ |
|||
"name": "stopTrackingHeapObjects", |
|||
"parameters": [ |
|||
{ "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken when the tracking is stopped." } |
|||
] |
|||
}, |
|||
{ |
|||
"name": "takeHeapSnapshot", |
|||
"parameters": [ |
|||
{ "name": "reportProgress", "type": "boolean", "optional": true, "description": "If true 'reportHeapSnapshotProgress' events will be generated while snapshot is being taken." } |
|||
] |
|||
}, |
|||
{ |
|||
"name": "collectGarbage" |
|||
}, |
|||
{ |
|||
"name": "getObjectByHeapObjectId", |
|||
"parameters": [ |
|||
{ "name": "objectId", "$ref": "HeapSnapshotObjectId" }, |
|||
{ "name": "objectGroup", "type": "string", "optional": true, "description": "Symbolic group name that can be used to release multiple objects." } |
|||
], |
|||
"returns": [ |
|||
{ "name": "result", "$ref": "Runtime.RemoteObject", "description": "Evaluation result." } |
|||
] |
|||
}, |
|||
{ |
|||
"name": "addInspectedHeapObject", |
|||
"parameters": [ |
|||
{ "name": "heapObjectId", "$ref": "HeapSnapshotObjectId", "description": "Heap snapshot object id to be accessible by means of $x command line API." } |
|||
], |
|||
"description": "Enables console to refer to the node with given id via $x (see Command Line API for more details $x functions)." |
|||
}, |
|||
{ |
|||
"name": "getHeapObjectId", |
|||
"parameters": [ |
|||
{ "name": "objectId", "$ref": "Runtime.RemoteObjectId", "description": "Identifier of the object to get heap object id for." } |
|||
], |
|||
"returns": [ |
|||
{ "name": "heapSnapshotObjectId", "$ref": "HeapSnapshotObjectId", "description": "Id of the heap snapshot object corresponding to the passed remote object id." } |
|||
] |
|||
}, |
|||
{ |
|||
"name": "startSampling", |
|||
"parameters": [ |
|||
{ "name": "samplingInterval", "type": "number", "optional": true, "description": "Average sample interval in bytes. Poisson distribution is used for the intervals. The default value is 32768 bytes." } |
|||
] |
|||
}, |
|||
{ |
|||
"name": "stopSampling", |
|||
"returns": [ |
|||
{ "name": "profile", "$ref": "SamplingHeapProfile", "description": "Recorded sampling heap profile." } |
|||
] |
|||
} |
|||
], |
|||
"events": [ |
|||
{ |
|||
"name": "addHeapSnapshotChunk", |
|||
"parameters": [ |
|||
{ "name": "chunk", "type": "string" } |
|||
] |
|||
}, |
|||
{ |
|||
"name": "resetProfiles" |
|||
}, |
|||
{ |
|||
"name": "reportHeapSnapshotProgress", |
|||
"parameters": [ |
|||
{ "name": "done", "type": "integer" }, |
|||
{ "name": "total", "type": "integer" }, |
|||
{ "name": "finished", "type": "boolean", "optional": true } |
|||
] |
|||
}, |
|||
{ |
|||
"name": "lastSeenObjectId", |
|||
"description": "If heap objects tracking has been started then backend regulary sends a current value for last seen object id and corresponding timestamp. If the were changes in the heap since last event then one or more heapStatsUpdate events will be sent before a new lastSeenObjectId event.", |
|||
"parameters": [ |
|||
{ "name": "lastSeenObjectId", "type": "integer" }, |
|||
{ "name": "timestamp", "type": "number" } |
|||
] |
|||
}, |
|||
{ |
|||
"name": "heapStatsUpdate", |
|||
"description": "If heap objects tracking has been started then backend may send update for one or more fragments", |
|||
"parameters": [ |
|||
{ "name": "statsUpdate", "type": "array", "items": { "type": "integer" }, "description": "An array of triplets. Each triplet describes a fragment. The first integer is the fragment index, the second integer is a total count of objects for the fragment, the third integer is a total size of the objects for the fragment."} |
|||
] |
|||
} |
|||
] |
|||
}] |
|||
} |
@ -0,0 +1,21 @@ |
|||
// 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_PROTOCOLPLATFORM_H_ |
|||
#define V8_INSPECTOR_PROTOCOLPLATFORM_H_ |
|||
|
|||
#include <memory> |
|||
|
|||
#include "src/base/logging.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
template <typename T> |
|||
std::unique_ptr<T> wrapUnique(T* ptr) { |
|||
return std::unique_ptr<T>(ptr); |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_PROTOCOLPLATFORM_H_
|
@ -0,0 +1,76 @@ |
|||
// 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/remote-object-id.h" |
|||
|
|||
#include "src/inspector/protocol/Protocol.h" |
|||
#include "src/inspector/string-util.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
RemoteObjectIdBase::RemoteObjectIdBase() : m_injectedScriptId(0) {} |
|||
|
|||
std::unique_ptr<protocol::DictionaryValue> |
|||
RemoteObjectIdBase::parseInjectedScriptId(const String16& objectId) { |
|||
std::unique_ptr<protocol::Value> parsedValue = protocol::parseJSON(objectId); |
|||
if (!parsedValue || parsedValue->type() != protocol::Value::TypeObject) |
|||
return nullptr; |
|||
|
|||
std::unique_ptr<protocol::DictionaryValue> parsedObjectId( |
|||
protocol::DictionaryValue::cast(parsedValue.release())); |
|||
bool success = |
|||
parsedObjectId->getInteger("injectedScriptId", &m_injectedScriptId); |
|||
if (success) return parsedObjectId; |
|||
return nullptr; |
|||
} |
|||
|
|||
RemoteObjectId::RemoteObjectId() : RemoteObjectIdBase(), m_id(0) {} |
|||
|
|||
std::unique_ptr<RemoteObjectId> RemoteObjectId::parse( |
|||
ErrorString* errorString, const String16& objectId) { |
|||
std::unique_ptr<RemoteObjectId> result(new RemoteObjectId()); |
|||
std::unique_ptr<protocol::DictionaryValue> parsedObjectId = |
|||
result->parseInjectedScriptId(objectId); |
|||
if (!parsedObjectId) { |
|||
*errorString = "Invalid remote object id"; |
|||
return nullptr; |
|||
} |
|||
|
|||
bool success = parsedObjectId->getInteger("id", &result->m_id); |
|||
if (!success) { |
|||
*errorString = "Invalid remote object id"; |
|||
return nullptr; |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
RemoteCallFrameId::RemoteCallFrameId() |
|||
: RemoteObjectIdBase(), m_frameOrdinal(0) {} |
|||
|
|||
std::unique_ptr<RemoteCallFrameId> RemoteCallFrameId::parse( |
|||
ErrorString* errorString, const String16& objectId) { |
|||
std::unique_ptr<RemoteCallFrameId> result(new RemoteCallFrameId()); |
|||
std::unique_ptr<protocol::DictionaryValue> parsedObjectId = |
|||
result->parseInjectedScriptId(objectId); |
|||
if (!parsedObjectId) { |
|||
*errorString = "Invalid call frame id"; |
|||
return nullptr; |
|||
} |
|||
|
|||
bool success = parsedObjectId->getInteger("ordinal", &result->m_frameOrdinal); |
|||
if (!success) { |
|||
*errorString = "Invalid call frame id"; |
|||
return nullptr; |
|||
} |
|||
|
|||
return result; |
|||
} |
|||
|
|||
String16 RemoteCallFrameId::serialize(int injectedScriptId, int frameOrdinal) { |
|||
return "{\"ordinal\":" + String16::fromInteger(frameOrdinal) + |
|||
",\"injectedScriptId\":" + String16::fromInteger(injectedScriptId) + |
|||
"}"; |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,58 @@ |
|||
// 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_REMOTEOBJECTID_H_ |
|||
#define V8_INSPECTOR_REMOTEOBJECTID_H_ |
|||
|
|||
#include "src/inspector/protocol/Forward.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
using protocol::ErrorString; |
|||
|
|||
class RemoteObjectIdBase { |
|||
public: |
|||
int contextId() const { return m_injectedScriptId; } |
|||
|
|||
protected: |
|||
RemoteObjectIdBase(); |
|||
~RemoteObjectIdBase() {} |
|||
|
|||
std::unique_ptr<protocol::DictionaryValue> parseInjectedScriptId( |
|||
const String16&); |
|||
|
|||
int m_injectedScriptId; |
|||
}; |
|||
|
|||
class RemoteObjectId final : public RemoteObjectIdBase { |
|||
public: |
|||
static std::unique_ptr<RemoteObjectId> parse(ErrorString*, const String16&); |
|||
~RemoteObjectId() {} |
|||
int id() const { return m_id; } |
|||
|
|||
private: |
|||
RemoteObjectId(); |
|||
|
|||
int m_id; |
|||
}; |
|||
|
|||
class RemoteCallFrameId final : public RemoteObjectIdBase { |
|||
public: |
|||
static std::unique_ptr<RemoteCallFrameId> parse(ErrorString*, |
|||
const String16&); |
|||
~RemoteCallFrameId() {} |
|||
|
|||
int frameOrdinal() const { return m_frameOrdinal; } |
|||
|
|||
static String16 serialize(int injectedScriptId, int frameOrdinal); |
|||
|
|||
private: |
|||
RemoteCallFrameId(); |
|||
|
|||
int m_frameOrdinal; |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_REMOTEOBJECTID_H_
|
@ -0,0 +1,164 @@ |
|||
// 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/search-util.h" |
|||
|
|||
#include "src/inspector/protocol/Protocol.h" |
|||
#include "src/inspector/v8-inspector-impl.h" |
|||
#include "src/inspector/v8-inspector-session-impl.h" |
|||
#include "src/inspector/v8-regex.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
namespace { |
|||
|
|||
String16 findMagicComment(const String16& content, const String16& name, |
|||
bool multiline) { |
|||
DCHECK(name.find("=") == String16::kNotFound); |
|||
size_t length = content.length(); |
|||
size_t nameLength = name.length(); |
|||
|
|||
size_t pos = length; |
|||
size_t equalSignPos = 0; |
|||
size_t closingCommentPos = 0; |
|||
while (true) { |
|||
pos = content.reverseFind(name, pos); |
|||
if (pos == String16::kNotFound) return String16(); |
|||
|
|||
// Check for a /\/[\/*][@#][ \t]/ regexp (length of 4) before found name.
|
|||
if (pos < 4) return String16(); |
|||
pos -= 4; |
|||
if (content[pos] != '/') continue; |
|||
if ((content[pos + 1] != '/' || multiline) && |
|||
(content[pos + 1] != '*' || !multiline)) |
|||
continue; |
|||
if (content[pos + 2] != '#' && content[pos + 2] != '@') continue; |
|||
if (content[pos + 3] != ' ' && content[pos + 3] != '\t') continue; |
|||
equalSignPos = pos + 4 + nameLength; |
|||
if (equalSignPos < length && content[equalSignPos] != '=') continue; |
|||
if (multiline) { |
|||
closingCommentPos = content.find("*/", equalSignPos + 1); |
|||
if (closingCommentPos == String16::kNotFound) return String16(); |
|||
} |
|||
|
|||
break; |
|||
} |
|||
|
|||
DCHECK(equalSignPos); |
|||
DCHECK(!multiline || closingCommentPos); |
|||
size_t urlPos = equalSignPos + 1; |
|||
String16 match = multiline |
|||
? content.substring(urlPos, closingCommentPos - urlPos) |
|||
: content.substring(urlPos); |
|||
|
|||
size_t newLine = match.find("\n"); |
|||
if (newLine != String16::kNotFound) match = match.substring(0, newLine); |
|||
match = match.stripWhiteSpace(); |
|||
|
|||
for (size_t i = 0; i < match.length(); ++i) { |
|||
UChar c = match[i]; |
|||
if (c == '"' || c == '\'' || c == ' ' || c == '\t') return ""; |
|||
} |
|||
|
|||
return match; |
|||
} |
|||
|
|||
String16 createSearchRegexSource(const String16& text) { |
|||
String16Builder result; |
|||
|
|||
for (size_t i = 0; i < text.length(); i++) { |
|||
UChar c = text[i]; |
|||
if (c == '[' || c == ']' || c == '(' || c == ')' || c == '{' || c == '}' || |
|||
c == '+' || c == '-' || c == '*' || c == '.' || c == ',' || c == '?' || |
|||
c == '\\' || c == '^' || c == '$' || c == '|') { |
|||
result.append('\\'); |
|||
} |
|||
result.append(c); |
|||
} |
|||
|
|||
return result.toString(); |
|||
} |
|||
|
|||
std::unique_ptr<std::vector<size_t>> lineEndings(const String16& text) { |
|||
std::unique_ptr<std::vector<size_t>> result(new std::vector<size_t>()); |
|||
|
|||
const String16 lineEndString = "\n"; |
|||
size_t start = 0; |
|||
while (start < text.length()) { |
|||
size_t lineEnd = text.find(lineEndString, start); |
|||
if (lineEnd == String16::kNotFound) break; |
|||
|
|||
result->push_back(lineEnd); |
|||
start = lineEnd + 1; |
|||
} |
|||
result->push_back(text.length()); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
std::vector<std::pair<int, String16>> scriptRegexpMatchesByLines( |
|||
const V8Regex& regex, const String16& text) { |
|||
std::vector<std::pair<int, String16>> result; |
|||
if (text.isEmpty()) return result; |
|||
|
|||
std::unique_ptr<std::vector<size_t>> endings(lineEndings(text)); |
|||
size_t size = endings->size(); |
|||
size_t start = 0; |
|||
for (size_t lineNumber = 0; lineNumber < size; ++lineNumber) { |
|||
size_t lineEnd = endings->at(lineNumber); |
|||
String16 line = text.substring(start, lineEnd - start); |
|||
if (line.length() && line[line.length() - 1] == '\r') |
|||
line = line.substring(0, line.length() - 1); |
|||
|
|||
int matchLength; |
|||
if (regex.match(line, 0, &matchLength) != -1) |
|||
result.push_back(std::pair<int, String16>(lineNumber, line)); |
|||
|
|||
start = lineEnd + 1; |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Debugger::SearchMatch> buildObjectForSearchMatch( |
|||
int lineNumber, const String16& lineContent) { |
|||
return protocol::Debugger::SearchMatch::create() |
|||
.setLineNumber(lineNumber) |
|||
.setLineContent(lineContent) |
|||
.build(); |
|||
} |
|||
|
|||
std::unique_ptr<V8Regex> createSearchRegex(V8InspectorImpl* inspector, |
|||
const String16& query, |
|||
bool caseSensitive, bool isRegex) { |
|||
String16 regexSource = isRegex ? query : createSearchRegexSource(query); |
|||
return wrapUnique(new V8Regex(inspector, regexSource, caseSensitive)); |
|||
} |
|||
|
|||
} // namespace
|
|||
|
|||
std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> |
|||
searchInTextByLinesImpl(V8InspectorSession* session, const String16& text, |
|||
const String16& query, const bool caseSensitive, |
|||
const bool isRegex) { |
|||
std::unique_ptr<V8Regex> regex = createSearchRegex( |
|||
static_cast<V8InspectorSessionImpl*>(session)->inspector(), query, |
|||
caseSensitive, isRegex); |
|||
std::vector<std::pair<int, String16>> matches = |
|||
scriptRegexpMatchesByLines(*regex.get(), text); |
|||
|
|||
std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> result; |
|||
for (const auto& match : matches) |
|||
result.push_back(buildObjectForSearchMatch(match.first, match.second)); |
|||
return result; |
|||
} |
|||
|
|||
String16 findSourceURL(const String16& content, bool multiline) { |
|||
return findMagicComment(content, "sourceURL", multiline); |
|||
} |
|||
|
|||
String16 findSourceMapURL(const String16& content, bool multiline) { |
|||
return findMagicComment(content, "sourceMappingURL", multiline); |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,24 @@ |
|||
// 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_SEARCHUTIL_H_ |
|||
#define V8_INSPECTOR_SEARCHUTIL_H_ |
|||
|
|||
#include "src/inspector/protocol/Debugger.h" |
|||
#include "src/inspector/string-util.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
class V8InspectorSession; |
|||
|
|||
String16 findSourceURL(const String16& content, bool multiline); |
|||
String16 findSourceMapURL(const String16& content, bool multiline); |
|||
std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> |
|||
searchInTextByLinesImpl(V8InspectorSession*, const String16& text, |
|||
const String16& query, bool caseSensitive, |
|||
bool isRegex); |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_SEARCHUTIL_H_
|
@ -0,0 +1,523 @@ |
|||
// 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/string-16.h" |
|||
|
|||
#include <algorithm> |
|||
#include <cctype> |
|||
#include <cstdlib> |
|||
#include <cstring> |
|||
#include <limits> |
|||
#include <locale> |
|||
#include <string> |
|||
|
|||
#include "src/base/platform/platform.h" |
|||
#include "src/inspector/protocol-platform.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
namespace { |
|||
|
|||
bool isASCII(UChar c) { return !(c & ~0x7F); } |
|||
|
|||
bool isSpaceOrNewLine(UChar c) { |
|||
return isASCII(c) && c <= ' ' && (c == ' ' || (c <= 0xD && c >= 0x9)); |
|||
} |
|||
|
|||
int charactersToInteger(const UChar* characters, size_t length, |
|||
bool* ok = nullptr) { |
|||
std::vector<char> buffer; |
|||
buffer.reserve(length + 1); |
|||
for (size_t i = 0; i < length; ++i) { |
|||
if (!isASCII(characters[i])) { |
|||
if (ok) *ok = false; |
|||
return 0; |
|||
} |
|||
buffer.push_back(static_cast<char>(characters[i])); |
|||
} |
|||
buffer.push_back('\0'); |
|||
|
|||
char* endptr; |
|||
int64_t result = |
|||
static_cast<int64_t>(std::strtol(buffer.data(), &endptr, 10)); |
|||
if (ok) { |
|||
*ok = !(*endptr) && result <= std::numeric_limits<int>::max() && |
|||
result >= std::numeric_limits<int>::min(); |
|||
} |
|||
return static_cast<int>(result); |
|||
} |
|||
|
|||
const UChar replacementCharacter = 0xFFFD; |
|||
using UChar32 = uint32_t; |
|||
|
|||
inline int inlineUTF8SequenceLengthNonASCII(char b0) { |
|||
if ((b0 & 0xC0) != 0xC0) return 0; |
|||
if ((b0 & 0xE0) == 0xC0) return 2; |
|||
if ((b0 & 0xF0) == 0xE0) return 3; |
|||
if ((b0 & 0xF8) == 0xF0) return 4; |
|||
return 0; |
|||
} |
|||
|
|||
inline int inlineUTF8SequenceLength(char b0) { |
|||
return isASCII(b0) ? 1 : inlineUTF8SequenceLengthNonASCII(b0); |
|||
} |
|||
|
|||
// Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
|
|||
// into the first byte, depending on how many bytes follow. There are
|
|||
// as many entries in this table as there are UTF-8 sequence types.
|
|||
// (I.e., one byte sequence, two byte... etc.). Remember that sequences
|
|||
// for *legal* UTF-8 will be 4 or fewer bytes total.
|
|||
static const unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, |
|||
0xF0, 0xF8, 0xFC}; |
|||
|
|||
typedef enum { |
|||
conversionOK, // conversion successful
|
|||
sourceExhausted, // partial character in source, but hit end
|
|||
targetExhausted, // insuff. room in target for conversion
|
|||
sourceIllegal // source sequence is illegal/malformed
|
|||
} ConversionResult; |
|||
|
|||
ConversionResult convertUTF16ToUTF8(const UChar** sourceStart, |
|||
const UChar* sourceEnd, char** targetStart, |
|||
char* targetEnd, bool strict) { |
|||
ConversionResult result = conversionOK; |
|||
const UChar* source = *sourceStart; |
|||
char* target = *targetStart; |
|||
while (source < sourceEnd) { |
|||
UChar32 ch; |
|||
uint32_t bytesToWrite = 0; |
|||
const UChar32 byteMask = 0xBF; |
|||
const UChar32 byteMark = 0x80; |
|||
const UChar* oldSource = |
|||
source; // In case we have to back up because of target overflow.
|
|||
ch = static_cast<uint16_t>(*source++); |
|||
// If we have a surrogate pair, convert to UChar32 first.
|
|||
if (ch >= 0xD800 && ch <= 0xDBFF) { |
|||
// If the 16 bits following the high surrogate are in the source buffer...
|
|||
if (source < sourceEnd) { |
|||
UChar32 ch2 = static_cast<uint16_t>(*source); |
|||
// If it's a low surrogate, convert to UChar32.
|
|||
if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { |
|||
ch = ((ch - 0xD800) << 10) + (ch2 - 0xDC00) + 0x0010000; |
|||
++source; |
|||
} else if (strict) { // it's an unpaired high surrogate
|
|||
--source; // return to the illegal value itself
|
|||
result = sourceIllegal; |
|||
break; |
|||
} |
|||
} else { // We don't have the 16 bits following the high surrogate.
|
|||
--source; // return to the high surrogate
|
|||
result = sourceExhausted; |
|||
break; |
|||
} |
|||
} else if (strict) { |
|||
// UTF-16 surrogate values are illegal in UTF-32
|
|||
if (ch >= 0xDC00 && ch <= 0xDFFF) { |
|||
--source; // return to the illegal value itself
|
|||
result = sourceIllegal; |
|||
break; |
|||
} |
|||
} |
|||
// Figure out how many bytes the result will require
|
|||
if (ch < (UChar32)0x80) { |
|||
bytesToWrite = 1; |
|||
} else if (ch < (UChar32)0x800) { |
|||
bytesToWrite = 2; |
|||
} else if (ch < (UChar32)0x10000) { |
|||
bytesToWrite = 3; |
|||
} else if (ch < (UChar32)0x110000) { |
|||
bytesToWrite = 4; |
|||
} else { |
|||
bytesToWrite = 3; |
|||
ch = replacementCharacter; |
|||
} |
|||
|
|||
target += bytesToWrite; |
|||
if (target > targetEnd) { |
|||
source = oldSource; // Back up source pointer!
|
|||
target -= bytesToWrite; |
|||
result = targetExhausted; |
|||
break; |
|||
} |
|||
switch (bytesToWrite) { // note: everything falls through.
|
|||
case 4: |
|||
*--target = static_cast<char>((ch | byteMark) & byteMask); |
|||
ch >>= 6; |
|||
case 3: |
|||
*--target = static_cast<char>((ch | byteMark) & byteMask); |
|||
ch >>= 6; |
|||
case 2: |
|||
*--target = static_cast<char>((ch | byteMark) & byteMask); |
|||
ch >>= 6; |
|||
case 1: |
|||
*--target = static_cast<char>(ch | firstByteMark[bytesToWrite]); |
|||
} |
|||
target += bytesToWrite; |
|||
} |
|||
*sourceStart = source; |
|||
*targetStart = target; |
|||
return result; |
|||
} |
|||
|
|||
/**
|
|||
* Is this code point a BMP code point (U+0000..U+ffff)? |
|||
* @param c 32-bit code point |
|||
* @return TRUE or FALSE |
|||
* @stable ICU 2.8 |
|||
*/ |
|||
#define U_IS_BMP(c) ((uint32_t)(c) <= 0xffff) |
|||
|
|||
/**
|
|||
* Is this code point a supplementary code point (U+10000..U+10ffff)? |
|||
* @param c 32-bit code point |
|||
* @return TRUE or FALSE |
|||
* @stable ICU 2.8 |
|||
*/ |
|||
#define U_IS_SUPPLEMENTARY(c) ((uint32_t)((c)-0x10000) <= 0xfffff) |
|||
|
|||
/**
|
|||
* Is this code point a surrogate (U+d800..U+dfff)? |
|||
* @param c 32-bit code point |
|||
* @return TRUE or FALSE |
|||
* @stable ICU 2.4 |
|||
*/ |
|||
#define U_IS_SURROGATE(c) (((c)&0xfffff800) == 0xd800) |
|||
|
|||
/**
|
|||
* Get the lead surrogate (0xd800..0xdbff) for a |
|||
* supplementary code point (0x10000..0x10ffff). |
|||
* @param supplementary 32-bit code point (U+10000..U+10ffff) |
|||
* @return lead surrogate (U+d800..U+dbff) for supplementary |
|||
* @stable ICU 2.4 |
|||
*/ |
|||
#define U16_LEAD(supplementary) (UChar)(((supplementary) >> 10) + 0xd7c0) |
|||
|
|||
/**
|
|||
* Get the trail surrogate (0xdc00..0xdfff) for a |
|||
* supplementary code point (0x10000..0x10ffff). |
|||
* @param supplementary 32-bit code point (U+10000..U+10ffff) |
|||
* @return trail surrogate (U+dc00..U+dfff) for supplementary |
|||
* @stable ICU 2.4 |
|||
*/ |
|||
#define U16_TRAIL(supplementary) (UChar)(((supplementary)&0x3ff) | 0xdc00) |
|||
|
|||
// This must be called with the length pre-determined by the first byte.
|
|||
// If presented with a length > 4, this returns false. The Unicode
|
|||
// definition of UTF-8 goes up to 4-byte sequences.
|
|||
static bool isLegalUTF8(const unsigned char* source, int length) { |
|||
unsigned char a; |
|||
const unsigned char* srcptr = source + length; |
|||
switch (length) { |
|||
default: |
|||
return false; |
|||
// Everything else falls through when "true"...
|
|||
case 4: |
|||
if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; |
|||
case 3: |
|||
if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; |
|||
case 2: |
|||
if ((a = (*--srcptr)) > 0xBF) return false; |
|||
|
|||
// no fall-through in this inner switch
|
|||
switch (*source) { |
|||
case 0xE0: |
|||
if (a < 0xA0) return false; |
|||
break; |
|||
case 0xED: |
|||
if (a > 0x9F) return false; |
|||
break; |
|||
case 0xF0: |
|||
if (a < 0x90) return false; |
|||
break; |
|||
case 0xF4: |
|||
if (a > 0x8F) return false; |
|||
break; |
|||
default: |
|||
if (a < 0x80) return false; |
|||
} |
|||
|
|||
case 1: |
|||
if (*source >= 0x80 && *source < 0xC2) return false; |
|||
} |
|||
if (*source > 0xF4) return false; |
|||
return true; |
|||
} |
|||
|
|||
// Magic values subtracted from a buffer value during UTF8 conversion.
|
|||
// This table contains as many values as there might be trailing bytes
|
|||
// in a UTF-8 sequence.
|
|||
static const UChar32 offsetsFromUTF8[6] = {0x00000000UL, |
|||
0x00003080UL, |
|||
0x000E2080UL, |
|||
0x03C82080UL, |
|||
static_cast<UChar32>(0xFA082080UL), |
|||
static_cast<UChar32>(0x82082080UL)}; |
|||
|
|||
static inline UChar32 readUTF8Sequence(const char*& sequence, size_t length) { |
|||
UChar32 character = 0; |
|||
|
|||
// The cases all fall through.
|
|||
switch (length) { |
|||
case 6: |
|||
character += static_cast<unsigned char>(*sequence++); |
|||
character <<= 6; |
|||
case 5: |
|||
character += static_cast<unsigned char>(*sequence++); |
|||
character <<= 6; |
|||
case 4: |
|||
character += static_cast<unsigned char>(*sequence++); |
|||
character <<= 6; |
|||
case 3: |
|||
character += static_cast<unsigned char>(*sequence++); |
|||
character <<= 6; |
|||
case 2: |
|||
character += static_cast<unsigned char>(*sequence++); |
|||
character <<= 6; |
|||
case 1: |
|||
character += static_cast<unsigned char>(*sequence++); |
|||
} |
|||
|
|||
return character - offsetsFromUTF8[length - 1]; |
|||
} |
|||
|
|||
ConversionResult convertUTF8ToUTF16(const char** sourceStart, |
|||
const char* sourceEnd, UChar** targetStart, |
|||
UChar* targetEnd, bool* sourceAllASCII, |
|||
bool strict) { |
|||
ConversionResult result = conversionOK; |
|||
const char* source = *sourceStart; |
|||
UChar* target = *targetStart; |
|||
UChar orAllData = 0; |
|||
while (source < sourceEnd) { |
|||
int utf8SequenceLength = inlineUTF8SequenceLength(*source); |
|||
if (sourceEnd - source < utf8SequenceLength) { |
|||
result = sourceExhausted; |
|||
break; |
|||
} |
|||
// Do this check whether lenient or strict
|
|||
if (!isLegalUTF8(reinterpret_cast<const unsigned char*>(source), |
|||
utf8SequenceLength)) { |
|||
result = sourceIllegal; |
|||
break; |
|||
} |
|||
|
|||
UChar32 character = readUTF8Sequence(source, utf8SequenceLength); |
|||
|
|||
if (target >= targetEnd) { |
|||
source -= utf8SequenceLength; // Back up source pointer!
|
|||
result = targetExhausted; |
|||
break; |
|||
} |
|||
|
|||
if (U_IS_BMP(character)) { |
|||
// UTF-16 surrogate values are illegal in UTF-32
|
|||
if (U_IS_SURROGATE(character)) { |
|||
if (strict) { |
|||
source -= utf8SequenceLength; // return to the illegal value itself
|
|||
result = sourceIllegal; |
|||
break; |
|||
} |
|||
*target++ = replacementCharacter; |
|||
orAllData |= replacementCharacter; |
|||
} else { |
|||
*target++ = static_cast<UChar>(character); // normal case
|
|||
orAllData |= character; |
|||
} |
|||
} else if (U_IS_SUPPLEMENTARY(character)) { |
|||
// target is a character in range 0xFFFF - 0x10FFFF
|
|||
if (target + 1 >= targetEnd) { |
|||
source -= utf8SequenceLength; // Back up source pointer!
|
|||
result = targetExhausted; |
|||
break; |
|||
} |
|||
*target++ = U16_LEAD(character); |
|||
*target++ = U16_TRAIL(character); |
|||
orAllData = 0xffff; |
|||
} else { |
|||
if (strict) { |
|||
source -= utf8SequenceLength; // return to the start
|
|||
result = sourceIllegal; |
|||
break; // Bail out; shouldn't continue
|
|||
} else { |
|||
*target++ = replacementCharacter; |
|||
orAllData |= replacementCharacter; |
|||
} |
|||
} |
|||
} |
|||
*sourceStart = source; |
|||
*targetStart = target; |
|||
|
|||
if (sourceAllASCII) *sourceAllASCII = !(orAllData & ~0x7f); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
// Helper to write a three-byte UTF-8 code point to the buffer, caller must
|
|||
// check room is available.
|
|||
static inline void putUTF8Triple(char*& buffer, UChar ch) { |
|||
*buffer++ = static_cast<char>(((ch >> 12) & 0x0F) | 0xE0); |
|||
*buffer++ = static_cast<char>(((ch >> 6) & 0x3F) | 0x80); |
|||
*buffer++ = static_cast<char>((ch & 0x3F) | 0x80); |
|||
} |
|||
|
|||
} // namespace
|
|||
|
|||
// static
|
|||
String16 String16::fromInteger(int number) { |
|||
const size_t kBufferSize = 50; |
|||
char buffer[kBufferSize]; |
|||
v8::base::OS::SNPrintF(buffer, kBufferSize, "%d", number); |
|||
return String16(buffer); |
|||
} |
|||
|
|||
// static
|
|||
String16 String16::fromInteger(size_t number) { |
|||
const size_t kBufferSize = 50; |
|||
char buffer[kBufferSize]; |
|||
v8::base::OS::SNPrintF(buffer, kBufferSize, "%zu", number); |
|||
return String16(buffer); |
|||
} |
|||
|
|||
// static
|
|||
String16 String16::fromDouble(double number) { |
|||
const size_t kBufferSize = 100; |
|||
char buffer[kBufferSize]; |
|||
v8::base::OS::SNPrintF(buffer, kBufferSize, "%f", number); |
|||
return String16(buffer); |
|||
} |
|||
|
|||
// static
|
|||
String16 String16::fromDoublePrecision3(double number) { |
|||
const size_t kBufferSize = 100; |
|||
char buffer[kBufferSize]; |
|||
v8::base::OS::SNPrintF(buffer, kBufferSize, "%.3g", number); |
|||
return String16(buffer); |
|||
} |
|||
|
|||
// static
|
|||
String16 String16::fromDoublePrecision6(double number) { |
|||
const size_t kBufferSize = 100; |
|||
char buffer[kBufferSize]; |
|||
v8::base::OS::SNPrintF(buffer, kBufferSize, "%.6g", number); |
|||
return String16(buffer); |
|||
} |
|||
|
|||
int String16::toInteger(bool* ok) const { |
|||
return charactersToInteger(characters16(), length(), ok); |
|||
} |
|||
|
|||
String16 String16::stripWhiteSpace() const { |
|||
if (!length()) return String16(); |
|||
|
|||
size_t start = 0; |
|||
size_t end = length() - 1; |
|||
|
|||
// skip white space from start
|
|||
while (start <= end && isSpaceOrNewLine(characters16()[start])) ++start; |
|||
|
|||
// only white space
|
|||
if (start > end) return String16(); |
|||
|
|||
// skip white space from end
|
|||
while (end && isSpaceOrNewLine(characters16()[end])) --end; |
|||
|
|||
if (!start && end == length() - 1) return *this; |
|||
return String16(characters16() + start, end + 1 - start); |
|||
} |
|||
|
|||
String16Builder::String16Builder() {} |
|||
|
|||
void String16Builder::append(const String16& s) { |
|||
m_buffer.insert(m_buffer.end(), s.characters16(), |
|||
s.characters16() + s.length()); |
|||
} |
|||
|
|||
void String16Builder::append(UChar c) { m_buffer.push_back(c); } |
|||
|
|||
void String16Builder::append(char c) { |
|||
UChar u = c; |
|||
m_buffer.push_back(u); |
|||
} |
|||
|
|||
void String16Builder::append(const UChar* characters, size_t length) { |
|||
m_buffer.insert(m_buffer.end(), characters, characters + length); |
|||
} |
|||
|
|||
void String16Builder::append(const char* characters, size_t length) { |
|||
m_buffer.insert(m_buffer.end(), characters, characters + length); |
|||
} |
|||
|
|||
String16 String16Builder::toString() { |
|||
return String16(m_buffer.data(), m_buffer.size()); |
|||
} |
|||
|
|||
void String16Builder::reserveCapacity(size_t capacity) { |
|||
m_buffer.reserve(capacity); |
|||
} |
|||
|
|||
String16 String16::fromUTF8(const char* stringStart, size_t length) { |
|||
if (!stringStart || !length) return String16(); |
|||
|
|||
std::vector<UChar> buffer(length); |
|||
UChar* bufferStart = buffer.data(); |
|||
|
|||
UChar* bufferCurrent = bufferStart; |
|||
const char* stringCurrent = stringStart; |
|||
if (convertUTF8ToUTF16(&stringCurrent, stringStart + length, &bufferCurrent, |
|||
bufferCurrent + buffer.size(), 0, |
|||
true) != conversionOK) |
|||
return String16(); |
|||
|
|||
size_t utf16Length = bufferCurrent - bufferStart; |
|||
return String16(bufferStart, utf16Length); |
|||
} |
|||
|
|||
std::string String16::utf8() const { |
|||
size_t length = this->length(); |
|||
|
|||
if (!length) return std::string(""); |
|||
|
|||
// Allocate a buffer big enough to hold all the characters
|
|||
// (an individual UTF-16 UChar can only expand to 3 UTF-8 bytes).
|
|||
// Optimization ideas, if we find this function is hot:
|
|||
// * We could speculatively create a CStringBuffer to contain 'length'
|
|||
// characters, and resize if necessary (i.e. if the buffer contains
|
|||
// non-ascii characters). (Alternatively, scan the buffer first for
|
|||
// ascii characters, so we know this will be sufficient).
|
|||
// * We could allocate a CStringBuffer with an appropriate size to
|
|||
// have a good chance of being able to write the string into the
|
|||
// buffer without reallocing (say, 1.5 x length).
|
|||
if (length > std::numeric_limits<unsigned>::max() / 3) return std::string(); |
|||
std::vector<char> bufferVector(length * 3); |
|||
char* buffer = bufferVector.data(); |
|||
const UChar* characters = m_impl.data(); |
|||
|
|||
ConversionResult result = |
|||
convertUTF16ToUTF8(&characters, characters + length, &buffer, |
|||
buffer + bufferVector.size(), false); |
|||
DCHECK( |
|||
result != |
|||
targetExhausted); // (length * 3) should be sufficient for any conversion
|
|||
|
|||
// Only produced from strict conversion.
|
|||
DCHECK(result != sourceIllegal); |
|||
|
|||
// Check for an unconverted high surrogate.
|
|||
if (result == sourceExhausted) { |
|||
// This should be one unpaired high surrogate. Treat it the same
|
|||
// was as an unpaired high surrogate would have been handled in
|
|||
// the middle of a string with non-strict conversion - which is
|
|||
// to say, simply encode it to UTF-8.
|
|||
DCHECK((characters + 1) == (m_impl.data() + length)); |
|||
DCHECK((*characters >= 0xD800) && (*characters <= 0xDBFF)); |
|||
// There should be room left, since one UChar hasn't been
|
|||
// converted.
|
|||
DCHECK((buffer + 3) <= (buffer + bufferVector.size())); |
|||
putUTF8Triple(buffer, *characters); |
|||
} |
|||
|
|||
return std::string(bufferVector.data(), buffer - bufferVector.data()); |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,134 @@ |
|||
// 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_STRING16_H_ |
|||
#define V8_INSPECTOR_STRING16_H_ |
|||
|
|||
#include <stdint.h> |
|||
#include <cctype> |
|||
#include <climits> |
|||
#include <cstring> |
|||
#include <string> |
|||
#include <vector> |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
using UChar = uint16_t; |
|||
|
|||
class String16 { |
|||
public: |
|||
static const size_t kNotFound = static_cast<size_t>(-1); |
|||
|
|||
String16() {} |
|||
String16(const String16& other) : m_impl(other.m_impl) {} |
|||
String16(const UChar* characters, size_t size) : m_impl(characters, size) {} |
|||
String16(const UChar* characters) // NOLINT(runtime/explicit)
|
|||
: m_impl(characters) {} |
|||
String16(const char* characters) // NOLINT(runtime/explicit)
|
|||
: String16(characters, std::strlen(characters)) {} |
|||
String16(const char* characters, size_t size) { |
|||
m_impl.resize(size); |
|||
for (size_t i = 0; i < size; ++i) m_impl[i] = characters[i]; |
|||
} |
|||
|
|||
static String16 fromInteger(int); |
|||
static String16 fromInteger(size_t); |
|||
static String16 fromDouble(double); |
|||
static String16 fromDoublePrecision3(double); |
|||
static String16 fromDoublePrecision6(double); |
|||
|
|||
int toInteger(bool* ok = nullptr) const; |
|||
String16 stripWhiteSpace() const; |
|||
const UChar* characters16() const { return m_impl.c_str(); } |
|||
size_t length() const { return m_impl.length(); } |
|||
bool isEmpty() const { return !m_impl.length(); } |
|||
UChar operator[](size_t index) const { return m_impl[index]; } |
|||
String16 substring(size_t pos, size_t len = UINT_MAX) const { |
|||
return String16(m_impl.substr(pos, len)); |
|||
} |
|||
size_t find(const String16& str, size_t start = 0) const { |
|||
return m_impl.find(str.m_impl, start); |
|||
} |
|||
size_t reverseFind(const String16& str, size_t start = UINT_MAX) const { |
|||
return m_impl.rfind(str.m_impl, start); |
|||
} |
|||
void swap(String16& other) { m_impl.swap(other.m_impl); } |
|||
|
|||
// Convenience methods.
|
|||
std::string utf8() const; |
|||
static String16 fromUTF8(const char* stringStart, size_t length); |
|||
|
|||
const std::basic_string<UChar>& impl() const { return m_impl; } |
|||
explicit String16(const std::basic_string<UChar>& impl) : m_impl(impl) {} |
|||
|
|||
std::size_t hash() const { |
|||
if (!has_hash) { |
|||
size_t hash = 0; |
|||
for (size_t i = 0; i < length(); ++i) hash = 31 * hash + m_impl[i]; |
|||
hash_code = hash; |
|||
has_hash = true; |
|||
} |
|||
return hash_code; |
|||
} |
|||
|
|||
private: |
|||
std::basic_string<UChar> m_impl; |
|||
mutable bool has_hash = false; |
|||
mutable std::size_t hash_code = 0; |
|||
}; |
|||
|
|||
inline bool operator==(const String16& a, const String16& b) { |
|||
return a.impl() == b.impl(); |
|||
} |
|||
inline bool operator<(const String16& a, const String16& b) { |
|||
return a.impl() < b.impl(); |
|||
} |
|||
inline bool operator!=(const String16& a, const String16& b) { |
|||
return a.impl() != b.impl(); |
|||
} |
|||
inline bool operator==(const String16& a, const char* b) { |
|||
return a.impl() == String16(b).impl(); |
|||
} |
|||
inline String16 operator+(const String16& a, const char* b) { |
|||
return String16(a.impl() + String16(b).impl()); |
|||
} |
|||
inline String16 operator+(const char* a, const String16& b) { |
|||
return String16(String16(a).impl() + b.impl()); |
|||
} |
|||
inline String16 operator+(const String16& a, const String16& b) { |
|||
return String16(a.impl() + b.impl()); |
|||
} |
|||
|
|||
class String16Builder { |
|||
public: |
|||
String16Builder(); |
|||
void append(const String16&); |
|||
void append(UChar); |
|||
void append(char); |
|||
void append(const UChar*, size_t); |
|||
void append(const char*, size_t); |
|||
String16 toString(); |
|||
void reserveCapacity(size_t); |
|||
|
|||
private: |
|||
std::vector<UChar> m_buffer; |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#if !defined(__APPLE__) || defined(_LIBCPP_VERSION) |
|||
|
|||
namespace std { |
|||
template <> |
|||
struct hash<v8_inspector::String16> { |
|||
std::size_t operator()(const v8_inspector::String16& string) const { |
|||
return string.hash(); |
|||
} |
|||
}; |
|||
|
|||
} // namespace std
|
|||
|
|||
#endif // !defined(__APPLE__) || defined(_LIBCPP_VERSION)
|
|||
|
|||
#endif // V8_INSPECTOR_STRING16_H_
|
@ -0,0 +1,218 @@ |
|||
// 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/string-util.h" |
|||
|
|||
#include "src/inspector/protocol/Protocol.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
v8::Local<v8::String> toV8String(v8::Isolate* isolate, const String16& string) { |
|||
if (string.isEmpty()) return v8::String::Empty(isolate); |
|||
DCHECK(string.length() < v8::String::kMaxLength); |
|||
return v8::String::NewFromTwoByte( |
|||
isolate, reinterpret_cast<const uint16_t*>(string.characters16()), |
|||
v8::NewStringType::kNormal, static_cast<int>(string.length())) |
|||
.ToLocalChecked(); |
|||
} |
|||
|
|||
v8::Local<v8::String> toV8StringInternalized(v8::Isolate* isolate, |
|||
const String16& string) { |
|||
if (string.isEmpty()) return v8::String::Empty(isolate); |
|||
DCHECK(string.length() < v8::String::kMaxLength); |
|||
return v8::String::NewFromTwoByte( |
|||
isolate, reinterpret_cast<const uint16_t*>(string.characters16()), |
|||
v8::NewStringType::kInternalized, |
|||
static_cast<int>(string.length())) |
|||
.ToLocalChecked(); |
|||
} |
|||
|
|||
v8::Local<v8::String> toV8StringInternalized(v8::Isolate* isolate, |
|||
const char* str) { |
|||
return v8::String::NewFromUtf8(isolate, str, v8::NewStringType::kInternalized) |
|||
.ToLocalChecked(); |
|||
} |
|||
|
|||
v8::Local<v8::String> toV8String(v8::Isolate* isolate, |
|||
const StringView& string) { |
|||
if (!string.length()) return v8::String::Empty(isolate); |
|||
DCHECK(string.length() < v8::String::kMaxLength); |
|||
if (string.is8Bit()) |
|||
return v8::String::NewFromOneByte( |
|||
isolate, reinterpret_cast<const uint8_t*>(string.characters8()), |
|||
v8::NewStringType::kNormal, static_cast<int>(string.length())) |
|||
.ToLocalChecked(); |
|||
return v8::String::NewFromTwoByte( |
|||
isolate, reinterpret_cast<const uint16_t*>(string.characters16()), |
|||
v8::NewStringType::kNormal, static_cast<int>(string.length())) |
|||
.ToLocalChecked(); |
|||
} |
|||
|
|||
String16 toProtocolString(v8::Local<v8::String> value) { |
|||
if (value.IsEmpty() || value->IsNull() || value->IsUndefined()) |
|||
return String16(); |
|||
std::unique_ptr<UChar[]> buffer(new UChar[value->Length()]); |
|||
value->Write(reinterpret_cast<uint16_t*>(buffer.get()), 0, value->Length()); |
|||
return String16(buffer.get(), value->Length()); |
|||
} |
|||
|
|||
String16 toProtocolStringWithTypeCheck(v8::Local<v8::Value> value) { |
|||
if (value.IsEmpty() || !value->IsString()) return String16(); |
|||
return toProtocolString(value.As<v8::String>()); |
|||
} |
|||
|
|||
String16 toString16(const StringView& string) { |
|||
if (!string.length()) return String16(); |
|||
if (string.is8Bit()) |
|||
return String16(reinterpret_cast<const char*>(string.characters8()), |
|||
string.length()); |
|||
return String16(reinterpret_cast<const UChar*>(string.characters16()), |
|||
string.length()); |
|||
} |
|||
|
|||
StringView toStringView(const String16& string) { |
|||
if (string.isEmpty()) return StringView(); |
|||
return StringView(reinterpret_cast<const uint16_t*>(string.characters16()), |
|||
string.length()); |
|||
} |
|||
|
|||
bool stringViewStartsWith(const StringView& string, const char* prefix) { |
|||
if (!string.length()) return !(*prefix); |
|||
if (string.is8Bit()) { |
|||
for (size_t i = 0, j = 0; prefix[j] && i < string.length(); ++i, ++j) { |
|||
if (string.characters8()[i] != prefix[j]) return false; |
|||
} |
|||
} else { |
|||
for (size_t i = 0, j = 0; prefix[j] && i < string.length(); ++i, ++j) { |
|||
if (string.characters16()[i] != prefix[j]) return false; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
namespace protocol { |
|||
|
|||
std::unique_ptr<protocol::Value> parseJSON(const StringView& string) { |
|||
if (!string.length()) return nullptr; |
|||
if (string.is8Bit()) { |
|||
return protocol::parseJSON(string.characters8(), |
|||
static_cast<int>(string.length())); |
|||
} |
|||
return protocol::parseJSON(string.characters16(), |
|||
static_cast<int>(string.length())); |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Value> parseJSON(const String16& string) { |
|||
if (!string.length()) return nullptr; |
|||
return protocol::parseJSON(string.characters16(), |
|||
static_cast<int>(string.length())); |
|||
} |
|||
|
|||
} // namespace protocol
|
|||
|
|||
std::unique_ptr<protocol::Value> toProtocolValue(protocol::String* errorString, |
|||
v8::Local<v8::Context> context, |
|||
v8::Local<v8::Value> value, |
|||
int maxDepth) { |
|||
if (value.IsEmpty()) { |
|||
UNREACHABLE(); |
|||
return nullptr; |
|||
} |
|||
|
|||
if (!maxDepth) { |
|||
*errorString = "Object reference chain is too long"; |
|||
return nullptr; |
|||
} |
|||
maxDepth--; |
|||
|
|||
if (value->IsNull() || value->IsUndefined()) return protocol::Value::null(); |
|||
if (value->IsBoolean()) |
|||
return protocol::FundamentalValue::create(value.As<v8::Boolean>()->Value()); |
|||
if (value->IsNumber()) { |
|||
double doubleValue = value.As<v8::Number>()->Value(); |
|||
int intValue = static_cast<int>(doubleValue); |
|||
if (intValue == doubleValue) |
|||
return protocol::FundamentalValue::create(intValue); |
|||
return protocol::FundamentalValue::create(doubleValue); |
|||
} |
|||
if (value->IsString()) |
|||
return protocol::StringValue::create( |
|||
toProtocolString(value.As<v8::String>())); |
|||
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)) { |
|||
*errorString = "Internal error"; |
|||
return nullptr; |
|||
} |
|||
std::unique_ptr<protocol::Value> element = |
|||
toProtocolValue(errorString, context, value, maxDepth); |
|||
if (!element) return nullptr; |
|||
inspectorArray->pushValue(std::move(element)); |
|||
} |
|||
return std::move(inspectorArray); |
|||
} |
|||
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)) { |
|||
*errorString = "Internal error"; |
|||
return nullptr; |
|||
} |
|||
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)) { |
|||
*errorString = "Internal error"; |
|||
return nullptr; |
|||
} |
|||
// 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.IsJust() || !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)) { |
|||
*errorString = "Internal error"; |
|||
return nullptr; |
|||
} |
|||
std::unique_ptr<protocol::Value> propertyValue = |
|||
toProtocolValue(errorString, context, property, maxDepth); |
|||
if (!propertyValue) return nullptr; |
|||
jsonObject->setValue(toProtocolString(propertyName), |
|||
std::move(propertyValue)); |
|||
} |
|||
return std::move(jsonObject); |
|||
} |
|||
*errorString = "Object couldn't be returned by value"; |
|||
return nullptr; |
|||
} |
|||
|
|||
// static
|
|||
std::unique_ptr<StringBuffer> StringBuffer::create(const StringView& string) { |
|||
String16 owner = toString16(string); |
|||
return StringBufferImpl::adopt(owner); |
|||
} |
|||
|
|||
// static
|
|||
std::unique_ptr<StringBufferImpl> StringBufferImpl::adopt(String16& string) { |
|||
return wrapUnique(new StringBufferImpl(string)); |
|||
} |
|||
|
|||
StringBufferImpl::StringBufferImpl(String16& string) { |
|||
m_owner.swap(string); |
|||
m_string = toStringView(m_owner); |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,75 @@ |
|||
// 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_STRINGUTIL_H_ |
|||
#define V8_INSPECTOR_STRINGUTIL_H_ |
|||
|
|||
#include "src/base/macros.h" |
|||
#include "src/inspector/string-16.h" |
|||
|
|||
#include "include/v8-inspector.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
namespace protocol { |
|||
|
|||
class Value; |
|||
|
|||
using String = v8_inspector::String16; |
|||
using StringBuilder = v8_inspector::String16Builder; |
|||
|
|||
class StringUtil { |
|||
public: |
|||
static String substring(const String& s, size_t pos, size_t len) { |
|||
return s.substring(pos, len); |
|||
} |
|||
static String fromInteger(int number) { return String::fromInteger(number); } |
|||
static String fromInteger(size_t number) { |
|||
return String::fromInteger(number); |
|||
} |
|||
static String fromDouble(double number) { return String::fromDouble(number); } |
|||
static const size_t kNotFound = String::kNotFound; |
|||
static void builderReserve(StringBuilder& builder, size_t capacity) { |
|||
builder.reserveCapacity(capacity); |
|||
} |
|||
}; |
|||
|
|||
std::unique_ptr<protocol::Value> parseJSON(const StringView& json); |
|||
std::unique_ptr<protocol::Value> parseJSON(const String16& json); |
|||
|
|||
} // namespace protocol
|
|||
|
|||
std::unique_ptr<protocol::Value> toProtocolValue(protocol::String* errorString, |
|||
v8::Local<v8::Context>, |
|||
v8::Local<v8::Value>, |
|||
int maxDepth = 1000); |
|||
|
|||
v8::Local<v8::String> toV8String(v8::Isolate*, const String16&); |
|||
v8::Local<v8::String> toV8StringInternalized(v8::Isolate*, const String16&); |
|||
v8::Local<v8::String> toV8StringInternalized(v8::Isolate*, const char*); |
|||
v8::Local<v8::String> toV8String(v8::Isolate*, const StringView&); |
|||
// TODO(dgozman): rename to toString16.
|
|||
String16 toProtocolString(v8::Local<v8::String>); |
|||
String16 toProtocolStringWithTypeCheck(v8::Local<v8::Value>); |
|||
String16 toString16(const StringView&); |
|||
StringView toStringView(const String16&); |
|||
bool stringViewStartsWith(const StringView&, const char*); |
|||
|
|||
class StringBufferImpl : public StringBuffer { |
|||
public: |
|||
// Destroys string's content.
|
|||
static std::unique_ptr<StringBufferImpl> adopt(String16&); |
|||
const StringView& string() override { return m_string; } |
|||
|
|||
private: |
|||
explicit StringBufferImpl(String16&); |
|||
String16 m_owner; |
|||
StringView m_string; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(StringBufferImpl); |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_STRINGUTIL_H_
|
@ -0,0 +1,79 @@ |
|||
// 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-console-agent-impl.h" |
|||
|
|||
#include "src/inspector/protocol/Protocol.h" |
|||
#include "src/inspector/v8-console-message.h" |
|||
#include "src/inspector/v8-inspector-impl.h" |
|||
#include "src/inspector/v8-inspector-session-impl.h" |
|||
#include "src/inspector/v8-stack-trace-impl.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
namespace ConsoleAgentState { |
|||
static const char consoleEnabled[] = "consoleEnabled"; |
|||
} |
|||
|
|||
V8ConsoleAgentImpl::V8ConsoleAgentImpl( |
|||
V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel, |
|||
protocol::DictionaryValue* state) |
|||
: m_session(session), |
|||
m_state(state), |
|||
m_frontend(frontendChannel), |
|||
m_enabled(false) {} |
|||
|
|||
V8ConsoleAgentImpl::~V8ConsoleAgentImpl() {} |
|||
|
|||
void V8ConsoleAgentImpl::enable(ErrorString* errorString) { |
|||
if (m_enabled) return; |
|||
m_state->setBoolean(ConsoleAgentState::consoleEnabled, true); |
|||
m_enabled = true; |
|||
m_session->inspector()->enableStackCapturingIfNeeded(); |
|||
reportAllMessages(); |
|||
} |
|||
|
|||
void V8ConsoleAgentImpl::disable(ErrorString* errorString) { |
|||
if (!m_enabled) return; |
|||
m_session->inspector()->disableStackCapturingIfNeeded(); |
|||
m_state->setBoolean(ConsoleAgentState::consoleEnabled, false); |
|||
m_enabled = false; |
|||
} |
|||
|
|||
void V8ConsoleAgentImpl::clearMessages(ErrorString* errorString) {} |
|||
|
|||
void V8ConsoleAgentImpl::restore() { |
|||
if (!m_state->booleanProperty(ConsoleAgentState::consoleEnabled, false)) |
|||
return; |
|||
ErrorString ignored; |
|||
enable(&ignored); |
|||
} |
|||
|
|||
void V8ConsoleAgentImpl::messageAdded(V8ConsoleMessage* message) { |
|||
if (m_enabled) reportMessage(message, true); |
|||
} |
|||
|
|||
bool V8ConsoleAgentImpl::enabled() { return m_enabled; } |
|||
|
|||
void V8ConsoleAgentImpl::reportAllMessages() { |
|||
V8ConsoleMessageStorage* storage = |
|||
m_session->inspector()->ensureConsoleMessageStorage( |
|||
m_session->contextGroupId()); |
|||
for (const auto& message : storage->messages()) { |
|||
if (message->origin() == V8MessageOrigin::kConsole) { |
|||
if (!reportMessage(message.get(), false)) return; |
|||
} |
|||
} |
|||
} |
|||
|
|||
bool V8ConsoleAgentImpl::reportMessage(V8ConsoleMessage* message, |
|||
bool generatePreview) { |
|||
DCHECK(message->origin() == V8MessageOrigin::kConsole); |
|||
message->reportToFrontend(&m_frontend); |
|||
m_frontend.flush(); |
|||
return m_session->inspector()->hasConsoleMessageStorage( |
|||
m_session->contextGroupId()); |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,48 @@ |
|||
// 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_V8CONSOLEAGENTIMPL_H_ |
|||
#define V8_INSPECTOR_V8CONSOLEAGENTIMPL_H_ |
|||
|
|||
#include "src/base/macros.h" |
|||
#include "src/inspector/protocol/Console.h" |
|||
#include "src/inspector/protocol/Forward.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
class V8ConsoleMessage; |
|||
class V8InspectorSessionImpl; |
|||
|
|||
using protocol::ErrorString; |
|||
|
|||
class V8ConsoleAgentImpl : public protocol::Console::Backend { |
|||
public: |
|||
V8ConsoleAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*, |
|||
protocol::DictionaryValue* state); |
|||
~V8ConsoleAgentImpl() override; |
|||
|
|||
void enable(ErrorString*) override; |
|||
void disable(ErrorString*) override; |
|||
void clearMessages(ErrorString*) override; |
|||
|
|||
void restore(); |
|||
void messageAdded(V8ConsoleMessage*); |
|||
void reset(); |
|||
bool enabled(); |
|||
|
|||
private: |
|||
void reportAllMessages(); |
|||
bool reportMessage(V8ConsoleMessage*, bool generatePreview); |
|||
|
|||
V8InspectorSessionImpl* m_session; |
|||
protocol::DictionaryValue* m_state; |
|||
protocol::Console::Frontend m_frontend; |
|||
bool m_enabled; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(V8ConsoleAgentImpl); |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_V8CONSOLEAGENTIMPL_H_
|
@ -0,0 +1,473 @@ |
|||
// 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-console-message.h" |
|||
|
|||
#include "src/inspector/inspected-context.h" |
|||
#include "src/inspector/protocol/Protocol.h" |
|||
#include "src/inspector/string-util.h" |
|||
#include "src/inspector/v8-console-agent-impl.h" |
|||
#include "src/inspector/v8-inspector-impl.h" |
|||
#include "src/inspector/v8-inspector-session-impl.h" |
|||
#include "src/inspector/v8-runtime-agent-impl.h" |
|||
#include "src/inspector/v8-stack-trace-impl.h" |
|||
|
|||
#include "include/v8-inspector.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
namespace { |
|||
|
|||
String16 consoleAPITypeValue(ConsoleAPIType type) { |
|||
switch (type) { |
|||
case ConsoleAPIType::kLog: |
|||
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Log; |
|||
case ConsoleAPIType::kDebug: |
|||
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Debug; |
|||
case ConsoleAPIType::kInfo: |
|||
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Info; |
|||
case ConsoleAPIType::kError: |
|||
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Error; |
|||
case ConsoleAPIType::kWarning: |
|||
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Warning; |
|||
case ConsoleAPIType::kClear: |
|||
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Clear; |
|||
case ConsoleAPIType::kDir: |
|||
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Dir; |
|||
case ConsoleAPIType::kDirXML: |
|||
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Dirxml; |
|||
case ConsoleAPIType::kTable: |
|||
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Table; |
|||
case ConsoleAPIType::kTrace: |
|||
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Trace; |
|||
case ConsoleAPIType::kStartGroup: |
|||
return protocol::Runtime::ConsoleAPICalled::TypeEnum::StartGroup; |
|||
case ConsoleAPIType::kStartGroupCollapsed: |
|||
return protocol::Runtime::ConsoleAPICalled::TypeEnum::StartGroupCollapsed; |
|||
case ConsoleAPIType::kEndGroup: |
|||
return protocol::Runtime::ConsoleAPICalled::TypeEnum::EndGroup; |
|||
case ConsoleAPIType::kAssert: |
|||
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Assert; |
|||
case ConsoleAPIType::kTimeEnd: |
|||
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Debug; |
|||
case ConsoleAPIType::kCount: |
|||
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Debug; |
|||
} |
|||
return protocol::Runtime::ConsoleAPICalled::TypeEnum::Log; |
|||
} |
|||
|
|||
const unsigned maxConsoleMessageCount = 1000; |
|||
const unsigned maxArrayItemsLimit = 10000; |
|||
const unsigned maxStackDepthLimit = 32; |
|||
|
|||
class V8ValueStringBuilder { |
|||
public: |
|||
static String16 toString(v8::Local<v8::Value> value, |
|||
v8::Local<v8::Context> context) { |
|||
V8ValueStringBuilder builder(context); |
|||
if (!builder.append(value)) return String16(); |
|||
return builder.toString(); |
|||
} |
|||
|
|||
private: |
|||
enum { |
|||
IgnoreNull = 1 << 0, |
|||
IgnoreUndefined = 1 << 1, |
|||
}; |
|||
|
|||
explicit V8ValueStringBuilder(v8::Local<v8::Context> context) |
|||
: m_arrayLimit(maxArrayItemsLimit), |
|||
m_isolate(context->GetIsolate()), |
|||
m_tryCatch(context->GetIsolate()), |
|||
m_context(context) {} |
|||
|
|||
bool append(v8::Local<v8::Value> value, unsigned ignoreOptions = 0) { |
|||
if (value.IsEmpty()) return true; |
|||
if ((ignoreOptions & IgnoreNull) && value->IsNull()) return true; |
|||
if ((ignoreOptions & IgnoreUndefined) && value->IsUndefined()) return true; |
|||
if (value->IsString()) return append(v8::Local<v8::String>::Cast(value)); |
|||
if (value->IsStringObject()) |
|||
return append(v8::Local<v8::StringObject>::Cast(value)->ValueOf()); |
|||
if (value->IsSymbol()) return append(v8::Local<v8::Symbol>::Cast(value)); |
|||
if (value->IsSymbolObject()) |
|||
return append(v8::Local<v8::SymbolObject>::Cast(value)->ValueOf()); |
|||
if (value->IsNumberObject()) { |
|||
m_builder.append(String16::fromDoublePrecision6( |
|||
v8::Local<v8::NumberObject>::Cast(value)->ValueOf())); |
|||
return true; |
|||
} |
|||
if (value->IsBooleanObject()) { |
|||
m_builder.append(v8::Local<v8::BooleanObject>::Cast(value)->ValueOf() |
|||
? "true" |
|||
: "false"); |
|||
return true; |
|||
} |
|||
if (value->IsArray()) return append(v8::Local<v8::Array>::Cast(value)); |
|||
if (value->IsProxy()) { |
|||
m_builder.append("[object Proxy]"); |
|||
return true; |
|||
} |
|||
if (value->IsObject() && !value->IsDate() && !value->IsFunction() && |
|||
!value->IsNativeError() && !value->IsRegExp()) { |
|||
v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value); |
|||
v8::Local<v8::String> stringValue; |
|||
if (object->ObjectProtoToString(m_isolate->GetCurrentContext()) |
|||
.ToLocal(&stringValue)) |
|||
return append(stringValue); |
|||
} |
|||
v8::Local<v8::String> stringValue; |
|||
if (!value->ToString(m_isolate->GetCurrentContext()).ToLocal(&stringValue)) |
|||
return false; |
|||
return append(stringValue); |
|||
} |
|||
|
|||
bool append(v8::Local<v8::Array> array) { |
|||
for (const auto& it : m_visitedArrays) { |
|||
if (it == array) return true; |
|||
} |
|||
uint32_t length = array->Length(); |
|||
if (length > m_arrayLimit) return false; |
|||
if (m_visitedArrays.size() > maxStackDepthLimit) return false; |
|||
|
|||
bool result = true; |
|||
m_arrayLimit -= length; |
|||
m_visitedArrays.push_back(array); |
|||
for (uint32_t i = 0; i < length; ++i) { |
|||
if (i) m_builder.append(','); |
|||
v8::Local<v8::Value> value; |
|||
if (!array->Get(m_context, i).ToLocal(&value)) continue; |
|||
if (!append(value, IgnoreNull | IgnoreUndefined)) { |
|||
result = false; |
|||
break; |
|||
} |
|||
} |
|||
m_visitedArrays.pop_back(); |
|||
return result; |
|||
} |
|||
|
|||
bool append(v8::Local<v8::Symbol> symbol) { |
|||
m_builder.append("Symbol("); |
|||
bool result = append(symbol->Name(), IgnoreUndefined); |
|||
m_builder.append(')'); |
|||
return result; |
|||
} |
|||
|
|||
bool append(v8::Local<v8::String> string) { |
|||
if (m_tryCatch.HasCaught()) return false; |
|||
if (!string.IsEmpty()) m_builder.append(toProtocolString(string)); |
|||
return true; |
|||
} |
|||
|
|||
String16 toString() { |
|||
if (m_tryCatch.HasCaught()) return String16(); |
|||
return m_builder.toString(); |
|||
} |
|||
|
|||
uint32_t m_arrayLimit; |
|||
v8::Isolate* m_isolate; |
|||
String16Builder m_builder; |
|||
std::vector<v8::Local<v8::Array>> m_visitedArrays; |
|||
v8::TryCatch m_tryCatch; |
|||
v8::Local<v8::Context> m_context; |
|||
}; |
|||
|
|||
} // namespace
|
|||
|
|||
V8ConsoleMessage::V8ConsoleMessage(V8MessageOrigin origin, double timestamp, |
|||
const String16& message) |
|||
: m_origin(origin), |
|||
m_timestamp(timestamp), |
|||
m_message(message), |
|||
m_lineNumber(0), |
|||
m_columnNumber(0), |
|||
m_scriptId(0), |
|||
m_contextId(0), |
|||
m_type(ConsoleAPIType::kLog), |
|||
m_exceptionId(0), |
|||
m_revokedExceptionId(0) {} |
|||
|
|||
V8ConsoleMessage::~V8ConsoleMessage() {} |
|||
|
|||
void V8ConsoleMessage::setLocation(const String16& url, unsigned lineNumber, |
|||
unsigned columnNumber, |
|||
std::unique_ptr<V8StackTraceImpl> stackTrace, |
|||
int scriptId) { |
|||
m_url = url; |
|||
m_lineNumber = lineNumber; |
|||
m_columnNumber = columnNumber; |
|||
m_stackTrace = std::move(stackTrace); |
|||
m_scriptId = scriptId; |
|||
} |
|||
|
|||
void V8ConsoleMessage::reportToFrontend( |
|||
protocol::Console::Frontend* frontend) const { |
|||
DCHECK(m_origin == V8MessageOrigin::kConsole); |
|||
String16 level = protocol::Console::ConsoleMessage::LevelEnum::Log; |
|||
if (m_type == ConsoleAPIType::kDebug || m_type == ConsoleAPIType::kCount || |
|||
m_type == ConsoleAPIType::kTimeEnd) |
|||
level = protocol::Console::ConsoleMessage::LevelEnum::Debug; |
|||
else if (m_type == ConsoleAPIType::kError || |
|||
m_type == ConsoleAPIType::kAssert) |
|||
level = protocol::Console::ConsoleMessage::LevelEnum::Error; |
|||
else if (m_type == ConsoleAPIType::kWarning) |
|||
level = protocol::Console::ConsoleMessage::LevelEnum::Warning; |
|||
else if (m_type == ConsoleAPIType::kInfo) |
|||
level = protocol::Console::ConsoleMessage::LevelEnum::Info; |
|||
std::unique_ptr<protocol::Console::ConsoleMessage> result = |
|||
protocol::Console::ConsoleMessage::create() |
|||
.setSource(protocol::Console::ConsoleMessage::SourceEnum::ConsoleApi) |
|||
.setLevel(level) |
|||
.setText(m_message) |
|||
.build(); |
|||
result->setLine(static_cast<int>(m_lineNumber)); |
|||
result->setColumn(static_cast<int>(m_columnNumber)); |
|||
result->setUrl(m_url); |
|||
frontend->messageAdded(std::move(result)); |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> |
|||
V8ConsoleMessage::wrapArguments(V8InspectorSessionImpl* session, |
|||
bool generatePreview) const { |
|||
if (!m_arguments.size() || !m_contextId) return nullptr; |
|||
InspectedContext* inspectedContext = |
|||
session->inspector()->getContext(session->contextGroupId(), m_contextId); |
|||
if (!inspectedContext) return nullptr; |
|||
|
|||
v8::Isolate* isolate = inspectedContext->isolate(); |
|||
v8::HandleScope handles(isolate); |
|||
v8::Local<v8::Context> context = inspectedContext->context(); |
|||
|
|||
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>(); |
|||
std::unique_ptr<protocol::Runtime::RemoteObject> wrapped = |
|||
session->wrapTable(context, table, columns); |
|||
if (wrapped) |
|||
args->addItem(std::move(wrapped)); |
|||
else |
|||
args = nullptr; |
|||
} else { |
|||
for (size_t i = 0; i < m_arguments.size(); ++i) { |
|||
std::unique_ptr<protocol::Runtime::RemoteObject> wrapped = |
|||
session->wrapObject(context, m_arguments[i]->Get(isolate), "console", |
|||
generatePreview); |
|||
if (!wrapped) { |
|||
args = nullptr; |
|||
break; |
|||
} |
|||
args->addItem(std::move(wrapped)); |
|||
} |
|||
} |
|||
return args; |
|||
} |
|||
|
|||
void V8ConsoleMessage::reportToFrontend(protocol::Runtime::Frontend* frontend, |
|||
V8InspectorSessionImpl* session, |
|||
bool generatePreview) const { |
|||
if (m_origin == V8MessageOrigin::kException) { |
|||
std::unique_ptr<protocol::Runtime::RemoteObject> exception = |
|||
wrapException(session, generatePreview); |
|||
std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails = |
|||
protocol::Runtime::ExceptionDetails::create() |
|||
.setExceptionId(m_exceptionId) |
|||
.setText(exception ? m_message : m_detailedMessage) |
|||
.setLineNumber(m_lineNumber ? m_lineNumber - 1 : 0) |
|||
.setColumnNumber(m_columnNumber ? m_columnNumber - 1 : 0) |
|||
.build(); |
|||
if (m_scriptId) |
|||
exceptionDetails->setScriptId(String16::fromInteger(m_scriptId)); |
|||
if (!m_url.isEmpty()) exceptionDetails->setUrl(m_url); |
|||
if (m_stackTrace) |
|||
exceptionDetails->setStackTrace(m_stackTrace->buildInspectorObjectImpl()); |
|||
if (m_contextId) exceptionDetails->setExecutionContextId(m_contextId); |
|||
if (exception) exceptionDetails->setException(std::move(exception)); |
|||
frontend->exceptionThrown(m_timestamp, std::move(exceptionDetails)); |
|||
return; |
|||
} |
|||
if (m_origin == V8MessageOrigin::kRevokedException) { |
|||
frontend->exceptionRevoked(m_message, m_revokedExceptionId); |
|||
return; |
|||
} |
|||
if (m_origin == V8MessageOrigin::kConsole) { |
|||
std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> |
|||
arguments = wrapArguments(session, generatePreview); |
|||
if (!arguments) { |
|||
arguments = protocol::Array<protocol::Runtime::RemoteObject>::create(); |
|||
if (!m_message.isEmpty()) { |
|||
std::unique_ptr<protocol::Runtime::RemoteObject> messageArg = |
|||
protocol::Runtime::RemoteObject::create() |
|||
.setType(protocol::Runtime::RemoteObject::TypeEnum::String) |
|||
.build(); |
|||
messageArg->setValue(protocol::StringValue::create(m_message)); |
|||
arguments->addItem(std::move(messageArg)); |
|||
} |
|||
} |
|||
frontend->consoleAPICalled( |
|||
consoleAPITypeValue(m_type), std::move(arguments), m_contextId, |
|||
m_timestamp, |
|||
m_stackTrace ? m_stackTrace->buildInspectorObjectImpl() : nullptr); |
|||
return; |
|||
} |
|||
UNREACHABLE(); |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Runtime::RemoteObject> |
|||
V8ConsoleMessage::wrapException(V8InspectorSessionImpl* session, |
|||
bool generatePreview) const { |
|||
if (!m_arguments.size() || !m_contextId) return nullptr; |
|||
DCHECK_EQ(1u, m_arguments.size()); |
|||
InspectedContext* inspectedContext = |
|||
session->inspector()->getContext(session->contextGroupId(), m_contextId); |
|||
if (!inspectedContext) return nullptr; |
|||
|
|||
v8::Isolate* isolate = inspectedContext->isolate(); |
|||
v8::HandleScope handles(isolate); |
|||
// TODO(dgozman): should we use different object group?
|
|||
return session->wrapObject(inspectedContext->context(), |
|||
m_arguments[0]->Get(isolate), "console", |
|||
generatePreview); |
|||
} |
|||
|
|||
V8MessageOrigin V8ConsoleMessage::origin() const { return m_origin; } |
|||
|
|||
ConsoleAPIType V8ConsoleMessage::type() const { return m_type; } |
|||
|
|||
// static
|
|||
std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForConsoleAPI( |
|||
double timestamp, ConsoleAPIType type, |
|||
const std::vector<v8::Local<v8::Value>>& arguments, |
|||
std::unique_ptr<V8StackTraceImpl> stackTrace, |
|||
InspectedContext* inspectedContext) { |
|||
v8::Isolate* isolate = inspectedContext->isolate(); |
|||
int contextId = inspectedContext->contextId(); |
|||
int contextGroupId = inspectedContext->contextGroupId(); |
|||
V8InspectorImpl* inspector = inspectedContext->inspector(); |
|||
v8::Local<v8::Context> context = inspectedContext->context(); |
|||
|
|||
std::unique_ptr<V8ConsoleMessage> message = wrapUnique( |
|||
new V8ConsoleMessage(V8MessageOrigin::kConsole, timestamp, String16())); |
|||
if (stackTrace && !stackTrace->isEmpty()) { |
|||
message->m_url = toString16(stackTrace->topSourceURL()); |
|||
message->m_lineNumber = stackTrace->topLineNumber(); |
|||
message->m_columnNumber = stackTrace->topColumnNumber(); |
|||
} |
|||
message->m_stackTrace = std::move(stackTrace); |
|||
message->m_type = type; |
|||
message->m_contextId = contextId; |
|||
for (size_t i = 0; i < arguments.size(); ++i) |
|||
message->m_arguments.push_back( |
|||
wrapUnique(new v8::Global<v8::Value>(isolate, arguments.at(i)))); |
|||
if (arguments.size()) |
|||
message->m_message = V8ValueStringBuilder::toString(arguments[0], context); |
|||
|
|||
V8ConsoleAPIType clientType = V8ConsoleAPIType::kLog; |
|||
if (type == ConsoleAPIType::kDebug || type == ConsoleAPIType::kCount || |
|||
type == ConsoleAPIType::kTimeEnd) |
|||
clientType = V8ConsoleAPIType::kDebug; |
|||
else if (type == ConsoleAPIType::kError || type == ConsoleAPIType::kAssert) |
|||
clientType = V8ConsoleAPIType::kError; |
|||
else if (type == ConsoleAPIType::kWarning) |
|||
clientType = V8ConsoleAPIType::kWarning; |
|||
else if (type == ConsoleAPIType::kInfo) |
|||
clientType = V8ConsoleAPIType::kInfo; |
|||
else if (type == ConsoleAPIType::kClear) |
|||
clientType = V8ConsoleAPIType::kClear; |
|||
inspector->client()->consoleAPIMessage( |
|||
contextGroupId, clientType, toStringView(message->m_message), |
|||
toStringView(message->m_url), message->m_lineNumber, |
|||
message->m_columnNumber, message->m_stackTrace.get()); |
|||
|
|||
return message; |
|||
} |
|||
|
|||
// static
|
|||
std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForException( |
|||
double timestamp, const String16& detailedMessage, const String16& url, |
|||
unsigned lineNumber, unsigned columnNumber, |
|||
std::unique_ptr<V8StackTraceImpl> stackTrace, int scriptId, |
|||
v8::Isolate* isolate, const String16& message, int contextId, |
|||
v8::Local<v8::Value> exception, unsigned exceptionId) { |
|||
std::unique_ptr<V8ConsoleMessage> consoleMessage = wrapUnique( |
|||
new V8ConsoleMessage(V8MessageOrigin::kException, timestamp, message)); |
|||
consoleMessage->setLocation(url, lineNumber, columnNumber, |
|||
std::move(stackTrace), scriptId); |
|||
consoleMessage->m_exceptionId = exceptionId; |
|||
consoleMessage->m_detailedMessage = detailedMessage; |
|||
if (contextId && !exception.IsEmpty()) { |
|||
consoleMessage->m_contextId = contextId; |
|||
consoleMessage->m_arguments.push_back( |
|||
wrapUnique(new v8::Global<v8::Value>(isolate, exception))); |
|||
} |
|||
return consoleMessage; |
|||
} |
|||
|
|||
// static
|
|||
std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForRevokedException( |
|||
double timestamp, const String16& messageText, |
|||
unsigned revokedExceptionId) { |
|||
std::unique_ptr<V8ConsoleMessage> message = wrapUnique(new V8ConsoleMessage( |
|||
V8MessageOrigin::kRevokedException, timestamp, messageText)); |
|||
message->m_revokedExceptionId = revokedExceptionId; |
|||
return message; |
|||
} |
|||
|
|||
void V8ConsoleMessage::contextDestroyed(int contextId) { |
|||
if (contextId != m_contextId) return; |
|||
m_contextId = 0; |
|||
if (m_message.isEmpty()) m_message = "<message collected>"; |
|||
Arguments empty; |
|||
m_arguments.swap(empty); |
|||
} |
|||
|
|||
// ------------------------ V8ConsoleMessageStorage ----------------------------
|
|||
|
|||
V8ConsoleMessageStorage::V8ConsoleMessageStorage(V8InspectorImpl* inspector, |
|||
int contextGroupId) |
|||
: m_inspector(inspector), |
|||
m_contextGroupId(contextGroupId), |
|||
m_expiredCount(0) {} |
|||
|
|||
V8ConsoleMessageStorage::~V8ConsoleMessageStorage() { clear(); } |
|||
|
|||
void V8ConsoleMessageStorage::addMessage( |
|||
std::unique_ptr<V8ConsoleMessage> message) { |
|||
int contextGroupId = m_contextGroupId; |
|||
V8InspectorImpl* inspector = m_inspector; |
|||
if (message->type() == ConsoleAPIType::kClear) clear(); |
|||
|
|||
V8InspectorSessionImpl* session = |
|||
inspector->sessionForContextGroup(contextGroupId); |
|||
if (session) { |
|||
if (message->origin() == V8MessageOrigin::kConsole) |
|||
session->consoleAgent()->messageAdded(message.get()); |
|||
session->runtimeAgent()->messageAdded(message.get()); |
|||
} |
|||
if (!inspector->hasConsoleMessageStorage(contextGroupId)) return; |
|||
|
|||
DCHECK(m_messages.size() <= maxConsoleMessageCount); |
|||
if (m_messages.size() == maxConsoleMessageCount) { |
|||
++m_expiredCount; |
|||
m_messages.pop_front(); |
|||
} |
|||
m_messages.push_back(std::move(message)); |
|||
} |
|||
|
|||
void V8ConsoleMessageStorage::clear() { |
|||
m_messages.clear(); |
|||
m_expiredCount = 0; |
|||
if (V8InspectorSessionImpl* session = |
|||
m_inspector->sessionForContextGroup(m_contextGroupId)) |
|||
session->releaseObjectGroup("console"); |
|||
} |
|||
|
|||
void V8ConsoleMessageStorage::contextDestroyed(int contextId) { |
|||
for (size_t i = 0; i < m_messages.size(); ++i) |
|||
m_messages[i]->contextDestroyed(contextId); |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,120 @@ |
|||
// 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_V8CONSOLEMESSAGE_H_ |
|||
#define V8_INSPECTOR_V8CONSOLEMESSAGE_H_ |
|||
|
|||
#include <deque> |
|||
#include "include/v8.h" |
|||
#include "src/inspector/protocol/Console.h" |
|||
#include "src/inspector/protocol/Forward.h" |
|||
#include "src/inspector/protocol/Runtime.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
class InspectedContext; |
|||
class V8InspectorImpl; |
|||
class V8InspectorSessionImpl; |
|||
class V8StackTraceImpl; |
|||
|
|||
enum class V8MessageOrigin { kConsole, kException, kRevokedException }; |
|||
|
|||
enum class ConsoleAPIType { |
|||
kLog, |
|||
kDebug, |
|||
kInfo, |
|||
kError, |
|||
kWarning, |
|||
kDir, |
|||
kDirXML, |
|||
kTable, |
|||
kTrace, |
|||
kStartGroup, |
|||
kStartGroupCollapsed, |
|||
kEndGroup, |
|||
kClear, |
|||
kAssert, |
|||
kTimeEnd, |
|||
kCount |
|||
}; |
|||
|
|||
class V8ConsoleMessage { |
|||
public: |
|||
~V8ConsoleMessage(); |
|||
|
|||
static std::unique_ptr<V8ConsoleMessage> createForConsoleAPI( |
|||
double timestamp, ConsoleAPIType, |
|||
const std::vector<v8::Local<v8::Value>>& arguments, |
|||
std::unique_ptr<V8StackTraceImpl>, InspectedContext*); |
|||
|
|||
static std::unique_ptr<V8ConsoleMessage> createForException( |
|||
double timestamp, const String16& detailedMessage, const String16& url, |
|||
unsigned lineNumber, unsigned columnNumber, |
|||
std::unique_ptr<V8StackTraceImpl>, int scriptId, v8::Isolate*, |
|||
const String16& message, int contextId, v8::Local<v8::Value> exception, |
|||
unsigned exceptionId); |
|||
|
|||
static std::unique_ptr<V8ConsoleMessage> createForRevokedException( |
|||
double timestamp, const String16& message, unsigned revokedExceptionId); |
|||
|
|||
V8MessageOrigin origin() const; |
|||
void reportToFrontend(protocol::Console::Frontend*) const; |
|||
void reportToFrontend(protocol::Runtime::Frontend*, V8InspectorSessionImpl*, |
|||
bool generatePreview) const; |
|||
ConsoleAPIType type() const; |
|||
void contextDestroyed(int contextId); |
|||
|
|||
private: |
|||
V8ConsoleMessage(V8MessageOrigin, double timestamp, const String16& message); |
|||
|
|||
using Arguments = std::vector<std::unique_ptr<v8::Global<v8::Value>>>; |
|||
std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> |
|||
wrapArguments(V8InspectorSessionImpl*, bool generatePreview) const; |
|||
std::unique_ptr<protocol::Runtime::RemoteObject> wrapException( |
|||
V8InspectorSessionImpl*, bool generatePreview) const; |
|||
void setLocation(const String16& url, unsigned lineNumber, |
|||
unsigned columnNumber, std::unique_ptr<V8StackTraceImpl>, |
|||
int scriptId); |
|||
|
|||
V8MessageOrigin m_origin; |
|||
double m_timestamp; |
|||
String16 m_message; |
|||
String16 m_url; |
|||
unsigned m_lineNumber; |
|||
unsigned m_columnNumber; |
|||
std::unique_ptr<V8StackTraceImpl> m_stackTrace; |
|||
int m_scriptId; |
|||
int m_contextId; |
|||
ConsoleAPIType m_type; |
|||
unsigned m_exceptionId; |
|||
unsigned m_revokedExceptionId; |
|||
Arguments m_arguments; |
|||
String16 m_detailedMessage; |
|||
}; |
|||
|
|||
class V8ConsoleMessageStorage { |
|||
public: |
|||
V8ConsoleMessageStorage(V8InspectorImpl*, int contextGroupId); |
|||
~V8ConsoleMessageStorage(); |
|||
|
|||
int contextGroupId() { return m_contextGroupId; } |
|||
int expiredCount() { return m_expiredCount; } |
|||
const std::deque<std::unique_ptr<V8ConsoleMessage>>& messages() const { |
|||
return m_messages; |
|||
} |
|||
|
|||
void addMessage(std::unique_ptr<V8ConsoleMessage>); |
|||
void contextDestroyed(int contextId); |
|||
void clear(); |
|||
|
|||
private: |
|||
V8InspectorImpl* m_inspector; |
|||
int m_contextGroupId; |
|||
int m_expiredCount; |
|||
std::deque<std::unique_ptr<V8ConsoleMessage>> m_messages; |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_V8CONSOLEMESSAGE_H_
|
@ -0,0 +1,919 @@ |
|||
// 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-console.h" |
|||
|
|||
#include "src/base/macros.h" |
|||
#include "src/inspector/injected-script.h" |
|||
#include "src/inspector/inspected-context.h" |
|||
#include "src/inspector/string-util.h" |
|||
#include "src/inspector/v8-console-message.h" |
|||
#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-profiler-agent-impl.h" |
|||
#include "src/inspector/v8-runtime-agent-impl.h" |
|||
#include "src/inspector/v8-stack-trace-impl.h" |
|||
#include "src/inspector/v8-value-copier.h" |
|||
|
|||
#include "include/v8-inspector.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
namespace { |
|||
|
|||
v8::Local<v8::Private> inspectedContextPrivateKey(v8::Isolate* isolate) { |
|||
return v8::Private::ForApi( |
|||
isolate, toV8StringInternalized(isolate, "V8Console#InspectedContext")); |
|||
} |
|||
|
|||
class ConsoleHelper { |
|||
public: |
|||
explicit ConsoleHelper(const v8::FunctionCallbackInfo<v8::Value>& info) |
|||
: m_info(info), |
|||
m_isolate(info.GetIsolate()), |
|||
m_context(info.GetIsolate()->GetCurrentContext()), |
|||
m_inspectedContext(nullptr), |
|||
m_inspectorClient(nullptr) {} |
|||
|
|||
v8::Local<v8::Object> ensureConsole() { |
|||
if (m_console.IsEmpty()) { |
|||
DCHECK(!m_info.Data().IsEmpty()); |
|||
DCHECK(!m_info.Data()->IsUndefined()); |
|||
m_console = m_info.Data().As<v8::Object>(); |
|||
} |
|||
return m_console; |
|||
} |
|||
|
|||
InspectedContext* ensureInspectedContext() { |
|||
if (m_inspectedContext) return m_inspectedContext; |
|||
v8::Local<v8::Object> console = ensureConsole(); |
|||
|
|||
v8::Local<v8::Private> key = inspectedContextPrivateKey(m_isolate); |
|||
v8::Local<v8::Value> inspectedContextValue; |
|||
if (!console->GetPrivate(m_context, key).ToLocal(&inspectedContextValue)) |
|||
return nullptr; |
|||
DCHECK(inspectedContextValue->IsExternal()); |
|||
m_inspectedContext = static_cast<InspectedContext*>( |
|||
inspectedContextValue.As<v8::External>()->Value()); |
|||
return m_inspectedContext; |
|||
} |
|||
|
|||
V8InspectorClient* ensureDebuggerClient() { |
|||
if (m_inspectorClient) return m_inspectorClient; |
|||
InspectedContext* inspectedContext = ensureInspectedContext(); |
|||
if (!inspectedContext) return nullptr; |
|||
m_inspectorClient = inspectedContext->inspector()->client(); |
|||
return m_inspectorClient; |
|||
} |
|||
|
|||
void reportCall(ConsoleAPIType type) { |
|||
if (!m_info.Length()) return; |
|||
std::vector<v8::Local<v8::Value>> arguments; |
|||
for (int i = 0; i < m_info.Length(); ++i) arguments.push_back(m_info[i]); |
|||
reportCall(type, arguments); |
|||
} |
|||
|
|||
void reportCallWithDefaultArgument(ConsoleAPIType type, |
|||
const String16& message) { |
|||
std::vector<v8::Local<v8::Value>> arguments; |
|||
for (int i = 0; i < m_info.Length(); ++i) arguments.push_back(m_info[i]); |
|||
if (!m_info.Length()) arguments.push_back(toV8String(m_isolate, message)); |
|||
reportCall(type, arguments); |
|||
} |
|||
|
|||
void reportCallWithArgument(ConsoleAPIType type, const String16& message) { |
|||
std::vector<v8::Local<v8::Value>> arguments(1, |
|||
toV8String(m_isolate, message)); |
|||
reportCall(type, arguments); |
|||
} |
|||
|
|||
void reportCall(ConsoleAPIType type, |
|||
const std::vector<v8::Local<v8::Value>>& arguments) { |
|||
InspectedContext* inspectedContext = ensureInspectedContext(); |
|||
if (!inspectedContext) return; |
|||
int contextGroupId = inspectedContext->contextGroupId(); |
|||
V8InspectorImpl* inspector = inspectedContext->inspector(); |
|||
std::unique_ptr<V8ConsoleMessage> message = |
|||
V8ConsoleMessage::createForConsoleAPI( |
|||
inspector->client()->currentTimeMS(), type, arguments, |
|||
inspector->debugger()->captureStackTrace(false), inspectedContext); |
|||
inspector->ensureConsoleMessageStorage(contextGroupId) |
|||
->addMessage(std::move(message)); |
|||
} |
|||
|
|||
void reportDeprecatedCall(const char* id, const String16& message) { |
|||
if (checkAndSetPrivateFlagOnConsole(id, false)) return; |
|||
std::vector<v8::Local<v8::Value>> arguments(1, |
|||
toV8String(m_isolate, message)); |
|||
reportCall(ConsoleAPIType::kWarning, arguments); |
|||
} |
|||
|
|||
bool firstArgToBoolean(bool defaultValue) { |
|||
if (m_info.Length() < 1) return defaultValue; |
|||
if (m_info[0]->IsBoolean()) return m_info[0].As<v8::Boolean>()->Value(); |
|||
return m_info[0]->BooleanValue(m_context).FromMaybe(defaultValue); |
|||
} |
|||
|
|||
String16 firstArgToString(const String16& defaultValue) { |
|||
if (m_info.Length() < 1) return defaultValue; |
|||
v8::Local<v8::String> titleValue; |
|||
if (m_info[0]->IsObject()) { |
|||
if (!m_info[0].As<v8::Object>()->ObjectProtoToString(m_context).ToLocal( |
|||
&titleValue)) |
|||
return defaultValue; |
|||
} else { |
|||
if (!m_info[0]->ToString(m_context).ToLocal(&titleValue)) |
|||
return defaultValue; |
|||
} |
|||
return toProtocolString(titleValue); |
|||
} |
|||
|
|||
v8::MaybeLocal<v8::Object> firstArgAsObject() { |
|||
if (m_info.Length() < 1 || !m_info[0]->IsObject()) |
|||
return v8::MaybeLocal<v8::Object>(); |
|||
return m_info[0].As<v8::Object>(); |
|||
} |
|||
|
|||
v8::MaybeLocal<v8::Function> firstArgAsFunction() { |
|||
if (m_info.Length() < 1 || !m_info[0]->IsFunction()) |
|||
return v8::MaybeLocal<v8::Function>(); |
|||
return m_info[0].As<v8::Function>(); |
|||
} |
|||
|
|||
v8::MaybeLocal<v8::Map> privateMap(const char* name) { |
|||
v8::Local<v8::Object> console = ensureConsole(); |
|||
v8::Local<v8::Private> privateKey = |
|||
v8::Private::ForApi(m_isolate, toV8StringInternalized(m_isolate, name)); |
|||
v8::Local<v8::Value> mapValue; |
|||
if (!console->GetPrivate(m_context, privateKey).ToLocal(&mapValue)) |
|||
return v8::MaybeLocal<v8::Map>(); |
|||
if (mapValue->IsUndefined()) { |
|||
v8::Local<v8::Map> map = v8::Map::New(m_isolate); |
|||
if (!console->SetPrivate(m_context, privateKey, map).FromMaybe(false)) |
|||
return v8::MaybeLocal<v8::Map>(); |
|||
return map; |
|||
} |
|||
return mapValue->IsMap() ? mapValue.As<v8::Map>() |
|||
: v8::MaybeLocal<v8::Map>(); |
|||
} |
|||
|
|||
int32_t getIntFromMap(v8::Local<v8::Map> map, const String16& key, |
|||
int32_t defaultValue) { |
|||
v8::Local<v8::String> v8Key = toV8String(m_isolate, key); |
|||
if (!map->Has(m_context, v8Key).FromMaybe(false)) return defaultValue; |
|||
v8::Local<v8::Value> intValue; |
|||
if (!map->Get(m_context, v8Key).ToLocal(&intValue)) return defaultValue; |
|||
return static_cast<int32_t>(intValue.As<v8::Integer>()->Value()); |
|||
} |
|||
|
|||
void setIntOnMap(v8::Local<v8::Map> map, const String16& key, int32_t value) { |
|||
v8::Local<v8::String> v8Key = toV8String(m_isolate, key); |
|||
if (!map->Set(m_context, v8Key, v8::Integer::New(m_isolate, value)) |
|||
.ToLocal(&map)) |
|||
return; |
|||
} |
|||
|
|||
double getDoubleFromMap(v8::Local<v8::Map> map, const String16& key, |
|||
double defaultValue) { |
|||
v8::Local<v8::String> v8Key = toV8String(m_isolate, key); |
|||
if (!map->Has(m_context, v8Key).FromMaybe(false)) return defaultValue; |
|||
v8::Local<v8::Value> intValue; |
|||
if (!map->Get(m_context, v8Key).ToLocal(&intValue)) return defaultValue; |
|||
return intValue.As<v8::Number>()->Value(); |
|||
} |
|||
|
|||
void setDoubleOnMap(v8::Local<v8::Map> map, const String16& key, |
|||
double value) { |
|||
v8::Local<v8::String> v8Key = toV8String(m_isolate, key); |
|||
if (!map->Set(m_context, v8Key, v8::Number::New(m_isolate, value)) |
|||
.ToLocal(&map)) |
|||
return; |
|||
} |
|||
|
|||
V8ProfilerAgentImpl* profilerAgent() { |
|||
if (V8InspectorSessionImpl* session = currentSession()) { |
|||
if (session && session->profilerAgent()->enabled()) |
|||
return session->profilerAgent(); |
|||
} |
|||
return nullptr; |
|||
} |
|||
|
|||
V8DebuggerAgentImpl* debuggerAgent() { |
|||
if (V8InspectorSessionImpl* session = currentSession()) { |
|||
if (session && session->debuggerAgent()->enabled()) |
|||
return session->debuggerAgent(); |
|||
} |
|||
return nullptr; |
|||
} |
|||
|
|||
V8InspectorSessionImpl* currentSession() { |
|||
InspectedContext* inspectedContext = ensureInspectedContext(); |
|||
if (!inspectedContext) return nullptr; |
|||
return inspectedContext->inspector()->sessionForContextGroup( |
|||
inspectedContext->contextGroupId()); |
|||
} |
|||
|
|||
private: |
|||
const v8::FunctionCallbackInfo<v8::Value>& m_info; |
|||
v8::Isolate* m_isolate; |
|||
v8::Local<v8::Context> m_context; |
|||
v8::Local<v8::Object> m_console; |
|||
InspectedContext* m_inspectedContext; |
|||
V8InspectorClient* m_inspectorClient; |
|||
|
|||
bool checkAndSetPrivateFlagOnConsole(const char* name, bool defaultValue) { |
|||
v8::Local<v8::Object> console = ensureConsole(); |
|||
v8::Local<v8::Private> key = |
|||
v8::Private::ForApi(m_isolate, toV8StringInternalized(m_isolate, name)); |
|||
v8::Local<v8::Value> flagValue; |
|||
if (!console->GetPrivate(m_context, key).ToLocal(&flagValue)) |
|||
return defaultValue; |
|||
DCHECK(flagValue->IsUndefined() || flagValue->IsBoolean()); |
|||
if (flagValue->IsBoolean()) { |
|||
DCHECK(flagValue.As<v8::Boolean>()->Value()); |
|||
return true; |
|||
} |
|||
if (!console->SetPrivate(m_context, key, v8::True(m_isolate)) |
|||
.FromMaybe(false)) |
|||
return defaultValue; |
|||
return false; |
|||
} |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(ConsoleHelper); |
|||
}; |
|||
|
|||
void returnDataCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
info.GetReturnValue().Set(info.Data()); |
|||
} |
|||
|
|||
void createBoundFunctionProperty(v8::Local<v8::Context> context, |
|||
v8::Local<v8::Object> console, |
|||
const char* name, |
|||
v8::FunctionCallback callback, |
|||
const char* description = nullptr) { |
|||
v8::Local<v8::String> funcName = |
|||
toV8StringInternalized(context->GetIsolate(), name); |
|||
v8::Local<v8::Function> func; |
|||
if (!v8::Function::New(context, callback, console, 0, |
|||
v8::ConstructorBehavior::kThrow) |
|||
.ToLocal(&func)) |
|||
return; |
|||
func->SetName(funcName); |
|||
if (description) { |
|||
v8::Local<v8::String> returnValue = |
|||
toV8String(context->GetIsolate(), description); |
|||
v8::Local<v8::Function> toStringFunction; |
|||
if (v8::Function::New(context, returnDataCallback, returnValue, 0, |
|||
v8::ConstructorBehavior::kThrow) |
|||
.ToLocal(&toStringFunction)) |
|||
createDataProperty(context, func, toV8StringInternalized( |
|||
context->GetIsolate(), "toString"), |
|||
toStringFunction); |
|||
} |
|||
createDataProperty(context, console, funcName, func); |
|||
} |
|||
|
|||
} // namespace
|
|||
|
|||
void V8Console::debugCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper(info).reportCall(ConsoleAPIType::kDebug); |
|||
} |
|||
|
|||
void V8Console::errorCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper(info).reportCall(ConsoleAPIType::kError); |
|||
} |
|||
|
|||
void V8Console::infoCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper(info).reportCall(ConsoleAPIType::kInfo); |
|||
} |
|||
|
|||
void V8Console::logCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper(info).reportCall(ConsoleAPIType::kLog); |
|||
} |
|||
|
|||
void V8Console::warnCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper(info).reportCall(ConsoleAPIType::kWarning); |
|||
} |
|||
|
|||
void V8Console::dirCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper(info).reportCall(ConsoleAPIType::kDir); |
|||
} |
|||
|
|||
void V8Console::dirxmlCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper(info).reportCall(ConsoleAPIType::kDirXML); |
|||
} |
|||
|
|||
void V8Console::tableCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper(info).reportCall(ConsoleAPIType::kTable); |
|||
} |
|||
|
|||
void V8Console::traceCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kTrace, |
|||
String16("console.trace")); |
|||
} |
|||
|
|||
void V8Console::groupCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kStartGroup, |
|||
String16("console.group")); |
|||
} |
|||
|
|||
void V8Console::groupCollapsedCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper(info).reportCallWithDefaultArgument( |
|||
ConsoleAPIType::kStartGroupCollapsed, String16("console.groupCollapsed")); |
|||
} |
|||
|
|||
void V8Console::groupEndCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper(info).reportCallWithDefaultArgument( |
|||
ConsoleAPIType::kEndGroup, String16("console.groupEnd")); |
|||
} |
|||
|
|||
void V8Console::clearCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kClear, |
|||
String16("console.clear")); |
|||
} |
|||
|
|||
void V8Console::countCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper helper(info); |
|||
|
|||
String16 title = helper.firstArgToString(String16()); |
|||
String16 identifier; |
|||
if (title.isEmpty()) { |
|||
std::unique_ptr<V8StackTraceImpl> stackTrace = |
|||
V8StackTraceImpl::capture(nullptr, 0, 1); |
|||
if (stackTrace && !stackTrace->isEmpty()) { |
|||
identifier = toString16(stackTrace->topSourceURL()) + ":" + |
|||
String16::fromInteger(stackTrace->topLineNumber()); |
|||
} |
|||
} else { |
|||
identifier = title + "@"; |
|||
} |
|||
|
|||
v8::Local<v8::Map> countMap; |
|||
if (!helper.privateMap("V8Console#countMap").ToLocal(&countMap)) return; |
|||
int32_t count = helper.getIntFromMap(countMap, identifier, 0) + 1; |
|||
helper.setIntOnMap(countMap, identifier, count); |
|||
helper.reportCallWithArgument(ConsoleAPIType::kCount, |
|||
title + ": " + String16::fromInteger(count)); |
|||
} |
|||
|
|||
void V8Console::assertCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper helper(info); |
|||
if (helper.firstArgToBoolean(false)) return; |
|||
|
|||
std::vector<v8::Local<v8::Value>> arguments; |
|||
for (int i = 1; i < info.Length(); ++i) arguments.push_back(info[i]); |
|||
if (info.Length() < 2) |
|||
arguments.push_back( |
|||
toV8String(info.GetIsolate(), String16("console.assert"))); |
|||
helper.reportCall(ConsoleAPIType::kAssert, arguments); |
|||
|
|||
if (V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent()) |
|||
debuggerAgent->breakProgramOnException( |
|||
protocol::Debugger::Paused::ReasonEnum::Assert, nullptr); |
|||
} |
|||
|
|||
void V8Console::markTimelineCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper(info).reportDeprecatedCall("V8Console#markTimelineDeprecated", |
|||
"'console.markTimeline' is " |
|||
"deprecated. Please use " |
|||
"'console.timeStamp' instead."); |
|||
timeStampCallback(info); |
|||
} |
|||
|
|||
void V8Console::profileCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper helper(info); |
|||
if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent()) |
|||
profilerAgent->consoleProfile(helper.firstArgToString(String16())); |
|||
} |
|||
|
|||
void V8Console::profileEndCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper helper(info); |
|||
if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent()) |
|||
profilerAgent->consoleProfileEnd(helper.firstArgToString(String16())); |
|||
} |
|||
|
|||
static void timeFunction(const v8::FunctionCallbackInfo<v8::Value>& info, |
|||
bool timelinePrefix) { |
|||
ConsoleHelper helper(info); |
|||
if (V8InspectorClient* client = helper.ensureDebuggerClient()) { |
|||
String16 protocolTitle = helper.firstArgToString("default"); |
|||
if (timelinePrefix) protocolTitle = "Timeline '" + protocolTitle + "'"; |
|||
client->consoleTime(toStringView(protocolTitle)); |
|||
|
|||
v8::Local<v8::Map> timeMap; |
|||
if (!helper.privateMap("V8Console#timeMap").ToLocal(&timeMap)) return; |
|||
helper.setDoubleOnMap(timeMap, protocolTitle, client->currentTimeMS()); |
|||
} |
|||
} |
|||
|
|||
static void timeEndFunction(const v8::FunctionCallbackInfo<v8::Value>& info, |
|||
bool timelinePrefix) { |
|||
ConsoleHelper helper(info); |
|||
if (V8InspectorClient* client = helper.ensureDebuggerClient()) { |
|||
String16 protocolTitle = helper.firstArgToString("default"); |
|||
if (timelinePrefix) protocolTitle = "Timeline '" + protocolTitle + "'"; |
|||
client->consoleTimeEnd(toStringView(protocolTitle)); |
|||
|
|||
v8::Local<v8::Map> timeMap; |
|||
if (!helper.privateMap("V8Console#timeMap").ToLocal(&timeMap)) return; |
|||
double elapsed = client->currentTimeMS() - |
|||
helper.getDoubleFromMap(timeMap, protocolTitle, 0.0); |
|||
String16 message = |
|||
protocolTitle + ": " + String16::fromDoublePrecision3(elapsed) + "ms"; |
|||
helper.reportCallWithArgument(ConsoleAPIType::kTimeEnd, message); |
|||
} |
|||
} |
|||
|
|||
void V8Console::timelineCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper(info).reportDeprecatedCall( |
|||
"V8Console#timeline", |
|||
"'console.timeline' is deprecated. Please use 'console.time' instead."); |
|||
timeFunction(info, true); |
|||
} |
|||
|
|||
void V8Console::timelineEndCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper(info).reportDeprecatedCall("V8Console#timelineEnd", |
|||
"'console.timelineEnd' is " |
|||
"deprecated. Please use " |
|||
"'console.timeEnd' instead."); |
|||
timeEndFunction(info, true); |
|||
} |
|||
|
|||
void V8Console::timeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
timeFunction(info, false); |
|||
} |
|||
|
|||
void V8Console::timeEndCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
timeEndFunction(info, false); |
|||
} |
|||
|
|||
void V8Console::timeStampCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper helper(info); |
|||
if (V8InspectorClient* client = helper.ensureDebuggerClient()) { |
|||
String16 title = helper.firstArgToString(String16()); |
|||
client->consoleTimeStamp(toStringView(title)); |
|||
} |
|||
} |
|||
|
|||
void V8Console::memoryGetterCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
if (V8InspectorClient* client = ConsoleHelper(info).ensureDebuggerClient()) { |
|||
v8::Local<v8::Value> memoryValue; |
|||
if (!client |
|||
->memoryInfo(info.GetIsolate(), |
|||
info.GetIsolate()->GetCurrentContext()) |
|||
.ToLocal(&memoryValue)) |
|||
return; |
|||
info.GetReturnValue().Set(memoryValue); |
|||
} |
|||
} |
|||
|
|||
void V8Console::memorySetterCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
// We can't make the attribute readonly as it breaks existing code that relies
|
|||
// on being able to assign to console.memory in strict mode. Instead, the
|
|||
// setter just ignores the passed value. http://crbug.com/468611
|
|||
} |
|||
|
|||
void V8Console::keysCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
v8::Isolate* isolate = info.GetIsolate(); |
|||
info.GetReturnValue().Set(v8::Array::New(isolate)); |
|||
|
|||
ConsoleHelper helper(info); |
|||
v8::Local<v8::Object> obj; |
|||
if (!helper.firstArgAsObject().ToLocal(&obj)) return; |
|||
v8::Local<v8::Array> names; |
|||
if (!obj->GetOwnPropertyNames(isolate->GetCurrentContext()).ToLocal(&names)) |
|||
return; |
|||
info.GetReturnValue().Set(names); |
|||
} |
|||
|
|||
void V8Console::valuesCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
v8::Isolate* isolate = info.GetIsolate(); |
|||
info.GetReturnValue().Set(v8::Array::New(isolate)); |
|||
|
|||
ConsoleHelper helper(info); |
|||
v8::Local<v8::Object> obj; |
|||
if (!helper.firstArgAsObject().ToLocal(&obj)) return; |
|||
v8::Local<v8::Array> names; |
|||
v8::Local<v8::Context> context = isolate->GetCurrentContext(); |
|||
if (!obj->GetOwnPropertyNames(context).ToLocal(&names)) return; |
|||
v8::Local<v8::Array> values = v8::Array::New(isolate, names->Length()); |
|||
for (uint32_t i = 0; i < names->Length(); ++i) { |
|||
v8::Local<v8::Value> key; |
|||
if (!names->Get(context, i).ToLocal(&key)) continue; |
|||
v8::Local<v8::Value> value; |
|||
if (!obj->Get(context, key).ToLocal(&value)) continue; |
|||
createDataProperty(context, values, i, value); |
|||
} |
|||
info.GetReturnValue().Set(values); |
|||
} |
|||
|
|||
static void setFunctionBreakpoint(ConsoleHelper& helper, |
|||
v8::Local<v8::Function> function, |
|||
V8DebuggerAgentImpl::BreakpointSource source, |
|||
const String16& condition, bool enable) { |
|||
V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent(); |
|||
if (!debuggerAgent) return; |
|||
String16 scriptId = String16::fromInteger(function->ScriptId()); |
|||
int lineNumber = function->GetScriptLineNumber(); |
|||
int columnNumber = function->GetScriptColumnNumber(); |
|||
if (lineNumber == v8::Function::kLineOffsetNotFound || |
|||
columnNumber == v8::Function::kLineOffsetNotFound) |
|||
return; |
|||
if (enable) |
|||
debuggerAgent->setBreakpointAt(scriptId, lineNumber, columnNumber, source, |
|||
condition); |
|||
else |
|||
debuggerAgent->removeBreakpointAt(scriptId, lineNumber, columnNumber, |
|||
source); |
|||
} |
|||
|
|||
void V8Console::debugFunctionCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper helper(info); |
|||
v8::Local<v8::Function> function; |
|||
if (!helper.firstArgAsFunction().ToLocal(&function)) return; |
|||
setFunctionBreakpoint(helper, function, |
|||
V8DebuggerAgentImpl::DebugCommandBreakpointSource, |
|||
String16(), true); |
|||
} |
|||
|
|||
void V8Console::undebugFunctionCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper helper(info); |
|||
v8::Local<v8::Function> function; |
|||
if (!helper.firstArgAsFunction().ToLocal(&function)) return; |
|||
setFunctionBreakpoint(helper, function, |
|||
V8DebuggerAgentImpl::DebugCommandBreakpointSource, |
|||
String16(), false); |
|||
} |
|||
|
|||
void V8Console::monitorFunctionCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper helper(info); |
|||
v8::Local<v8::Function> function; |
|||
if (!helper.firstArgAsFunction().ToLocal(&function)) return; |
|||
v8::Local<v8::Value> name = function->GetName(); |
|||
if (!name->IsString() || !v8::Local<v8::String>::Cast(name)->Length()) |
|||
name = function->GetInferredName(); |
|||
String16 functionName = toProtocolStringWithTypeCheck(name); |
|||
String16Builder builder; |
|||
builder.append("console.log(\"function "); |
|||
if (functionName.isEmpty()) |
|||
builder.append("(anonymous function)"); |
|||
else |
|||
builder.append(functionName); |
|||
builder.append( |
|||
" called\" + (arguments.length > 0 ? \" with arguments: \" + " |
|||
"Array.prototype.join.call(arguments, \", \") : \"\")) && false"); |
|||
setFunctionBreakpoint(helper, function, |
|||
V8DebuggerAgentImpl::MonitorCommandBreakpointSource, |
|||
builder.toString(), true); |
|||
} |
|||
|
|||
void V8Console::unmonitorFunctionCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper helper(info); |
|||
v8::Local<v8::Function> function; |
|||
if (!helper.firstArgAsFunction().ToLocal(&function)) return; |
|||
setFunctionBreakpoint(helper, function, |
|||
V8DebuggerAgentImpl::MonitorCommandBreakpointSource, |
|||
String16(), false); |
|||
} |
|||
|
|||
void V8Console::lastEvaluationResultCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ConsoleHelper helper(info); |
|||
InspectedContext* context = helper.ensureInspectedContext(); |
|||
if (!context) return; |
|||
if (InjectedScript* injectedScript = context->getInjectedScript()) |
|||
info.GetReturnValue().Set(injectedScript->lastEvaluationResult()); |
|||
} |
|||
|
|||
static void inspectImpl(const v8::FunctionCallbackInfo<v8::Value>& info, |
|||
bool copyToClipboard) { |
|||
if (info.Length() < 1) return; |
|||
if (!copyToClipboard) info.GetReturnValue().Set(info[0]); |
|||
|
|||
ConsoleHelper helper(info); |
|||
InspectedContext* context = helper.ensureInspectedContext(); |
|||
if (!context) return; |
|||
InjectedScript* injectedScript = context->getInjectedScript(); |
|||
if (!injectedScript) return; |
|||
ErrorString errorString; |
|||
std::unique_ptr<protocol::Runtime::RemoteObject> wrappedObject = |
|||
injectedScript->wrapObject(&errorString, info[0], "", |
|||
false /** forceValueType */, |
|||
false /** generatePreview */); |
|||
if (!wrappedObject || !errorString.isEmpty()) return; |
|||
|
|||
std::unique_ptr<protocol::DictionaryValue> hints = |
|||
protocol::DictionaryValue::create(); |
|||
if (copyToClipboard) hints->setBoolean("copyToClipboard", true); |
|||
if (V8InspectorSessionImpl* session = helper.currentSession()) |
|||
session->runtimeAgent()->inspect(std::move(wrappedObject), |
|||
std::move(hints)); |
|||
} |
|||
|
|||
void V8Console::inspectCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
inspectImpl(info, false); |
|||
} |
|||
|
|||
void V8Console::copyCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
inspectImpl(info, true); |
|||
} |
|||
|
|||
void V8Console::inspectedObject(const v8::FunctionCallbackInfo<v8::Value>& info, |
|||
unsigned num) { |
|||
DCHECK(num < V8InspectorSessionImpl::kInspectedObjectBufferSize); |
|||
ConsoleHelper helper(info); |
|||
if (V8InspectorSessionImpl* session = helper.currentSession()) { |
|||
V8InspectorSession::Inspectable* object = session->inspectedObject(num); |
|||
v8::Isolate* isolate = info.GetIsolate(); |
|||
if (object) |
|||
info.GetReturnValue().Set(object->get(isolate->GetCurrentContext())); |
|||
else |
|||
info.GetReturnValue().Set(v8::Undefined(isolate)); |
|||
} |
|||
} |
|||
|
|||
v8::Local<v8::Object> V8Console::createConsole( |
|||
InspectedContext* inspectedContext, bool hasMemoryAttribute) { |
|||
v8::Local<v8::Context> context = inspectedContext->context(); |
|||
v8::Context::Scope contextScope(context); |
|||
v8::Isolate* isolate = context->GetIsolate(); |
|||
v8::MicrotasksScope microtasksScope(isolate, |
|||
v8::MicrotasksScope::kDoNotRunMicrotasks); |
|||
|
|||
v8::Local<v8::Object> console = v8::Object::New(isolate); |
|||
bool success = |
|||
console->SetPrototype(context, v8::Object::New(isolate)).FromMaybe(false); |
|||
DCHECK(success); |
|||
USE(success); |
|||
|
|||
createBoundFunctionProperty(context, console, "debug", |
|||
V8Console::debugCallback); |
|||
createBoundFunctionProperty(context, console, "error", |
|||
V8Console::errorCallback); |
|||
createBoundFunctionProperty(context, console, "info", |
|||
V8Console::infoCallback); |
|||
createBoundFunctionProperty(context, console, "log", V8Console::logCallback); |
|||
createBoundFunctionProperty(context, console, "warn", |
|||
V8Console::warnCallback); |
|||
createBoundFunctionProperty(context, console, "dir", V8Console::dirCallback); |
|||
createBoundFunctionProperty(context, console, "dirxml", |
|||
V8Console::dirxmlCallback); |
|||
createBoundFunctionProperty(context, console, "table", |
|||
V8Console::tableCallback); |
|||
createBoundFunctionProperty(context, console, "trace", |
|||
V8Console::traceCallback); |
|||
createBoundFunctionProperty(context, console, "group", |
|||
V8Console::groupCallback); |
|||
createBoundFunctionProperty(context, console, "groupCollapsed", |
|||
V8Console::groupCollapsedCallback); |
|||
createBoundFunctionProperty(context, console, "groupEnd", |
|||
V8Console::groupEndCallback); |
|||
createBoundFunctionProperty(context, console, "clear", |
|||
V8Console::clearCallback); |
|||
createBoundFunctionProperty(context, console, "count", |
|||
V8Console::countCallback); |
|||
createBoundFunctionProperty(context, console, "assert", |
|||
V8Console::assertCallback); |
|||
createBoundFunctionProperty(context, console, "markTimeline", |
|||
V8Console::markTimelineCallback); |
|||
createBoundFunctionProperty(context, console, "profile", |
|||
V8Console::profileCallback); |
|||
createBoundFunctionProperty(context, console, "profileEnd", |
|||
V8Console::profileEndCallback); |
|||
createBoundFunctionProperty(context, console, "timeline", |
|||
V8Console::timelineCallback); |
|||
createBoundFunctionProperty(context, console, "timelineEnd", |
|||
V8Console::timelineEndCallback); |
|||
createBoundFunctionProperty(context, console, "time", |
|||
V8Console::timeCallback); |
|||
createBoundFunctionProperty(context, console, "timeEnd", |
|||
V8Console::timeEndCallback); |
|||
createBoundFunctionProperty(context, console, "timeStamp", |
|||
V8Console::timeStampCallback); |
|||
|
|||
if (hasMemoryAttribute) |
|||
console->SetAccessorProperty( |
|||
toV8StringInternalized(isolate, "memory"), |
|||
v8::Function::New(context, V8Console::memoryGetterCallback, console, 0, |
|||
v8::ConstructorBehavior::kThrow) |
|||
.ToLocalChecked(), |
|||
v8::Function::New(context, V8Console::memorySetterCallback, |
|||
v8::Local<v8::Value>(), 0, |
|||
v8::ConstructorBehavior::kThrow) |
|||
.ToLocalChecked(), |
|||
static_cast<v8::PropertyAttribute>(v8::None), v8::DEFAULT); |
|||
|
|||
console->SetPrivate(context, inspectedContextPrivateKey(isolate), |
|||
v8::External::New(isolate, inspectedContext)); |
|||
return console; |
|||
} |
|||
|
|||
void V8Console::clearInspectedContextIfNeeded(v8::Local<v8::Context> context, |
|||
v8::Local<v8::Object> console) { |
|||
v8::Isolate* isolate = context->GetIsolate(); |
|||
console->SetPrivate(context, inspectedContextPrivateKey(isolate), |
|||
v8::External::New(isolate, nullptr)); |
|||
} |
|||
|
|||
v8::Local<v8::Object> V8Console::createCommandLineAPI( |
|||
InspectedContext* inspectedContext) { |
|||
v8::Local<v8::Context> context = inspectedContext->context(); |
|||
v8::Isolate* isolate = context->GetIsolate(); |
|||
v8::MicrotasksScope microtasksScope(isolate, |
|||
v8::MicrotasksScope::kDoNotRunMicrotasks); |
|||
|
|||
v8::Local<v8::Object> commandLineAPI = v8::Object::New(isolate); |
|||
bool success = |
|||
commandLineAPI->SetPrototype(context, v8::Null(isolate)).FromMaybe(false); |
|||
DCHECK(success); |
|||
USE(success); |
|||
|
|||
createBoundFunctionProperty(context, commandLineAPI, "dir", |
|||
V8Console::dirCallback, |
|||
"function dir(value) { [Command Line API] }"); |
|||
createBoundFunctionProperty(context, commandLineAPI, "dirxml", |
|||
V8Console::dirxmlCallback, |
|||
"function dirxml(value) { [Command Line API] }"); |
|||
createBoundFunctionProperty(context, commandLineAPI, "profile", |
|||
V8Console::profileCallback, |
|||
"function profile(title) { [Command Line API] }"); |
|||
createBoundFunctionProperty( |
|||
context, commandLineAPI, "profileEnd", V8Console::profileEndCallback, |
|||
"function profileEnd(title) { [Command Line API] }"); |
|||
createBoundFunctionProperty(context, commandLineAPI, "clear", |
|||
V8Console::clearCallback, |
|||
"function clear() { [Command Line API] }"); |
|||
createBoundFunctionProperty( |
|||
context, commandLineAPI, "table", V8Console::tableCallback, |
|||
"function table(data, [columns]) { [Command Line API] }"); |
|||
|
|||
createBoundFunctionProperty(context, commandLineAPI, "keys", |
|||
V8Console::keysCallback, |
|||
"function keys(object) { [Command Line API] }"); |
|||
createBoundFunctionProperty(context, commandLineAPI, "values", |
|||
V8Console::valuesCallback, |
|||
"function values(object) { [Command Line API] }"); |
|||
createBoundFunctionProperty( |
|||
context, commandLineAPI, "debug", V8Console::debugFunctionCallback, |
|||
"function debug(function) { [Command Line API] }"); |
|||
createBoundFunctionProperty( |
|||
context, commandLineAPI, "undebug", V8Console::undebugFunctionCallback, |
|||
"function undebug(function) { [Command Line API] }"); |
|||
createBoundFunctionProperty( |
|||
context, commandLineAPI, "monitor", V8Console::monitorFunctionCallback, |
|||
"function monitor(function) { [Command Line API] }"); |
|||
createBoundFunctionProperty( |
|||
context, commandLineAPI, "unmonitor", |
|||
V8Console::unmonitorFunctionCallback, |
|||
"function unmonitor(function) { [Command Line API] }"); |
|||
createBoundFunctionProperty( |
|||
context, commandLineAPI, "inspect", V8Console::inspectCallback, |
|||
"function inspect(object) { [Command Line API] }"); |
|||
createBoundFunctionProperty(context, commandLineAPI, "copy", |
|||
V8Console::copyCallback, |
|||
"function copy(value) { [Command Line API] }"); |
|||
createBoundFunctionProperty(context, commandLineAPI, "$_", |
|||
V8Console::lastEvaluationResultCallback); |
|||
createBoundFunctionProperty(context, commandLineAPI, "$0", |
|||
V8Console::inspectedObject0); |
|||
createBoundFunctionProperty(context, commandLineAPI, "$1", |
|||
V8Console::inspectedObject1); |
|||
createBoundFunctionProperty(context, commandLineAPI, "$2", |
|||
V8Console::inspectedObject2); |
|||
createBoundFunctionProperty(context, commandLineAPI, "$3", |
|||
V8Console::inspectedObject3); |
|||
createBoundFunctionProperty(context, commandLineAPI, "$4", |
|||
V8Console::inspectedObject4); |
|||
|
|||
inspectedContext->inspector()->client()->installAdditionalCommandLineAPI( |
|||
context, commandLineAPI); |
|||
|
|||
commandLineAPI->SetPrivate(context, inspectedContextPrivateKey(isolate), |
|||
v8::External::New(isolate, inspectedContext)); |
|||
return commandLineAPI; |
|||
} |
|||
|
|||
static bool isCommandLineAPIGetter(const String16& name) { |
|||
if (name.length() != 2) return false; |
|||
// $0 ... $4, $_
|
|||
return name[0] == '$' && |
|||
((name[1] >= '0' && name[1] <= '4') || name[1] == '_'); |
|||
} |
|||
|
|||
void V8Console::CommandLineAPIScope::accessorGetterCallback( |
|||
v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) { |
|||
CommandLineAPIScope* scope = static_cast<CommandLineAPIScope*>( |
|||
info.Data().As<v8::External>()->Value()); |
|||
DCHECK(scope); |
|||
|
|||
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); |
|||
if (scope->m_cleanup) { |
|||
bool removed = info.Holder()->Delete(context, name).FromMaybe(false); |
|||
DCHECK(removed); |
|||
USE(removed); |
|||
return; |
|||
} |
|||
v8::Local<v8::Object> commandLineAPI = scope->m_commandLineAPI; |
|||
|
|||
v8::Local<v8::Value> value; |
|||
if (!commandLineAPI->Get(context, name).ToLocal(&value)) return; |
|||
if (isCommandLineAPIGetter(toProtocolStringWithTypeCheck(name))) { |
|||
DCHECK(value->IsFunction()); |
|||
v8::MicrotasksScope microtasks(info.GetIsolate(), |
|||
v8::MicrotasksScope::kDoNotRunMicrotasks); |
|||
if (value.As<v8::Function>() |
|||
->Call(context, commandLineAPI, 0, nullptr) |
|||
.ToLocal(&value)) |
|||
info.GetReturnValue().Set(value); |
|||
} else { |
|||
info.GetReturnValue().Set(value); |
|||
} |
|||
} |
|||
|
|||
void V8Console::CommandLineAPIScope::accessorSetterCallback( |
|||
v8::Local<v8::Name> name, v8::Local<v8::Value> value, |
|||
const v8::PropertyCallbackInfo<void>& info) { |
|||
CommandLineAPIScope* scope = static_cast<CommandLineAPIScope*>( |
|||
info.Data().As<v8::External>()->Value()); |
|||
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); |
|||
if (!info.Holder()->Delete(context, name).FromMaybe(false)) return; |
|||
if (!info.Holder()->CreateDataProperty(context, name, value).FromMaybe(false)) |
|||
return; |
|||
bool removed = |
|||
scope->m_installedMethods->Delete(context, name).FromMaybe(false); |
|||
DCHECK(removed); |
|||
USE(removed); |
|||
} |
|||
|
|||
V8Console::CommandLineAPIScope::CommandLineAPIScope( |
|||
v8::Local<v8::Context> context, v8::Local<v8::Object> commandLineAPI, |
|||
v8::Local<v8::Object> global) |
|||
: m_context(context), |
|||
m_commandLineAPI(commandLineAPI), |
|||
m_global(global), |
|||
m_installedMethods(v8::Set::New(context->GetIsolate())), |
|||
m_cleanup(false) { |
|||
v8::Local<v8::Array> names; |
|||
if (!m_commandLineAPI->GetOwnPropertyNames(context).ToLocal(&names)) return; |
|||
v8::Local<v8::External> externalThis = |
|||
v8::External::New(context->GetIsolate(), this); |
|||
for (uint32_t i = 0; i < names->Length(); ++i) { |
|||
v8::Local<v8::Value> name; |
|||
if (!names->Get(context, i).ToLocal(&name) || !name->IsName()) continue; |
|||
if (m_global->Has(context, name).FromMaybe(true)) continue; |
|||
if (!m_installedMethods->Add(context, name).ToLocal(&m_installedMethods)) |
|||
continue; |
|||
if (!m_global |
|||
->SetAccessor(context, v8::Local<v8::Name>::Cast(name), |
|||
CommandLineAPIScope::accessorGetterCallback, |
|||
CommandLineAPIScope::accessorSetterCallback, |
|||
externalThis, v8::DEFAULT, v8::DontEnum) |
|||
.FromMaybe(false)) { |
|||
bool removed = m_installedMethods->Delete(context, name).FromMaybe(false); |
|||
DCHECK(removed); |
|||
USE(removed); |
|||
continue; |
|||
} |
|||
} |
|||
} |
|||
|
|||
V8Console::CommandLineAPIScope::~CommandLineAPIScope() { |
|||
m_cleanup = true; |
|||
v8::Local<v8::Array> names = m_installedMethods->AsArray(); |
|||
for (uint32_t i = 0; i < names->Length(); ++i) { |
|||
v8::Local<v8::Value> name; |
|||
if (!names->Get(m_context, i).ToLocal(&name) || !name->IsName()) continue; |
|||
if (name->IsString()) { |
|||
v8::Local<v8::Value> descriptor; |
|||
bool success = m_global |
|||
->GetOwnPropertyDescriptor( |
|||
m_context, v8::Local<v8::String>::Cast(name)) |
|||
.ToLocal(&descriptor); |
|||
DCHECK(success); |
|||
USE(success); |
|||
} |
|||
} |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,119 @@ |
|||
// 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_V8CONSOLE_H_ |
|||
#define V8_INSPECTOR_V8CONSOLE_H_ |
|||
|
|||
#include "src/base/macros.h" |
|||
|
|||
#include "include/v8.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
class InspectedContext; |
|||
|
|||
// Console API
|
|||
// https://console.spec.whatwg.org/#console-interface
|
|||
class V8Console { |
|||
public: |
|||
static v8::Local<v8::Object> createConsole(InspectedContext*, |
|||
bool hasMemoryAttribute); |
|||
static void clearInspectedContextIfNeeded(v8::Local<v8::Context>, |
|||
v8::Local<v8::Object> console); |
|||
static v8::Local<v8::Object> createCommandLineAPI(InspectedContext*); |
|||
|
|||
class CommandLineAPIScope { |
|||
public: |
|||
CommandLineAPIScope(v8::Local<v8::Context>, |
|||
v8::Local<v8::Object> commandLineAPI, |
|||
v8::Local<v8::Object> global); |
|||
~CommandLineAPIScope(); |
|||
|
|||
private: |
|||
static void accessorGetterCallback( |
|||
v8::Local<v8::Name>, const v8::PropertyCallbackInfo<v8::Value>&); |
|||
static void accessorSetterCallback(v8::Local<v8::Name>, |
|||
v8::Local<v8::Value>, |
|||
const v8::PropertyCallbackInfo<void>&); |
|||
|
|||
v8::Local<v8::Context> m_context; |
|||
v8::Local<v8::Object> m_commandLineAPI; |
|||
v8::Local<v8::Object> m_global; |
|||
v8::Local<v8::Set> m_installedMethods; |
|||
bool m_cleanup; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(CommandLineAPIScope); |
|||
}; |
|||
|
|||
private: |
|||
static void debugCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void errorCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void infoCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void logCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void warnCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void dirCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void dirxmlCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void tableCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void traceCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void groupCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void groupCollapsedCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void groupEndCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void clearCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void countCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void assertCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void markTimelineCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void profileCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void profileEndCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void timelineCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void timelineEndCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void timeCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void timeEndCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void timeStampCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
// TODO(foolip): There is no spec for the Memory Info API, see blink-dev:
|
|||
// https://groups.google.com/a/chromium.org/d/msg/blink-dev/g5YRCGpC9vs/b4OJz71NmPwJ
|
|||
static void memoryGetterCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void memorySetterCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
|
|||
// CommandLineAPI
|
|||
static void keysCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void valuesCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void debugFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void undebugFunctionCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void monitorFunctionCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void unmonitorFunctionCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void lastEvaluationResultCallback( |
|||
const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void inspectCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void copyCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
static void inspectedObject(const v8::FunctionCallbackInfo<v8::Value>&, |
|||
unsigned num); |
|||
static void inspectedObject0( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
inspectedObject(info, 0); |
|||
} |
|||
static void inspectedObject1( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
inspectedObject(info, 1); |
|||
} |
|||
static void inspectedObject2( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
inspectedObject(info, 2); |
|||
} |
|||
static void inspectedObject3( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
inspectedObject(info, 3); |
|||
} |
|||
static void inspectedObject4( |
|||
const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
inspectedObject(info, 4); |
|||
} |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_V8CONSOLE_H_
|
File diff suppressed because it is too large
@ -0,0 +1,224 @@ |
|||
// 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_V8DEBUGGERAGENTIMPL_H_ |
|||
#define V8_INSPECTOR_V8DEBUGGERAGENTIMPL_H_ |
|||
|
|||
#include <vector> |
|||
|
|||
#include "src/base/macros.h" |
|||
#include "src/inspector/java-script-call-frame.h" |
|||
#include "src/inspector/protocol/Debugger.h" |
|||
#include "src/inspector/protocol/Forward.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
struct ScriptBreakpoint; |
|||
class JavaScriptCallFrame; |
|||
class PromiseTracker; |
|||
class V8Debugger; |
|||
class V8DebuggerScript; |
|||
class V8InspectorImpl; |
|||
class V8InspectorSessionImpl; |
|||
class V8Regex; |
|||
class V8StackTraceImpl; |
|||
|
|||
using protocol::ErrorString; |
|||
using protocol::Maybe; |
|||
|
|||
class V8DebuggerAgentImpl : public protocol::Debugger::Backend { |
|||
public: |
|||
enum SkipPauseRequest { |
|||
RequestNoSkip, |
|||
RequestContinue, |
|||
RequestStepInto, |
|||
RequestStepOut, |
|||
RequestStepFrame |
|||
}; |
|||
|
|||
enum BreakpointSource { |
|||
UserBreakpointSource, |
|||
DebugCommandBreakpointSource, |
|||
MonitorCommandBreakpointSource |
|||
}; |
|||
|
|||
V8DebuggerAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*, |
|||
protocol::DictionaryValue* state); |
|||
~V8DebuggerAgentImpl() override; |
|||
void restore(); |
|||
|
|||
// Part of the protocol.
|
|||
void enable(ErrorString*) override; |
|||
void disable(ErrorString*) override; |
|||
void setBreakpointsActive(ErrorString*, bool active) override; |
|||
void setSkipAllPauses(ErrorString*, bool skip) override; |
|||
void setBreakpointByUrl( |
|||
ErrorString*, int lineNumber, const Maybe<String16>& optionalURL, |
|||
const Maybe<String16>& optionalURLRegex, |
|||
const Maybe<int>& optionalColumnNumber, |
|||
const Maybe<String16>& optionalCondition, String16*, |
|||
std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations) |
|||
override; |
|||
void setBreakpoint( |
|||
ErrorString*, std::unique_ptr<protocol::Debugger::Location>, |
|||
const Maybe<String16>& optionalCondition, String16*, |
|||
std::unique_ptr<protocol::Debugger::Location>* actualLocation) override; |
|||
void removeBreakpoint(ErrorString*, const String16& breakpointId) override; |
|||
void continueToLocation( |
|||
ErrorString*, std::unique_ptr<protocol::Debugger::Location>) override; |
|||
void searchInContent( |
|||
ErrorString*, const String16& scriptId, const String16& query, |
|||
const Maybe<bool>& optionalCaseSensitive, |
|||
const Maybe<bool>& optionalIsRegex, |
|||
std::unique_ptr<protocol::Array<protocol::Debugger::SearchMatch>>*) |
|||
override; |
|||
void setScriptSource( |
|||
ErrorString*, const String16& inScriptId, const String16& inScriptSource, |
|||
const Maybe<bool>& dryRun, |
|||
Maybe<protocol::Array<protocol::Debugger::CallFrame>>* optOutCallFrames, |
|||
Maybe<bool>* optOutStackChanged, |
|||
Maybe<protocol::Runtime::StackTrace>* optOutAsyncStackTrace, |
|||
Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) override; |
|||
void restartFrame( |
|||
ErrorString*, const String16& callFrameId, |
|||
std::unique_ptr<protocol::Array<protocol::Debugger::CallFrame>>* |
|||
newCallFrames, |
|||
Maybe<protocol::Runtime::StackTrace>* asyncStackTrace) override; |
|||
void getScriptSource(ErrorString*, const String16& scriptId, |
|||
String16* scriptSource) override; |
|||
void pause(ErrorString*) override; |
|||
void resume(ErrorString*) override; |
|||
void stepOver(ErrorString*) override; |
|||
void stepInto(ErrorString*) override; |
|||
void stepOut(ErrorString*) override; |
|||
void setPauseOnExceptions(ErrorString*, const String16& pauseState) override; |
|||
void evaluateOnCallFrame( |
|||
ErrorString*, const String16& callFrameId, const String16& expression, |
|||
const Maybe<String16>& objectGroup, |
|||
const Maybe<bool>& includeCommandLineAPI, const Maybe<bool>& silent, |
|||
const Maybe<bool>& returnByValue, const Maybe<bool>& generatePreview, |
|||
std::unique_ptr<protocol::Runtime::RemoteObject>* result, |
|||
Maybe<protocol::Runtime::ExceptionDetails>*) override; |
|||
void setVariableValue( |
|||
ErrorString*, int scopeNumber, const String16& variableName, |
|||
std::unique_ptr<protocol::Runtime::CallArgument> newValue, |
|||
const String16& callFrame) override; |
|||
void setAsyncCallStackDepth(ErrorString*, int depth) override; |
|||
void setBlackboxPatterns( |
|||
ErrorString*, |
|||
std::unique_ptr<protocol::Array<String16>> patterns) override; |
|||
void setBlackboxedRanges( |
|||
ErrorString*, const String16& scriptId, |
|||
std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>> |
|||
positions) override; |
|||
|
|||
bool enabled(); |
|||
|
|||
void setBreakpointAt(const String16& scriptId, int lineNumber, |
|||
int columnNumber, BreakpointSource, |
|||
const String16& condition = String16()); |
|||
void removeBreakpointAt(const String16& scriptId, int lineNumber, |
|||
int columnNumber, BreakpointSource); |
|||
void schedulePauseOnNextStatement( |
|||
const String16& breakReason, |
|||
std::unique_ptr<protocol::DictionaryValue> data); |
|||
void cancelPauseOnNextStatement(); |
|||
void breakProgram(const String16& breakReason, |
|||
std::unique_ptr<protocol::DictionaryValue> data); |
|||
void breakProgramOnException(const String16& breakReason, |
|||
std::unique_ptr<protocol::DictionaryValue> data); |
|||
|
|||
void reset(); |
|||
|
|||
// Interface for V8InspectorImpl
|
|||
SkipPauseRequest didPause(v8::Local<v8::Context>, |
|||
v8::Local<v8::Value> exception, |
|||
const std::vector<String16>& hitBreakpoints, |
|||
bool isPromiseRejection); |
|||
void didContinue(); |
|||
void didParseSource(std::unique_ptr<V8DebuggerScript>, bool success); |
|||
void willExecuteScript(int scriptId); |
|||
void didExecuteScript(); |
|||
|
|||
v8::Isolate* isolate() { return m_isolate; } |
|||
|
|||
private: |
|||
bool checkEnabled(ErrorString*); |
|||
void enable(); |
|||
|
|||
SkipPauseRequest shouldSkipExceptionPause(JavaScriptCallFrame* topCallFrame); |
|||
SkipPauseRequest shouldSkipStepPause(JavaScriptCallFrame* topCallFrame); |
|||
|
|||
void schedulePauseOnNextStatementIfSteppingInto(); |
|||
|
|||
std::unique_ptr<protocol::Array<protocol::Debugger::CallFrame>> |
|||
currentCallFrames(ErrorString*); |
|||
std::unique_ptr<protocol::Runtime::StackTrace> currentAsyncStackTrace(); |
|||
|
|||
void changeJavaScriptRecursionLevel(int step); |
|||
|
|||
void setPauseOnExceptionsImpl(ErrorString*, int); |
|||
|
|||
std::unique_ptr<protocol::Debugger::Location> resolveBreakpoint( |
|||
const String16& breakpointId, const String16& scriptId, |
|||
const ScriptBreakpoint&, BreakpointSource); |
|||
void removeBreakpoint(const String16& breakpointId); |
|||
bool assertPaused(ErrorString*); |
|||
void clearBreakDetails(); |
|||
|
|||
bool isCurrentCallStackEmptyOrBlackboxed(); |
|||
bool isTopPausedCallFrameBlackboxed(); |
|||
bool isCallFrameWithUnknownScriptOrBlackboxed(JavaScriptCallFrame*); |
|||
|
|||
void internalSetAsyncCallStackDepth(int); |
|||
void increaseCachedSkipStackGeneration(); |
|||
|
|||
bool setBlackboxPattern(ErrorString*, const String16& pattern); |
|||
|
|||
using ScriptsMap = |
|||
protocol::HashMap<String16, std::unique_ptr<V8DebuggerScript>>; |
|||
using BreakpointIdToDebuggerBreakpointIdsMap = |
|||
protocol::HashMap<String16, std::vector<String16>>; |
|||
using DebugServerBreakpointToBreakpointIdAndSourceMap = |
|||
protocol::HashMap<String16, std::pair<String16, BreakpointSource>>; |
|||
using MuteBreakpoins = protocol::HashMap<String16, std::pair<String16, int>>; |
|||
|
|||
enum DebuggerStep { NoStep = 0, StepInto, StepOver, StepOut }; |
|||
|
|||
V8InspectorImpl* m_inspector; |
|||
V8Debugger* m_debugger; |
|||
V8InspectorSessionImpl* m_session; |
|||
bool m_enabled; |
|||
protocol::DictionaryValue* m_state; |
|||
protocol::Debugger::Frontend m_frontend; |
|||
v8::Isolate* m_isolate; |
|||
v8::Global<v8::Context> m_pausedContext; |
|||
JavaScriptCallFrames m_pausedCallFrames; |
|||
ScriptsMap m_scripts; |
|||
BreakpointIdToDebuggerBreakpointIdsMap m_breakpointIdToDebuggerBreakpointIds; |
|||
DebugServerBreakpointToBreakpointIdAndSourceMap m_serverBreakpoints; |
|||
String16 m_continueToLocationBreakpointId; |
|||
String16 m_breakReason; |
|||
std::unique_ptr<protocol::DictionaryValue> m_breakAuxData; |
|||
DebuggerStep m_scheduledDebuggerStep; |
|||
bool m_skipNextDebuggerStepOut; |
|||
bool m_javaScriptPauseScheduled; |
|||
bool m_steppingFromFramework; |
|||
bool m_pausingOnNativeEvent; |
|||
|
|||
int m_skippedStepFrameCount; |
|||
int m_recursionLevelForStepOut; |
|||
int m_recursionLevelForStepFrame; |
|||
bool m_skipAllPauses; |
|||
|
|||
std::unique_ptr<V8Regex> m_blackboxPattern; |
|||
protocol::HashMap<String16, std::vector<std::pair<int, int>>> |
|||
m_blackboxedPositions; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(V8DebuggerAgentImpl); |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_V8DEBUGGERAGENTIMPL_H_
|
@ -0,0 +1,140 @@ |
|||
// Copyright 2014 the V8 project authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
|||
// found in the LICENSE file.
|
|||
|
|||
#include "src/inspector/v8-debugger-script.h" |
|||
|
|||
#include "src/inspector/protocol-platform.h" |
|||
#include "src/inspector/string-util.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
static const char hexDigits[17] = "0123456789ABCDEF"; |
|||
|
|||
static void appendUnsignedAsHex(uint64_t number, String16Builder* destination) { |
|||
for (size_t i = 0; i < 8; ++i) { |
|||
UChar c = hexDigits[number & 0xF]; |
|||
destination->append(c); |
|||
number >>= 4; |
|||
} |
|||
} |
|||
|
|||
// Hash algorithm for substrings is described in "Über die Komplexität der
|
|||
// Multiplikation in
|
|||
// eingeschränkten Branchingprogrammmodellen" by Woelfe.
|
|||
// http://opendatastructures.org/versions/edition-0.1d/ods-java/node33.html#SECTION00832000000000000000
|
|||
static String16 calculateHash(const String16& str) { |
|||
static uint64_t prime[] = {0x3FB75161, 0xAB1F4E4F, 0x82675BC5, 0xCD924D35, |
|||
0x81ABE279}; |
|||
static uint64_t random[] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, |
|||
0xC3D2E1F0}; |
|||
static uint32_t randomOdd[] = {0xB4663807, 0xCC322BF5, 0xD4F91BBD, 0xA7BEA11D, |
|||
0x8F462907}; |
|||
|
|||
uint64_t hashes[] = {0, 0, 0, 0, 0}; |
|||
uint64_t zi[] = {1, 1, 1, 1, 1}; |
|||
|
|||
const size_t hashesSize = arraysize(hashes); |
|||
|
|||
size_t current = 0; |
|||
const uint32_t* data = nullptr; |
|||
size_t sizeInBytes = sizeof(UChar) * str.length(); |
|||
data = reinterpret_cast<const uint32_t*>(str.characters16()); |
|||
for (size_t i = 0; i < sizeInBytes / 4; i += 4) { |
|||
uint32_t v = data[i]; |
|||
uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF; |
|||
hashes[current] = (hashes[current] + zi[current] * xi) % prime[current]; |
|||
zi[current] = (zi[current] * random[current]) % prime[current]; |
|||
current = current == hashesSize - 1 ? 0 : current + 1; |
|||
} |
|||
if (sizeInBytes % 4) { |
|||
uint32_t v = 0; |
|||
for (size_t i = sizeInBytes - sizeInBytes % 4; i < sizeInBytes; ++i) { |
|||
v <<= 8; |
|||
v |= reinterpret_cast<const uint8_t*>(data)[i]; |
|||
} |
|||
uint64_t xi = v * randomOdd[current] & 0x7FFFFFFF; |
|||
hashes[current] = (hashes[current] + zi[current] * xi) % prime[current]; |
|||
zi[current] = (zi[current] * random[current]) % prime[current]; |
|||
current = current == hashesSize - 1 ? 0 : current + 1; |
|||
} |
|||
|
|||
for (size_t i = 0; i < hashesSize; ++i) |
|||
hashes[i] = (hashes[i] + zi[i] * (prime[i] - 1)) % prime[i]; |
|||
|
|||
String16Builder hash; |
|||
for (size_t i = 0; i < hashesSize; ++i) appendUnsignedAsHex(hashes[i], &hash); |
|||
return hash.toString(); |
|||
} |
|||
|
|||
static v8::Local<v8::Value> GetChecked(v8::Local<v8::Context> context, |
|||
v8::Local<v8::Object> object, |
|||
const char* name) { |
|||
return object |
|||
->Get(context, toV8StringInternalized(context->GetIsolate(), name)) |
|||
.ToLocalChecked(); |
|||
} |
|||
|
|||
static int GetCheckedInt(v8::Local<v8::Context> context, |
|||
v8::Local<v8::Object> object, const char* name) { |
|||
return static_cast<int>(GetChecked(context, object, name) |
|||
->ToInteger(context) |
|||
.ToLocalChecked() |
|||
->Value()); |
|||
} |
|||
|
|||
V8DebuggerScript::V8DebuggerScript(v8::Local<v8::Context> context, |
|||
v8::Local<v8::Object> object, |
|||
bool isLiveEdit) { |
|||
v8::Isolate* isolate = context->GetIsolate(); |
|||
v8::Local<v8::Value> idValue = GetChecked(context, object, "id"); |
|||
DCHECK(!idValue.IsEmpty() && idValue->IsInt32()); |
|||
m_id = String16::fromInteger(idValue->Int32Value(context).FromJust()); |
|||
|
|||
m_url = toProtocolStringWithTypeCheck(GetChecked(context, object, "name")); |
|||
m_sourceURL = |
|||
toProtocolStringWithTypeCheck(GetChecked(context, object, "sourceURL")); |
|||
m_sourceMappingURL = toProtocolStringWithTypeCheck( |
|||
GetChecked(context, object, "sourceMappingURL")); |
|||
m_startLine = GetCheckedInt(context, object, "startLine"); |
|||
m_startColumn = GetCheckedInt(context, object, "startColumn"); |
|||
m_endLine = GetCheckedInt(context, object, "endLine"); |
|||
m_endColumn = GetCheckedInt(context, object, "endColumn"); |
|||
m_executionContextAuxData = toProtocolStringWithTypeCheck( |
|||
GetChecked(context, object, "executionContextAuxData")); |
|||
m_executionContextId = GetCheckedInt(context, object, "executionContextId"); |
|||
m_isLiveEdit = isLiveEdit; |
|||
|
|||
v8::Local<v8::Value> sourceValue; |
|||
if (!object->Get(context, toV8StringInternalized(isolate, "source")) |
|||
.ToLocal(&sourceValue) || |
|||
!sourceValue->IsString()) |
|||
return; |
|||
setSource(isolate, sourceValue.As<v8::String>()); |
|||
} |
|||
|
|||
V8DebuggerScript::~V8DebuggerScript() {} |
|||
|
|||
const String16& V8DebuggerScript::sourceURL() const { |
|||
return m_sourceURL.isEmpty() ? m_url : m_sourceURL; |
|||
} |
|||
|
|||
v8::Local<v8::String> V8DebuggerScript::source(v8::Isolate* isolate) const { |
|||
return m_source.Get(isolate); |
|||
} |
|||
|
|||
void V8DebuggerScript::setSourceURL(const String16& sourceURL) { |
|||
m_sourceURL = sourceURL; |
|||
} |
|||
|
|||
void V8DebuggerScript::setSourceMappingURL(const String16& sourceMappingURL) { |
|||
m_sourceMappingURL = sourceMappingURL; |
|||
} |
|||
|
|||
void V8DebuggerScript::setSource(v8::Isolate* isolate, |
|||
v8::Local<v8::String> source) { |
|||
m_source.Reset(isolate, source); |
|||
m_hash = calculateHash(toProtocolString(source)); |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
File diff suppressed because it is too large
@ -0,0 +1,160 @@ |
|||
// 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_V8DEBUGGER_H_ |
|||
#define V8_INSPECTOR_V8DEBUGGER_H_ |
|||
|
|||
#include <vector> |
|||
|
|||
#include "src/base/macros.h" |
|||
#include "src/inspector/java-script-call-frame.h" |
|||
#include "src/inspector/protocol/Forward.h" |
|||
#include "src/inspector/protocol/Runtime.h" |
|||
#include "src/inspector/v8-debugger-script.h" |
|||
|
|||
#include "include/v8-debug.h" |
|||
#include "include/v8-inspector.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
struct ScriptBreakpoint; |
|||
class V8DebuggerAgentImpl; |
|||
class V8InspectorImpl; |
|||
class V8StackTraceImpl; |
|||
|
|||
using protocol::ErrorString; |
|||
|
|||
class V8Debugger { |
|||
public: |
|||
V8Debugger(v8::Isolate*, V8InspectorImpl*); |
|||
~V8Debugger(); |
|||
|
|||
static int contextId(v8::Local<v8::Context>); |
|||
static int getGroupId(v8::Local<v8::Context>); |
|||
int markContext(const V8ContextInfo&); |
|||
|
|||
bool enabled() const; |
|||
|
|||
String16 setBreakpoint(const String16& sourceID, const ScriptBreakpoint&, |
|||
int* actualLineNumber, int* actualColumnNumber); |
|||
void removeBreakpoint(const String16& breakpointId); |
|||
void setBreakpointsActivated(bool); |
|||
bool breakpointsActivated() const { return m_breakpointsActivated; } |
|||
|
|||
enum PauseOnExceptionsState { |
|||
DontPauseOnExceptions, |
|||
PauseOnAllExceptions, |
|||
PauseOnUncaughtExceptions |
|||
}; |
|||
PauseOnExceptionsState getPauseOnExceptionsState(); |
|||
void setPauseOnExceptionsState(PauseOnExceptionsState); |
|||
void setPauseOnNextStatement(bool); |
|||
bool canBreakProgram(); |
|||
void breakProgram(); |
|||
void continueProgram(); |
|||
void stepIntoStatement(); |
|||
void stepOverStatement(); |
|||
void stepOutOfFunction(); |
|||
void clearStepping(); |
|||
|
|||
bool setScriptSource(const String16& sourceID, |
|||
v8::Local<v8::String> newSource, bool dryRun, |
|||
ErrorString*, |
|||
protocol::Maybe<protocol::Runtime::ExceptionDetails>*, |
|||
JavaScriptCallFrames* newCallFrames, |
|||
protocol::Maybe<bool>* stackChanged); |
|||
JavaScriptCallFrames currentCallFrames(int limit = 0); |
|||
|
|||
// Each script inherits debug data from v8::Context where it has been
|
|||
// compiled.
|
|||
// Only scripts whose debug data matches |contextGroupId| will be reported.
|
|||
// Passing 0 will result in reporting all scripts.
|
|||
void getCompiledScripts(int contextGroupId, |
|||
std::vector<std::unique_ptr<V8DebuggerScript>>&); |
|||
void enable(); |
|||
void disable(); |
|||
|
|||
bool isPaused(); |
|||
v8::Local<v8::Context> pausedContext() { return m_pausedContext; } |
|||
|
|||
int maxAsyncCallChainDepth() { return m_maxAsyncCallStackDepth; } |
|||
V8StackTraceImpl* currentAsyncCallChain(); |
|||
void setAsyncCallStackDepth(V8DebuggerAgentImpl*, int); |
|||
std::unique_ptr<V8StackTraceImpl> createStackTrace(v8::Local<v8::StackTrace>); |
|||
std::unique_ptr<V8StackTraceImpl> captureStackTrace(bool fullStack); |
|||
|
|||
v8::MaybeLocal<v8::Array> internalProperties(v8::Local<v8::Context>, |
|||
v8::Local<v8::Value>); |
|||
|
|||
void asyncTaskScheduled(const StringView& taskName, void* task, |
|||
bool recurring); |
|||
void asyncTaskScheduled(const String16& taskName, void* task, bool recurring); |
|||
void asyncTaskCanceled(void* task); |
|||
void asyncTaskStarted(void* task); |
|||
void asyncTaskFinished(void* task); |
|||
void allAsyncTasksCanceled(); |
|||
|
|||
void muteScriptParsedEvents(); |
|||
void unmuteScriptParsedEvents(); |
|||
|
|||
V8InspectorImpl* inspector() { return m_inspector; } |
|||
|
|||
private: |
|||
void compileDebuggerScript(); |
|||
v8::MaybeLocal<v8::Value> callDebuggerMethod(const char* functionName, |
|||
int argc, |
|||
v8::Local<v8::Value> argv[]); |
|||
v8::Local<v8::Context> debuggerContext() const; |
|||
void clearBreakpoints(); |
|||
|
|||
static void breakProgramCallback(const v8::FunctionCallbackInfo<v8::Value>&); |
|||
void handleProgramBreak(v8::Local<v8::Context> pausedContext, |
|||
v8::Local<v8::Object> executionState, |
|||
v8::Local<v8::Value> exception, |
|||
v8::Local<v8::Array> hitBreakpoints, |
|||
bool isPromiseRejection = false); |
|||
static void v8DebugEventCallback(const v8::Debug::EventDetails&); |
|||
v8::Local<v8::Value> callInternalGetterFunction(v8::Local<v8::Object>, |
|||
const char* functionName); |
|||
void handleV8DebugEvent(const v8::Debug::EventDetails&); |
|||
void handleV8AsyncTaskEvent(v8::Local<v8::Context>, |
|||
v8::Local<v8::Object> executionState, |
|||
v8::Local<v8::Object> eventData); |
|||
|
|||
v8::Local<v8::Value> collectionEntries(v8::Local<v8::Context>, |
|||
v8::Local<v8::Object>); |
|||
v8::Local<v8::Value> generatorObjectLocation(v8::Local<v8::Context>, |
|||
v8::Local<v8::Object>); |
|||
v8::Local<v8::Value> functionLocation(v8::Local<v8::Context>, |
|||
v8::Local<v8::Function>); |
|||
v8::MaybeLocal<v8::Value> functionScopes(v8::Local<v8::Context>, |
|||
v8::Local<v8::Function>); |
|||
|
|||
v8::Isolate* m_isolate; |
|||
V8InspectorImpl* m_inspector; |
|||
int m_lastContextId; |
|||
int m_enableCount; |
|||
bool m_breakpointsActivated; |
|||
v8::Global<v8::Object> m_debuggerScript; |
|||
v8::Global<v8::Context> m_debuggerContext; |
|||
v8::Local<v8::Object> m_executionState; |
|||
v8::Local<v8::Context> m_pausedContext; |
|||
bool m_runningNestedMessageLoop; |
|||
int m_ignoreScriptParsedEventsCounter; |
|||
|
|||
using AsyncTaskToStackTrace = |
|||
protocol::HashMap<void*, std::unique_ptr<V8StackTraceImpl>>; |
|||
AsyncTaskToStackTrace m_asyncTaskStacks; |
|||
protocol::HashSet<void*> m_recurringTasks; |
|||
int m_maxAsyncCallStackDepth; |
|||
std::vector<void*> m_currentTasks; |
|||
std::vector<std::unique_ptr<V8StackTraceImpl>> m_currentStacks; |
|||
protocol::HashMap<V8DebuggerAgentImpl*, int> m_maxAsyncCallStackDepthMap; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(V8Debugger); |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_V8DEBUGGER_H_
|
@ -0,0 +1,111 @@ |
|||
/*
|
|||
* 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/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::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 = V8Debugger::getGroupId(m_context); |
|||
if (contextGroupId) { |
|||
m_inspector->client()->muteMetrics(contextGroupId); |
|||
m_inspector->muteExceptions(contextGroupId); |
|||
} |
|||
v8::MicrotasksScope microtasksScope(m_context->GetIsolate(), |
|||
v8::MicrotasksScope::kDoNotRunMicrotasks); |
|||
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
|
@ -0,0 +1,407 @@ |
|||
// 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-heap-profiler-agent-impl.h" |
|||
|
|||
#include "src/inspector/injected-script.h" |
|||
#include "src/inspector/protocol/Protocol.h" |
|||
#include "src/inspector/string-util.h" |
|||
#include "src/inspector/v8-debugger.h" |
|||
#include "src/inspector/v8-inspector-impl.h" |
|||
#include "src/inspector/v8-inspector-session-impl.h" |
|||
|
|||
#include "include/v8-inspector.h" |
|||
#include "include/v8-profiler.h" |
|||
#include "include/v8-version.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
namespace { |
|||
|
|||
namespace HeapProfilerAgentState { |
|||
static const char heapProfilerEnabled[] = "heapProfilerEnabled"; |
|||
static const char heapObjectsTrackingEnabled[] = "heapObjectsTrackingEnabled"; |
|||
static const char allocationTrackingEnabled[] = "allocationTrackingEnabled"; |
|||
static const char samplingHeapProfilerEnabled[] = "samplingHeapProfilerEnabled"; |
|||
static const char samplingHeapProfilerInterval[] = |
|||
"samplingHeapProfilerInterval"; |
|||
} |
|||
|
|||
class HeapSnapshotProgress final : public v8::ActivityControl { |
|||
public: |
|||
explicit HeapSnapshotProgress(protocol::HeapProfiler::Frontend* frontend) |
|||
: m_frontend(frontend) {} |
|||
ControlOption ReportProgressValue(int done, int total) override { |
|||
m_frontend->reportHeapSnapshotProgress(done, total, |
|||
protocol::Maybe<bool>()); |
|||
if (done >= total) { |
|||
m_frontend->reportHeapSnapshotProgress(total, total, true); |
|||
} |
|||
m_frontend->flush(); |
|||
return kContinue; |
|||
} |
|||
|
|||
private: |
|||
protocol::HeapProfiler::Frontend* m_frontend; |
|||
}; |
|||
|
|||
class GlobalObjectNameResolver final |
|||
: public v8::HeapProfiler::ObjectNameResolver { |
|||
public: |
|||
explicit GlobalObjectNameResolver(V8InspectorSessionImpl* session) |
|||
: m_offset(0), m_strings(10000), m_session(session) {} |
|||
|
|||
const char* GetName(v8::Local<v8::Object> object) override { |
|||
InspectedContext* context = m_session->inspector()->getContext( |
|||
m_session->contextGroupId(), |
|||
V8Debugger::contextId(object->CreationContext())); |
|||
if (!context) return ""; |
|||
String16 name = context->origin(); |
|||
size_t length = name.length(); |
|||
if (m_offset + length + 1 >= m_strings.size()) return ""; |
|||
for (size_t i = 0; i < length; ++i) { |
|||
UChar ch = name[i]; |
|||
m_strings[m_offset + i] = ch > 0xff ? '?' : static_cast<char>(ch); |
|||
} |
|||
m_strings[m_offset + length] = '\0'; |
|||
char* result = &*m_strings.begin() + m_offset; |
|||
m_offset += length + 1; |
|||
return result; |
|||
} |
|||
|
|||
private: |
|||
size_t m_offset; |
|||
std::vector<char> m_strings; |
|||
V8InspectorSessionImpl* m_session; |
|||
}; |
|||
|
|||
class HeapSnapshotOutputStream final : public v8::OutputStream { |
|||
public: |
|||
explicit HeapSnapshotOutputStream(protocol::HeapProfiler::Frontend* frontend) |
|||
: m_frontend(frontend) {} |
|||
void EndOfStream() override {} |
|||
int GetChunkSize() override { return 102400; } |
|||
WriteResult WriteAsciiChunk(char* data, int size) override { |
|||
m_frontend->addHeapSnapshotChunk(String16(data, size)); |
|||
m_frontend->flush(); |
|||
return kContinue; |
|||
} |
|||
|
|||
private: |
|||
protocol::HeapProfiler::Frontend* m_frontend; |
|||
}; |
|||
|
|||
v8::Local<v8::Object> objectByHeapObjectId(v8::Isolate* isolate, int id) { |
|||
v8::HeapProfiler* profiler = isolate->GetHeapProfiler(); |
|||
v8::Local<v8::Value> value = profiler->FindObjectById(id); |
|||
if (value.IsEmpty() || !value->IsObject()) return v8::Local<v8::Object>(); |
|||
return value.As<v8::Object>(); |
|||
} |
|||
|
|||
class InspectableHeapObject final : public V8InspectorSession::Inspectable { |
|||
public: |
|||
explicit InspectableHeapObject(int heapObjectId) |
|||
: m_heapObjectId(heapObjectId) {} |
|||
v8::Local<v8::Value> get(v8::Local<v8::Context> context) override { |
|||
return objectByHeapObjectId(context->GetIsolate(), m_heapObjectId); |
|||
} |
|||
|
|||
private: |
|||
int m_heapObjectId; |
|||
}; |
|||
|
|||
class HeapStatsStream final : public v8::OutputStream { |
|||
public: |
|||
explicit HeapStatsStream(protocol::HeapProfiler::Frontend* frontend) |
|||
: m_frontend(frontend) {} |
|||
|
|||
void EndOfStream() override {} |
|||
|
|||
WriteResult WriteAsciiChunk(char* data, int size) override { |
|||
DCHECK(false); |
|||
return kAbort; |
|||
} |
|||
|
|||
WriteResult WriteHeapStatsChunk(v8::HeapStatsUpdate* updateData, |
|||
int count) override { |
|||
DCHECK_GT(count, 0); |
|||
std::unique_ptr<protocol::Array<int>> statsDiff = |
|||
protocol::Array<int>::create(); |
|||
for (int i = 0; i < count; ++i) { |
|||
statsDiff->addItem(updateData[i].index); |
|||
statsDiff->addItem(updateData[i].count); |
|||
statsDiff->addItem(updateData[i].size); |
|||
} |
|||
m_frontend->heapStatsUpdate(std::move(statsDiff)); |
|||
return kContinue; |
|||
} |
|||
|
|||
private: |
|||
protocol::HeapProfiler::Frontend* m_frontend; |
|||
}; |
|||
|
|||
} // namespace
|
|||
|
|||
V8HeapProfilerAgentImpl::V8HeapProfilerAgentImpl( |
|||
V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel, |
|||
protocol::DictionaryValue* state) |
|||
: m_session(session), |
|||
m_isolate(session->inspector()->isolate()), |
|||
m_frontend(frontendChannel), |
|||
m_state(state), |
|||
m_hasTimer(false) {} |
|||
|
|||
V8HeapProfilerAgentImpl::~V8HeapProfilerAgentImpl() {} |
|||
|
|||
void V8HeapProfilerAgentImpl::restore() { |
|||
if (m_state->booleanProperty(HeapProfilerAgentState::heapProfilerEnabled, |
|||
false)) |
|||
m_frontend.resetProfiles(); |
|||
if (m_state->booleanProperty( |
|||
HeapProfilerAgentState::heapObjectsTrackingEnabled, false)) |
|||
startTrackingHeapObjectsInternal(m_state->booleanProperty( |
|||
HeapProfilerAgentState::allocationTrackingEnabled, false)); |
|||
if (m_state->booleanProperty( |
|||
HeapProfilerAgentState::samplingHeapProfilerEnabled, false)) { |
|||
ErrorString error; |
|||
double samplingInterval = m_state->doubleProperty( |
|||
HeapProfilerAgentState::samplingHeapProfilerInterval, -1); |
|||
DCHECK_GE(samplingInterval, 0); |
|||
startSampling(&error, Maybe<double>(samplingInterval)); |
|||
} |
|||
} |
|||
|
|||
void V8HeapProfilerAgentImpl::collectGarbage(ErrorString*) { |
|||
m_isolate->LowMemoryNotification(); |
|||
} |
|||
|
|||
void V8HeapProfilerAgentImpl::startTrackingHeapObjects( |
|||
ErrorString*, const protocol::Maybe<bool>& trackAllocations) { |
|||
m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled, true); |
|||
bool allocationTrackingEnabled = trackAllocations.fromMaybe(false); |
|||
m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled, |
|||
allocationTrackingEnabled); |
|||
startTrackingHeapObjectsInternal(allocationTrackingEnabled); |
|||
} |
|||
|
|||
void V8HeapProfilerAgentImpl::stopTrackingHeapObjects( |
|||
ErrorString* error, const protocol::Maybe<bool>& reportProgress) { |
|||
requestHeapStatsUpdate(); |
|||
takeHeapSnapshot(error, reportProgress); |
|||
stopTrackingHeapObjectsInternal(); |
|||
} |
|||
|
|||
void V8HeapProfilerAgentImpl::enable(ErrorString*) { |
|||
m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, true); |
|||
} |
|||
|
|||
void V8HeapProfilerAgentImpl::disable(ErrorString* error) { |
|||
stopTrackingHeapObjectsInternal(); |
|||
if (m_state->booleanProperty( |
|||
HeapProfilerAgentState::samplingHeapProfilerEnabled, false)) { |
|||
v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); |
|||
if (profiler) profiler->StopSamplingHeapProfiler(); |
|||
} |
|||
m_isolate->GetHeapProfiler()->ClearObjectIds(); |
|||
m_state->setBoolean(HeapProfilerAgentState::heapProfilerEnabled, false); |
|||
} |
|||
|
|||
void V8HeapProfilerAgentImpl::takeHeapSnapshot( |
|||
ErrorString* errorString, const protocol::Maybe<bool>& reportProgress) { |
|||
v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); |
|||
if (!profiler) { |
|||
*errorString = "Cannot access v8 heap profiler"; |
|||
return; |
|||
} |
|||
std::unique_ptr<HeapSnapshotProgress> progress; |
|||
if (reportProgress.fromMaybe(false)) |
|||
progress = wrapUnique(new HeapSnapshotProgress(&m_frontend)); |
|||
|
|||
GlobalObjectNameResolver resolver(m_session); |
|||
const v8::HeapSnapshot* snapshot = |
|||
profiler->TakeHeapSnapshot(progress.get(), &resolver); |
|||
if (!snapshot) { |
|||
*errorString = "Failed to take heap snapshot"; |
|||
return; |
|||
} |
|||
HeapSnapshotOutputStream stream(&m_frontend); |
|||
snapshot->Serialize(&stream); |
|||
const_cast<v8::HeapSnapshot*>(snapshot)->Delete(); |
|||
} |
|||
|
|||
void V8HeapProfilerAgentImpl::getObjectByHeapObjectId( |
|||
ErrorString* error, const String16& heapSnapshotObjectId, |
|||
const protocol::Maybe<String16>& objectGroup, |
|||
std::unique_ptr<protocol::Runtime::RemoteObject>* result) { |
|||
bool ok; |
|||
int id = heapSnapshotObjectId.toInteger(&ok); |
|||
if (!ok) { |
|||
*error = "Invalid heap snapshot object id"; |
|||
return; |
|||
} |
|||
|
|||
v8::HandleScope handles(m_isolate); |
|||
v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id); |
|||
if (heapObject.IsEmpty()) { |
|||
*error = "Object is not available"; |
|||
return; |
|||
} |
|||
|
|||
if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject)) { |
|||
*error = "Object is not available"; |
|||
return; |
|||
} |
|||
|
|||
*result = m_session->wrapObject(heapObject->CreationContext(), heapObject, |
|||
objectGroup.fromMaybe(""), false); |
|||
if (!result) *error = "Object is not available"; |
|||
} |
|||
|
|||
void V8HeapProfilerAgentImpl::addInspectedHeapObject( |
|||
ErrorString* errorString, const String16& inspectedHeapObjectId) { |
|||
bool ok; |
|||
int id = inspectedHeapObjectId.toInteger(&ok); |
|||
if (!ok) { |
|||
*errorString = "Invalid heap snapshot object id"; |
|||
return; |
|||
} |
|||
|
|||
v8::HandleScope handles(m_isolate); |
|||
v8::Local<v8::Object> heapObject = objectByHeapObjectId(m_isolate, id); |
|||
if (heapObject.IsEmpty()) { |
|||
*errorString = "Object is not available"; |
|||
return; |
|||
} |
|||
|
|||
if (!m_session->inspector()->client()->isInspectableHeapObject(heapObject)) { |
|||
*errorString = "Object is not available"; |
|||
return; |
|||
} |
|||
|
|||
m_session->addInspectedObject(wrapUnique(new InspectableHeapObject(id))); |
|||
} |
|||
|
|||
void V8HeapProfilerAgentImpl::getHeapObjectId(ErrorString* errorString, |
|||
const String16& objectId, |
|||
String16* heapSnapshotObjectId) { |
|||
v8::HandleScope handles(m_isolate); |
|||
v8::Local<v8::Value> value; |
|||
v8::Local<v8::Context> context; |
|||
if (!m_session->unwrapObject(errorString, objectId, &value, &context, |
|||
nullptr) || |
|||
value->IsUndefined()) |
|||
return; |
|||
|
|||
v8::SnapshotObjectId id = m_isolate->GetHeapProfiler()->GetObjectId(value); |
|||
*heapSnapshotObjectId = String16::fromInteger(static_cast<size_t>(id)); |
|||
} |
|||
|
|||
void V8HeapProfilerAgentImpl::requestHeapStatsUpdate() { |
|||
HeapStatsStream stream(&m_frontend); |
|||
v8::SnapshotObjectId lastSeenObjectId = |
|||
m_isolate->GetHeapProfiler()->GetHeapStats(&stream); |
|||
m_frontend.lastSeenObjectId( |
|||
lastSeenObjectId, m_session->inspector()->client()->currentTimeMS()); |
|||
} |
|||
|
|||
// static
|
|||
void V8HeapProfilerAgentImpl::onTimer(void* data) { |
|||
reinterpret_cast<V8HeapProfilerAgentImpl*>(data)->requestHeapStatsUpdate(); |
|||
} |
|||
|
|||
void V8HeapProfilerAgentImpl::startTrackingHeapObjectsInternal( |
|||
bool trackAllocations) { |
|||
m_isolate->GetHeapProfiler()->StartTrackingHeapObjects(trackAllocations); |
|||
if (!m_hasTimer) { |
|||
m_hasTimer = true; |
|||
m_session->inspector()->client()->startRepeatingTimer( |
|||
0.05, &V8HeapProfilerAgentImpl::onTimer, reinterpret_cast<void*>(this)); |
|||
} |
|||
} |
|||
|
|||
void V8HeapProfilerAgentImpl::stopTrackingHeapObjectsInternal() { |
|||
if (m_hasTimer) { |
|||
m_session->inspector()->client()->cancelTimer( |
|||
reinterpret_cast<void*>(this)); |
|||
m_hasTimer = false; |
|||
} |
|||
m_isolate->GetHeapProfiler()->StopTrackingHeapObjects(); |
|||
m_state->setBoolean(HeapProfilerAgentState::heapObjectsTrackingEnabled, |
|||
false); |
|||
m_state->setBoolean(HeapProfilerAgentState::allocationTrackingEnabled, false); |
|||
} |
|||
|
|||
void V8HeapProfilerAgentImpl::startSampling( |
|||
ErrorString* errorString, const Maybe<double>& samplingInterval) { |
|||
v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); |
|||
if (!profiler) { |
|||
*errorString = "Cannot access v8 heap profiler"; |
|||
return; |
|||
} |
|||
const unsigned defaultSamplingInterval = 1 << 15; |
|||
double samplingIntervalValue = |
|||
samplingInterval.fromMaybe(defaultSamplingInterval); |
|||
m_state->setDouble(HeapProfilerAgentState::samplingHeapProfilerInterval, |
|||
samplingIntervalValue); |
|||
m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled, |
|||
true); |
|||
profiler->StartSamplingHeapProfiler( |
|||
static_cast<uint64_t>(samplingIntervalValue), 128, |
|||
v8::HeapProfiler::kSamplingForceGC); |
|||
} |
|||
|
|||
namespace { |
|||
std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode> |
|||
buildSampingHeapProfileNode(const v8::AllocationProfile::Node* node) { |
|||
auto children = protocol::Array< |
|||
protocol::HeapProfiler::SamplingHeapProfileNode>::create(); |
|||
for (const auto* child : node->children) |
|||
children->addItem(buildSampingHeapProfileNode(child)); |
|||
size_t selfSize = 0; |
|||
for (const auto& allocation : node->allocations) |
|||
selfSize += allocation.size * allocation.count; |
|||
std::unique_ptr<protocol::Runtime::CallFrame> callFrame = |
|||
protocol::Runtime::CallFrame::create() |
|||
.setFunctionName(toProtocolString(node->name)) |
|||
.setScriptId(String16::fromInteger(node->script_id)) |
|||
.setUrl(toProtocolString(node->script_name)) |
|||
.setLineNumber(node->line_number - 1) |
|||
.setColumnNumber(node->column_number - 1) |
|||
.build(); |
|||
std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfileNode> result = |
|||
protocol::HeapProfiler::SamplingHeapProfileNode::create() |
|||
.setCallFrame(std::move(callFrame)) |
|||
.setSelfSize(selfSize) |
|||
.setChildren(std::move(children)) |
|||
.build(); |
|||
return result; |
|||
} |
|||
} // namespace
|
|||
|
|||
void V8HeapProfilerAgentImpl::stopSampling( |
|||
ErrorString* errorString, |
|||
std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>* profile) { |
|||
v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); |
|||
if (!profiler) { |
|||
*errorString = "Cannot access v8 heap profiler"; |
|||
return; |
|||
} |
|||
v8::HandleScope scope( |
|||
m_isolate); // Allocation profile contains Local handles.
|
|||
std::unique_ptr<v8::AllocationProfile> v8Profile( |
|||
profiler->GetAllocationProfile()); |
|||
profiler->StopSamplingHeapProfiler(); |
|||
m_state->setBoolean(HeapProfilerAgentState::samplingHeapProfilerEnabled, |
|||
false); |
|||
if (!v8Profile) { |
|||
*errorString = "Cannot access v8 sampled heap profile."; |
|||
return; |
|||
} |
|||
v8::AllocationProfile::Node* root = v8Profile->GetRootNode(); |
|||
*profile = protocol::HeapProfiler::SamplingHeapProfile::create() |
|||
.setHead(buildSampingHeapProfileNode(root)) |
|||
.build(); |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,73 @@ |
|||
// 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_V8HEAPPROFILERAGENTIMPL_H_ |
|||
#define V8_INSPECTOR_V8HEAPPROFILERAGENTIMPL_H_ |
|||
|
|||
#include "src/base/macros.h" |
|||
#include "src/inspector/protocol/Forward.h" |
|||
#include "src/inspector/protocol/HeapProfiler.h" |
|||
|
|||
#include "include/v8.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
class V8InspectorSessionImpl; |
|||
|
|||
using protocol::ErrorString; |
|||
using protocol::Maybe; |
|||
|
|||
class V8HeapProfilerAgentImpl : public protocol::HeapProfiler::Backend { |
|||
public: |
|||
V8HeapProfilerAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*, |
|||
protocol::DictionaryValue* state); |
|||
~V8HeapProfilerAgentImpl() override; |
|||
void restore(); |
|||
|
|||
void collectGarbage(ErrorString*) override; |
|||
|
|||
void enable(ErrorString*) override; |
|||
void startTrackingHeapObjects(ErrorString*, |
|||
const Maybe<bool>& trackAllocations) override; |
|||
void stopTrackingHeapObjects(ErrorString*, |
|||
const Maybe<bool>& reportProgress) override; |
|||
|
|||
void disable(ErrorString*) override; |
|||
|
|||
void takeHeapSnapshot(ErrorString*, |
|||
const Maybe<bool>& reportProgress) override; |
|||
|
|||
void getObjectByHeapObjectId( |
|||
ErrorString*, const String16& heapSnapshotObjectId, |
|||
const Maybe<String16>& objectGroup, |
|||
std::unique_ptr<protocol::Runtime::RemoteObject>* result) override; |
|||
void addInspectedHeapObject(ErrorString*, |
|||
const String16& inspectedHeapObjectId) override; |
|||
void getHeapObjectId(ErrorString*, const String16& objectId, |
|||
String16* heapSnapshotObjectId) override; |
|||
|
|||
void startSampling(ErrorString*, |
|||
const Maybe<double>& samplingInterval) override; |
|||
void stopSampling( |
|||
ErrorString*, |
|||
std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>*) override; |
|||
|
|||
private: |
|||
void startTrackingHeapObjectsInternal(bool trackAllocations); |
|||
void stopTrackingHeapObjectsInternal(); |
|||
void requestHeapStatsUpdate(); |
|||
static void onTimer(void*); |
|||
|
|||
V8InspectorSessionImpl* m_session; |
|||
v8::Isolate* m_isolate; |
|||
protocol::HeapProfiler::Frontend m_frontend; |
|||
protocol::DictionaryValue* m_state; |
|||
bool m_hasTimer; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(V8HeapProfilerAgentImpl); |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_V8HEAPPROFILERAGENTIMPL_H_
|
@ -0,0 +1,216 @@ |
|||
// 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/inspector/injected-script-native.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-copier.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; |
|||
} |
|||
|
|||
} // 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, "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); |
|||
return injectedScriptHost; |
|||
} |
|||
|
|||
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() || value->IsWeakMap()) { |
|||
info.GetReturnValue().Set(toV8StringInternalized(isolate, "map")); |
|||
return; |
|||
} |
|||
if (value->IsSet() || value->IsWeakSet()) { |
|||
info.GetReturnValue().Set(toV8StringInternalized(isolate, "set")); |
|||
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; |
|||
} |
|||
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; |
|||
v8::Local<v8::Array> properties; |
|||
if (unwrapInspector(info) |
|||
->debugger() |
|||
->internalProperties(info.GetIsolate()->GetCurrentContext(), info[0]) |
|||
.ToLocal(&properties)) |
|||
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; |
|||
InjectedScriptNative* injectedScriptNative = |
|||
InjectedScriptNative::fromInjectedScriptHost(info.GetIsolate(), |
|||
info.Holder()); |
|||
if (!injectedScriptNative) return; |
|||
|
|||
v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); |
|||
v8::Local<v8::String> v8groupName = |
|||
info[1]->ToString(context).ToLocalChecked(); |
|||
String16 groupName = toProtocolStringWithTypeCheck(v8groupName); |
|||
int id = injectedScriptNative->bind(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::Object> target = info[0].As<v8::Proxy>(); |
|||
while (target->IsProxy()) |
|||
target = v8::Local<v8::Proxy>::Cast(target)->GetTarget(); |
|||
info.GetReturnValue().Set(target); |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,46 @@ |
|||
// 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_V8INJECTEDSCRIPTHOST_H_ |
|||
#define V8_INSPECTOR_V8INJECTEDSCRIPTHOST_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 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>&); |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_V8INJECTEDSCRIPTHOST_H_
|
@ -0,0 +1,376 @@ |
|||
/*
|
|||
* Copyright (c) 2010-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. |
|||
*/ |
|||
|
|||
#include "src/inspector/v8-inspector-impl.h" |
|||
|
|||
#include "src/inspector/inspected-context.h" |
|||
#include "src/inspector/string-util.h" |
|||
#include "src/inspector/v8-console-agent-impl.h" |
|||
#include "src/inspector/v8-console-message.h" |
|||
#include "src/inspector/v8-debugger-agent-impl.h" |
|||
#include "src/inspector/v8-debugger.h" |
|||
#include "src/inspector/v8-inspector-session-impl.h" |
|||
#include "src/inspector/v8-profiler-agent-impl.h" |
|||
#include "src/inspector/v8-runtime-agent-impl.h" |
|||
#include "src/inspector/v8-stack-trace-impl.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
std::unique_ptr<V8Inspector> V8Inspector::create(v8::Isolate* isolate, |
|||
V8InspectorClient* client) { |
|||
return wrapUnique(new V8InspectorImpl(isolate, client)); |
|||
} |
|||
|
|||
V8InspectorImpl::V8InspectorImpl(v8::Isolate* isolate, |
|||
V8InspectorClient* client) |
|||
: m_isolate(isolate), |
|||
m_client(client), |
|||
m_debugger(new V8Debugger(isolate, this)), |
|||
m_capturingStackTracesCount(0), |
|||
m_lastExceptionId(0) {} |
|||
|
|||
V8InspectorImpl::~V8InspectorImpl() {} |
|||
|
|||
V8DebuggerAgentImpl* V8InspectorImpl::enabledDebuggerAgentForGroup( |
|||
int contextGroupId) { |
|||
V8InspectorSessionImpl* session = sessionForContextGroup(contextGroupId); |
|||
V8DebuggerAgentImpl* agent = session ? session->debuggerAgent() : nullptr; |
|||
return agent && agent->enabled() ? agent : nullptr; |
|||
} |
|||
|
|||
V8RuntimeAgentImpl* V8InspectorImpl::enabledRuntimeAgentForGroup( |
|||
int contextGroupId) { |
|||
V8InspectorSessionImpl* session = sessionForContextGroup(contextGroupId); |
|||
V8RuntimeAgentImpl* agent = session ? session->runtimeAgent() : nullptr; |
|||
return agent && agent->enabled() ? agent : nullptr; |
|||
} |
|||
|
|||
V8ProfilerAgentImpl* V8InspectorImpl::enabledProfilerAgentForGroup( |
|||
int contextGroupId) { |
|||
V8InspectorSessionImpl* session = sessionForContextGroup(contextGroupId); |
|||
V8ProfilerAgentImpl* agent = session ? session->profilerAgent() : nullptr; |
|||
return agent && agent->enabled() ? agent : nullptr; |
|||
} |
|||
|
|||
v8::MaybeLocal<v8::Value> V8InspectorImpl::runCompiledScript( |
|||
v8::Local<v8::Context> context, v8::Local<v8::Script> script) { |
|||
v8::MicrotasksScope microtasksScope(m_isolate, |
|||
v8::MicrotasksScope::kRunMicrotasks); |
|||
int groupId = V8Debugger::getGroupId(context); |
|||
if (V8DebuggerAgentImpl* agent = enabledDebuggerAgentForGroup(groupId)) |
|||
agent->willExecuteScript(script->GetUnboundScript()->GetId()); |
|||
v8::MaybeLocal<v8::Value> result = script->Run(context); |
|||
// Get agent from the map again, since it could have detached during script
|
|||
// execution.
|
|||
if (V8DebuggerAgentImpl* agent = enabledDebuggerAgentForGroup(groupId)) |
|||
agent->didExecuteScript(); |
|||
return result; |
|||
} |
|||
|
|||
v8::MaybeLocal<v8::Value> V8InspectorImpl::callFunction( |
|||
v8::Local<v8::Function> function, v8::Local<v8::Context> context, |
|||
v8::Local<v8::Value> receiver, int argc, v8::Local<v8::Value> info[]) { |
|||
v8::MicrotasksScope microtasksScope(m_isolate, |
|||
v8::MicrotasksScope::kRunMicrotasks); |
|||
int groupId = V8Debugger::getGroupId(context); |
|||
if (V8DebuggerAgentImpl* agent = enabledDebuggerAgentForGroup(groupId)) |
|||
agent->willExecuteScript(function->ScriptId()); |
|||
v8::MaybeLocal<v8::Value> result = |
|||
function->Call(context, receiver, argc, info); |
|||
// Get agent from the map again, since it could have detached during script
|
|||
// execution.
|
|||
if (V8DebuggerAgentImpl* agent = enabledDebuggerAgentForGroup(groupId)) |
|||
agent->didExecuteScript(); |
|||
return result; |
|||
} |
|||
|
|||
v8::MaybeLocal<v8::Value> V8InspectorImpl::compileAndRunInternalScript( |
|||
v8::Local<v8::Context> context, v8::Local<v8::String> source) { |
|||
v8::Local<v8::Script> script = |
|||
compileScript(context, source, String16(), true); |
|||
if (script.IsEmpty()) return v8::MaybeLocal<v8::Value>(); |
|||
v8::MicrotasksScope microtasksScope(m_isolate, |
|||
v8::MicrotasksScope::kDoNotRunMicrotasks); |
|||
return script->Run(context); |
|||
} |
|||
|
|||
v8::Local<v8::Script> V8InspectorImpl::compileScript( |
|||
v8::Local<v8::Context> context, v8::Local<v8::String> code, |
|||
const String16& fileName, bool markAsInternal) { |
|||
v8::ScriptOrigin origin( |
|||
toV8String(m_isolate, fileName), v8::Integer::New(m_isolate, 0), |
|||
v8::Integer::New(m_isolate, 0), |
|||
v8::False(m_isolate), // sharable
|
|||
v8::Local<v8::Integer>(), |
|||
v8::Boolean::New(m_isolate, markAsInternal), // internal
|
|||
toV8String(m_isolate, String16()), // sourceMap
|
|||
v8::True(m_isolate)); // opaqueresource
|
|||
v8::ScriptCompiler::Source source(code, origin); |
|||
v8::Local<v8::Script> script; |
|||
if (!v8::ScriptCompiler::Compile(context, &source, |
|||
v8::ScriptCompiler::kNoCompileOptions) |
|||
.ToLocal(&script)) |
|||
return v8::Local<v8::Script>(); |
|||
return script; |
|||
} |
|||
|
|||
void V8InspectorImpl::enableStackCapturingIfNeeded() { |
|||
if (!m_capturingStackTracesCount) |
|||
V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(m_isolate, |
|||
true); |
|||
++m_capturingStackTracesCount; |
|||
} |
|||
|
|||
void V8InspectorImpl::disableStackCapturingIfNeeded() { |
|||
if (!(--m_capturingStackTracesCount)) |
|||
V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions(m_isolate, |
|||
false); |
|||
} |
|||
|
|||
void V8InspectorImpl::muteExceptions(int contextGroupId) { |
|||
m_muteExceptionsMap[contextGroupId]++; |
|||
} |
|||
|
|||
void V8InspectorImpl::unmuteExceptions(int contextGroupId) { |
|||
m_muteExceptionsMap[contextGroupId]--; |
|||
} |
|||
|
|||
V8ConsoleMessageStorage* V8InspectorImpl::ensureConsoleMessageStorage( |
|||
int contextGroupId) { |
|||
ConsoleStorageMap::iterator storageIt = |
|||
m_consoleStorageMap.find(contextGroupId); |
|||
if (storageIt == m_consoleStorageMap.end()) |
|||
storageIt = |
|||
m_consoleStorageMap |
|||
.insert(std::make_pair( |
|||
contextGroupId, |
|||
wrapUnique(new V8ConsoleMessageStorage(this, contextGroupId)))) |
|||
.first; |
|||
return storageIt->second.get(); |
|||
} |
|||
|
|||
bool V8InspectorImpl::hasConsoleMessageStorage(int contextGroupId) { |
|||
ConsoleStorageMap::iterator storageIt = |
|||
m_consoleStorageMap.find(contextGroupId); |
|||
return storageIt != m_consoleStorageMap.end(); |
|||
} |
|||
|
|||
std::unique_ptr<V8StackTrace> V8InspectorImpl::createStackTrace( |
|||
v8::Local<v8::StackTrace> stackTrace) { |
|||
return m_debugger->createStackTrace(stackTrace); |
|||
} |
|||
|
|||
std::unique_ptr<V8InspectorSession> V8InspectorImpl::connect( |
|||
int contextGroupId, V8Inspector::Channel* channel, |
|||
const StringView& state) { |
|||
DCHECK(m_sessions.find(contextGroupId) == m_sessions.cend()); |
|||
std::unique_ptr<V8InspectorSessionImpl> session = |
|||
V8InspectorSessionImpl::create(this, contextGroupId, channel, state); |
|||
m_sessions[contextGroupId] = session.get(); |
|||
return std::move(session); |
|||
} |
|||
|
|||
void V8InspectorImpl::disconnect(V8InspectorSessionImpl* session) { |
|||
DCHECK(m_sessions.find(session->contextGroupId()) != m_sessions.end()); |
|||
m_sessions.erase(session->contextGroupId()); |
|||
} |
|||
|
|||
InspectedContext* V8InspectorImpl::getContext(int groupId, |
|||
int contextId) const { |
|||
if (!groupId || !contextId) return nullptr; |
|||
|
|||
ContextsByGroupMap::const_iterator contextGroupIt = m_contexts.find(groupId); |
|||
if (contextGroupIt == m_contexts.end()) return nullptr; |
|||
|
|||
ContextByIdMap::iterator contextIt = contextGroupIt->second->find(contextId); |
|||
if (contextIt == contextGroupIt->second->end()) return nullptr; |
|||
|
|||
return contextIt->second.get(); |
|||
} |
|||
|
|||
void V8InspectorImpl::contextCreated(const V8ContextInfo& info) { |
|||
int contextId = m_debugger->markContext(info); |
|||
|
|||
ContextsByGroupMap::iterator contextIt = m_contexts.find(info.contextGroupId); |
|||
if (contextIt == m_contexts.end()) |
|||
contextIt = m_contexts |
|||
.insert(std::make_pair(info.contextGroupId, |
|||
wrapUnique(new ContextByIdMap()))) |
|||
.first; |
|||
|
|||
const auto& contextById = contextIt->second; |
|||
|
|||
DCHECK(contextById->find(contextId) == contextById->cend()); |
|||
InspectedContext* context = new InspectedContext(this, info, contextId); |
|||
(*contextById)[contextId] = wrapUnique(context); |
|||
SessionMap::iterator sessionIt = m_sessions.find(info.contextGroupId); |
|||
if (sessionIt != m_sessions.end()) |
|||
sessionIt->second->runtimeAgent()->reportExecutionContextCreated(context); |
|||
} |
|||
|
|||
void V8InspectorImpl::contextDestroyed(v8::Local<v8::Context> context) { |
|||
int contextId = V8Debugger::contextId(context); |
|||
int contextGroupId = V8Debugger::getGroupId(context); |
|||
|
|||
ConsoleStorageMap::iterator storageIt = |
|||
m_consoleStorageMap.find(contextGroupId); |
|||
if (storageIt != m_consoleStorageMap.end()) |
|||
storageIt->second->contextDestroyed(contextId); |
|||
|
|||
InspectedContext* inspectedContext = getContext(contextGroupId, contextId); |
|||
if (!inspectedContext) return; |
|||
|
|||
SessionMap::iterator iter = m_sessions.find(contextGroupId); |
|||
if (iter != m_sessions.end()) |
|||
iter->second->runtimeAgent()->reportExecutionContextDestroyed( |
|||
inspectedContext); |
|||
discardInspectedContext(contextGroupId, contextId); |
|||
} |
|||
|
|||
void V8InspectorImpl::resetContextGroup(int contextGroupId) { |
|||
m_consoleStorageMap.erase(contextGroupId); |
|||
m_muteExceptionsMap.erase(contextGroupId); |
|||
SessionMap::iterator session = m_sessions.find(contextGroupId); |
|||
if (session != m_sessions.end()) session->second->reset(); |
|||
m_contexts.erase(contextGroupId); |
|||
} |
|||
|
|||
void V8InspectorImpl::willExecuteScript(v8::Local<v8::Context> context, |
|||
int scriptId) { |
|||
if (V8DebuggerAgentImpl* agent = |
|||
enabledDebuggerAgentForGroup(V8Debugger::getGroupId(context))) |
|||
agent->willExecuteScript(scriptId); |
|||
} |
|||
|
|||
void V8InspectorImpl::didExecuteScript(v8::Local<v8::Context> context) { |
|||
if (V8DebuggerAgentImpl* agent = |
|||
enabledDebuggerAgentForGroup(V8Debugger::getGroupId(context))) |
|||
agent->didExecuteScript(); |
|||
} |
|||
|
|||
void V8InspectorImpl::idleStarted() { |
|||
for (auto it = m_sessions.begin(); it != m_sessions.end(); ++it) { |
|||
if (it->second->profilerAgent()->idleStarted()) return; |
|||
} |
|||
} |
|||
|
|||
void V8InspectorImpl::idleFinished() { |
|||
for (auto it = m_sessions.begin(); it != m_sessions.end(); ++it) { |
|||
if (it->second->profilerAgent()->idleFinished()) return; |
|||
} |
|||
} |
|||
|
|||
unsigned V8InspectorImpl::exceptionThrown( |
|||
v8::Local<v8::Context> context, const StringView& message, |
|||
v8::Local<v8::Value> exception, const StringView& detailedMessage, |
|||
const StringView& url, unsigned lineNumber, unsigned columnNumber, |
|||
std::unique_ptr<V8StackTrace> stackTrace, int scriptId) { |
|||
int contextGroupId = V8Debugger::getGroupId(context); |
|||
if (!contextGroupId || m_muteExceptionsMap[contextGroupId]) return 0; |
|||
std::unique_ptr<V8StackTraceImpl> stackTraceImpl = |
|||
wrapUnique(static_cast<V8StackTraceImpl*>(stackTrace.release())); |
|||
unsigned exceptionId = nextExceptionId(); |
|||
std::unique_ptr<V8ConsoleMessage> consoleMessage = |
|||
V8ConsoleMessage::createForException( |
|||
m_client->currentTimeMS(), toString16(detailedMessage), |
|||
toString16(url), lineNumber, columnNumber, std::move(stackTraceImpl), |
|||
scriptId, m_isolate, toString16(message), |
|||
V8Debugger::contextId(context), exception, exceptionId); |
|||
ensureConsoleMessageStorage(contextGroupId) |
|||
->addMessage(std::move(consoleMessage)); |
|||
return exceptionId; |
|||
} |
|||
|
|||
void V8InspectorImpl::exceptionRevoked(v8::Local<v8::Context> context, |
|||
unsigned exceptionId, |
|||
const StringView& message) { |
|||
int contextGroupId = V8Debugger::getGroupId(context); |
|||
if (!contextGroupId) return; |
|||
|
|||
std::unique_ptr<V8ConsoleMessage> consoleMessage = |
|||
V8ConsoleMessage::createForRevokedException( |
|||
m_client->currentTimeMS(), toString16(message), exceptionId); |
|||
ensureConsoleMessageStorage(contextGroupId) |
|||
->addMessage(std::move(consoleMessage)); |
|||
} |
|||
|
|||
std::unique_ptr<V8StackTrace> V8InspectorImpl::captureStackTrace( |
|||
bool fullStack) { |
|||
return m_debugger->captureStackTrace(fullStack); |
|||
} |
|||
|
|||
void V8InspectorImpl::asyncTaskScheduled(const StringView& taskName, void* task, |
|||
bool recurring) { |
|||
m_debugger->asyncTaskScheduled(taskName, task, recurring); |
|||
} |
|||
|
|||
void V8InspectorImpl::asyncTaskCanceled(void* task) { |
|||
m_debugger->asyncTaskCanceled(task); |
|||
} |
|||
|
|||
void V8InspectorImpl::asyncTaskStarted(void* task) { |
|||
m_debugger->asyncTaskStarted(task); |
|||
} |
|||
|
|||
void V8InspectorImpl::asyncTaskFinished(void* task) { |
|||
m_debugger->asyncTaskFinished(task); |
|||
} |
|||
|
|||
void V8InspectorImpl::allAsyncTasksCanceled() { |
|||
m_debugger->allAsyncTasksCanceled(); |
|||
} |
|||
|
|||
v8::Local<v8::Context> V8InspectorImpl::regexContext() { |
|||
if (m_regexContext.IsEmpty()) |
|||
m_regexContext.Reset(m_isolate, v8::Context::New(m_isolate)); |
|||
return m_regexContext.Get(m_isolate); |
|||
} |
|||
|
|||
void V8InspectorImpl::discardInspectedContext(int contextGroupId, |
|||
int contextId) { |
|||
if (!getContext(contextGroupId, contextId)) return; |
|||
m_contexts[contextGroupId]->erase(contextId); |
|||
if (m_contexts[contextGroupId]->empty()) m_contexts.erase(contextGroupId); |
|||
} |
|||
|
|||
const V8InspectorImpl::ContextByIdMap* V8InspectorImpl::contextGroup( |
|||
int contextGroupId) { |
|||
ContextsByGroupMap::iterator iter = m_contexts.find(contextGroupId); |
|||
return iter == m_contexts.end() ? nullptr : iter->second.get(); |
|||
} |
|||
|
|||
V8InspectorSessionImpl* V8InspectorImpl::sessionForContextGroup( |
|||
int contextGroupId) { |
|||
if (!contextGroupId) return nullptr; |
|||
SessionMap::iterator iter = m_sessions.find(contextGroupId); |
|||
return iter == m_sessions.end() ? nullptr : iter->second; |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,150 @@ |
|||
/*
|
|||
* Copyright (c) 2010, 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_V8INSPECTORIMPL_H_ |
|||
#define V8_INSPECTOR_V8INSPECTORIMPL_H_ |
|||
|
|||
#include <vector> |
|||
|
|||
#include "src/base/macros.h" |
|||
#include "src/inspector/protocol/Protocol.h" |
|||
|
|||
#include "include/v8-debug.h" |
|||
#include "include/v8-inspector.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
class InspectedContext; |
|||
class V8ConsoleMessageStorage; |
|||
class V8Debugger; |
|||
class V8DebuggerAgentImpl; |
|||
class V8InspectorSessionImpl; |
|||
class V8ProfilerAgentImpl; |
|||
class V8RuntimeAgentImpl; |
|||
class V8StackTraceImpl; |
|||
|
|||
class V8InspectorImpl : public V8Inspector { |
|||
public: |
|||
V8InspectorImpl(v8::Isolate*, V8InspectorClient*); |
|||
~V8InspectorImpl() override; |
|||
|
|||
v8::Isolate* isolate() const { return m_isolate; } |
|||
V8InspectorClient* client() { return m_client; } |
|||
V8Debugger* debugger() { return m_debugger.get(); } |
|||
|
|||
v8::MaybeLocal<v8::Value> runCompiledScript(v8::Local<v8::Context>, |
|||
v8::Local<v8::Script>); |
|||
v8::MaybeLocal<v8::Value> callFunction(v8::Local<v8::Function>, |
|||
v8::Local<v8::Context>, |
|||
v8::Local<v8::Value> receiver, |
|||
int argc, v8::Local<v8::Value> info[]); |
|||
v8::MaybeLocal<v8::Value> compileAndRunInternalScript(v8::Local<v8::Context>, |
|||
v8::Local<v8::String>); |
|||
v8::Local<v8::Script> compileScript(v8::Local<v8::Context>, |
|||
v8::Local<v8::String>, |
|||
const String16& fileName, |
|||
bool markAsInternal); |
|||
v8::Local<v8::Context> regexContext(); |
|||
|
|||
// V8Inspector implementation.
|
|||
std::unique_ptr<V8InspectorSession> connect(int contextGroupId, |
|||
V8Inspector::Channel*, |
|||
const StringView& state) override; |
|||
void contextCreated(const V8ContextInfo&) override; |
|||
void contextDestroyed(v8::Local<v8::Context>) override; |
|||
void resetContextGroup(int contextGroupId) override; |
|||
void willExecuteScript(v8::Local<v8::Context>, int scriptId) override; |
|||
void didExecuteScript(v8::Local<v8::Context>) override; |
|||
void idleStarted() override; |
|||
void idleFinished() override; |
|||
unsigned exceptionThrown(v8::Local<v8::Context>, const StringView& message, |
|||
v8::Local<v8::Value> exception, |
|||
const StringView& detailedMessage, |
|||
const StringView& url, unsigned lineNumber, |
|||
unsigned columnNumber, std::unique_ptr<V8StackTrace>, |
|||
int scriptId) override; |
|||
void exceptionRevoked(v8::Local<v8::Context>, unsigned exceptionId, |
|||
const StringView& message) override; |
|||
std::unique_ptr<V8StackTrace> createStackTrace( |
|||
v8::Local<v8::StackTrace>) override; |
|||
std::unique_ptr<V8StackTrace> captureStackTrace(bool fullStack) override; |
|||
void asyncTaskScheduled(const StringView& taskName, void* task, |
|||
bool recurring) override; |
|||
void asyncTaskCanceled(void* task) override; |
|||
void asyncTaskStarted(void* task) override; |
|||
void asyncTaskFinished(void* task) override; |
|||
void allAsyncTasksCanceled() override; |
|||
|
|||
unsigned nextExceptionId() { return ++m_lastExceptionId; } |
|||
void enableStackCapturingIfNeeded(); |
|||
void disableStackCapturingIfNeeded(); |
|||
void muteExceptions(int contextGroupId); |
|||
void unmuteExceptions(int contextGroupId); |
|||
V8ConsoleMessageStorage* ensureConsoleMessageStorage(int contextGroupId); |
|||
bool hasConsoleMessageStorage(int contextGroupId); |
|||
using ContextByIdMap = |
|||
protocol::HashMap<int, std::unique_ptr<InspectedContext>>; |
|||
void discardInspectedContext(int contextGroupId, int contextId); |
|||
const ContextByIdMap* contextGroup(int contextGroupId); |
|||
void disconnect(V8InspectorSessionImpl*); |
|||
V8InspectorSessionImpl* sessionForContextGroup(int contextGroupId); |
|||
InspectedContext* getContext(int groupId, int contextId) const; |
|||
V8DebuggerAgentImpl* enabledDebuggerAgentForGroup(int contextGroupId); |
|||
V8RuntimeAgentImpl* enabledRuntimeAgentForGroup(int contextGroupId); |
|||
V8ProfilerAgentImpl* enabledProfilerAgentForGroup(int contextGroupId); |
|||
|
|||
private: |
|||
v8::Isolate* m_isolate; |
|||
V8InspectorClient* m_client; |
|||
std::unique_ptr<V8Debugger> m_debugger; |
|||
v8::Global<v8::Context> m_regexContext; |
|||
int m_capturingStackTracesCount; |
|||
unsigned m_lastExceptionId; |
|||
|
|||
using MuteExceptionsMap = protocol::HashMap<int, int>; |
|||
MuteExceptionsMap m_muteExceptionsMap; |
|||
|
|||
using ContextsByGroupMap = |
|||
protocol::HashMap<int, std::unique_ptr<ContextByIdMap>>; |
|||
ContextsByGroupMap m_contexts; |
|||
|
|||
using SessionMap = protocol::HashMap<int, V8InspectorSessionImpl*>; |
|||
SessionMap m_sessions; |
|||
|
|||
using ConsoleStorageMap = |
|||
protocol::HashMap<int, std::unique_ptr<V8ConsoleMessageStorage>>; |
|||
ConsoleStorageMap m_consoleStorageMap; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(V8InspectorImpl); |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_V8INSPECTORIMPL_H_
|
@ -0,0 +1,418 @@ |
|||
// 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-inspector-session-impl.h" |
|||
|
|||
#include "src/inspector/injected-script.h" |
|||
#include "src/inspector/inspected-context.h" |
|||
#include "src/inspector/protocol/Protocol.h" |
|||
#include "src/inspector/remote-object-id.h" |
|||
#include "src/inspector/search-util.h" |
|||
#include "src/inspector/string-util.h" |
|||
#include "src/inspector/v8-console-agent-impl.h" |
|||
#include "src/inspector/v8-debugger-agent-impl.h" |
|||
#include "src/inspector/v8-debugger.h" |
|||
#include "src/inspector/v8-heap-profiler-agent-impl.h" |
|||
#include "src/inspector/v8-inspector-impl.h" |
|||
#include "src/inspector/v8-profiler-agent-impl.h" |
|||
#include "src/inspector/v8-runtime-agent-impl.h" |
|||
#include "src/inspector/v8-schema-agent-impl.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
// static
|
|||
bool V8InspectorSession::canDispatchMethod(const StringView& method) { |
|||
return stringViewStartsWith(method, |
|||
protocol::Runtime::Metainfo::commandPrefix) || |
|||
stringViewStartsWith(method, |
|||
protocol::Debugger::Metainfo::commandPrefix) || |
|||
stringViewStartsWith(method, |
|||
protocol::Profiler::Metainfo::commandPrefix) || |
|||
stringViewStartsWith( |
|||
method, protocol::HeapProfiler::Metainfo::commandPrefix) || |
|||
stringViewStartsWith(method, |
|||
protocol::Console::Metainfo::commandPrefix) || |
|||
stringViewStartsWith(method, |
|||
protocol::Schema::Metainfo::commandPrefix); |
|||
} |
|||
|
|||
std::unique_ptr<V8InspectorSessionImpl> V8InspectorSessionImpl::create( |
|||
V8InspectorImpl* inspector, int contextGroupId, |
|||
V8Inspector::Channel* channel, const StringView& state) { |
|||
return wrapUnique( |
|||
new V8InspectorSessionImpl(inspector, contextGroupId, channel, state)); |
|||
} |
|||
|
|||
V8InspectorSessionImpl::V8InspectorSessionImpl(V8InspectorImpl* inspector, |
|||
int contextGroupId, |
|||
V8Inspector::Channel* channel, |
|||
const StringView& savedState) |
|||
: m_contextGroupId(contextGroupId), |
|||
m_inspector(inspector), |
|||
m_channel(channel), |
|||
m_customObjectFormatterEnabled(false), |
|||
m_dispatcher(this), |
|||
m_state(nullptr), |
|||
m_runtimeAgent(nullptr), |
|||
m_debuggerAgent(nullptr), |
|||
m_heapProfilerAgent(nullptr), |
|||
m_profilerAgent(nullptr), |
|||
m_consoleAgent(nullptr), |
|||
m_schemaAgent(nullptr) { |
|||
if (savedState.length()) { |
|||
std::unique_ptr<protocol::Value> state = |
|||
protocol::parseJSON(toString16(savedState)); |
|||
if (state) m_state = protocol::DictionaryValue::cast(std::move(state)); |
|||
if (!m_state) m_state = protocol::DictionaryValue::create(); |
|||
} else { |
|||
m_state = protocol::DictionaryValue::create(); |
|||
} |
|||
|
|||
m_runtimeAgent = wrapUnique(new V8RuntimeAgentImpl( |
|||
this, this, agentState(protocol::Runtime::Metainfo::domainName))); |
|||
protocol::Runtime::Dispatcher::wire(&m_dispatcher, m_runtimeAgent.get()); |
|||
|
|||
m_debuggerAgent = wrapUnique(new V8DebuggerAgentImpl( |
|||
this, this, agentState(protocol::Debugger::Metainfo::domainName))); |
|||
protocol::Debugger::Dispatcher::wire(&m_dispatcher, m_debuggerAgent.get()); |
|||
|
|||
m_profilerAgent = wrapUnique(new V8ProfilerAgentImpl( |
|||
this, this, agentState(protocol::Profiler::Metainfo::domainName))); |
|||
protocol::Profiler::Dispatcher::wire(&m_dispatcher, m_profilerAgent.get()); |
|||
|
|||
m_heapProfilerAgent = wrapUnique(new V8HeapProfilerAgentImpl( |
|||
this, this, agentState(protocol::HeapProfiler::Metainfo::domainName))); |
|||
protocol::HeapProfiler::Dispatcher::wire(&m_dispatcher, |
|||
m_heapProfilerAgent.get()); |
|||
|
|||
m_consoleAgent = wrapUnique(new V8ConsoleAgentImpl( |
|||
this, this, agentState(protocol::Console::Metainfo::domainName))); |
|||
protocol::Console::Dispatcher::wire(&m_dispatcher, m_consoleAgent.get()); |
|||
|
|||
m_schemaAgent = wrapUnique(new V8SchemaAgentImpl( |
|||
this, this, agentState(protocol::Schema::Metainfo::domainName))); |
|||
protocol::Schema::Dispatcher::wire(&m_dispatcher, m_schemaAgent.get()); |
|||
|
|||
if (savedState.length()) { |
|||
m_runtimeAgent->restore(); |
|||
m_debuggerAgent->restore(); |
|||
m_heapProfilerAgent->restore(); |
|||
m_profilerAgent->restore(); |
|||
m_consoleAgent->restore(); |
|||
} |
|||
} |
|||
|
|||
V8InspectorSessionImpl::~V8InspectorSessionImpl() { |
|||
ErrorString errorString; |
|||
m_consoleAgent->disable(&errorString); |
|||
m_profilerAgent->disable(&errorString); |
|||
m_heapProfilerAgent->disable(&errorString); |
|||
m_debuggerAgent->disable(&errorString); |
|||
m_runtimeAgent->disable(&errorString); |
|||
|
|||
discardInjectedScripts(); |
|||
m_inspector->disconnect(this); |
|||
} |
|||
|
|||
protocol::DictionaryValue* V8InspectorSessionImpl::agentState( |
|||
const String16& name) { |
|||
protocol::DictionaryValue* state = m_state->getObject(name); |
|||
if (!state) { |
|||
std::unique_ptr<protocol::DictionaryValue> newState = |
|||
protocol::DictionaryValue::create(); |
|||
state = newState.get(); |
|||
m_state->setObject(name, std::move(newState)); |
|||
} |
|||
return state; |
|||
} |
|||
|
|||
void V8InspectorSessionImpl::sendProtocolResponse(int callId, |
|||
const String16& message) { |
|||
m_channel->sendProtocolResponse(callId, toStringView(message)); |
|||
} |
|||
|
|||
void V8InspectorSessionImpl::sendProtocolNotification(const String16& message) { |
|||
m_channel->sendProtocolNotification(toStringView(message)); |
|||
} |
|||
|
|||
void V8InspectorSessionImpl::flushProtocolNotifications() { |
|||
m_channel->flushProtocolNotifications(); |
|||
} |
|||
|
|||
void V8InspectorSessionImpl::reset() { |
|||
m_debuggerAgent->reset(); |
|||
m_runtimeAgent->reset(); |
|||
discardInjectedScripts(); |
|||
} |
|||
|
|||
void V8InspectorSessionImpl::discardInjectedScripts() { |
|||
m_inspectedObjects.clear(); |
|||
const V8InspectorImpl::ContextByIdMap* contexts = |
|||
m_inspector->contextGroup(m_contextGroupId); |
|||
if (!contexts) return; |
|||
|
|||
std::vector<int> keys; |
|||
keys.reserve(contexts->size()); |
|||
for (auto& idContext : *contexts) keys.push_back(idContext.first); |
|||
for (auto& key : keys) { |
|||
contexts = m_inspector->contextGroup(m_contextGroupId); |
|||
if (!contexts) continue; |
|||
auto contextIt = contexts->find(key); |
|||
if (contextIt != contexts->end()) |
|||
contextIt->second |
|||
->discardInjectedScript(); // This may destroy some contexts.
|
|||
} |
|||
} |
|||
|
|||
InjectedScript* V8InspectorSessionImpl::findInjectedScript( |
|||
ErrorString* errorString, int contextId) { |
|||
if (!contextId) { |
|||
*errorString = "Cannot find context with specified id"; |
|||
return nullptr; |
|||
} |
|||
|
|||
const V8InspectorImpl::ContextByIdMap* contexts = |
|||
m_inspector->contextGroup(m_contextGroupId); |
|||
if (!contexts) { |
|||
*errorString = "Cannot find context with specified id"; |
|||
return nullptr; |
|||
} |
|||
|
|||
auto contextsIt = contexts->find(contextId); |
|||
if (contextsIt == contexts->end()) { |
|||
*errorString = "Cannot find context with specified id"; |
|||
return nullptr; |
|||
} |
|||
|
|||
const std::unique_ptr<InspectedContext>& context = contextsIt->second; |
|||
if (!context->getInjectedScript()) { |
|||
context->createInjectedScript(); |
|||
if (!context->getInjectedScript()) { |
|||
*errorString = "Cannot access specified execution context"; |
|||
return nullptr; |
|||
} |
|||
if (m_customObjectFormatterEnabled) |
|||
context->getInjectedScript()->setCustomObjectFormatterEnabled(true); |
|||
} |
|||
return context->getInjectedScript(); |
|||
} |
|||
|
|||
InjectedScript* V8InspectorSessionImpl::findInjectedScript( |
|||
ErrorString* errorString, RemoteObjectIdBase* objectId) { |
|||
return objectId ? findInjectedScript(errorString, objectId->contextId()) |
|||
: nullptr; |
|||
} |
|||
|
|||
void V8InspectorSessionImpl::releaseObjectGroup(const StringView& objectGroup) { |
|||
releaseObjectGroup(toString16(objectGroup)); |
|||
} |
|||
|
|||
void V8InspectorSessionImpl::releaseObjectGroup(const String16& objectGroup) { |
|||
const V8InspectorImpl::ContextByIdMap* contexts = |
|||
m_inspector->contextGroup(m_contextGroupId); |
|||
if (!contexts) return; |
|||
|
|||
std::vector<int> keys; |
|||
for (auto& idContext : *contexts) keys.push_back(idContext.first); |
|||
for (auto& key : keys) { |
|||
contexts = m_inspector->contextGroup(m_contextGroupId); |
|||
if (!contexts) continue; |
|||
auto contextsIt = contexts->find(key); |
|||
if (contextsIt == contexts->end()) continue; |
|||
InjectedScript* injectedScript = contextsIt->second->getInjectedScript(); |
|||
if (injectedScript) |
|||
injectedScript->releaseObjectGroup( |
|||
objectGroup); // This may destroy some contexts.
|
|||
} |
|||
} |
|||
|
|||
bool V8InspectorSessionImpl::unwrapObject( |
|||
std::unique_ptr<StringBuffer>* error, const StringView& objectId, |
|||
v8::Local<v8::Value>* object, v8::Local<v8::Context>* context, |
|||
std::unique_ptr<StringBuffer>* objectGroup) { |
|||
ErrorString errorString; |
|||
String16 objectGroupString; |
|||
bool result = |
|||
unwrapObject(&errorString, toString16(objectId), object, context, |
|||
objectGroup ? &objectGroupString : nullptr); |
|||
if (error) *error = StringBufferImpl::adopt(errorString); |
|||
if (objectGroup) *objectGroup = StringBufferImpl::adopt(objectGroupString); |
|||
return result; |
|||
} |
|||
|
|||
bool V8InspectorSessionImpl::unwrapObject(ErrorString* errorString, |
|||
const String16& objectId, |
|||
v8::Local<v8::Value>* object, |
|||
v8::Local<v8::Context>* context, |
|||
String16* objectGroup) { |
|||
std::unique_ptr<RemoteObjectId> remoteId = |
|||
RemoteObjectId::parse(errorString, objectId); |
|||
if (!remoteId) return false; |
|||
InjectedScript* injectedScript = |
|||
findInjectedScript(errorString, remoteId.get()); |
|||
if (!injectedScript) return false; |
|||
if (!injectedScript->findObject(errorString, *remoteId, object)) return false; |
|||
*context = injectedScript->context()->context(); |
|||
if (objectGroup) *objectGroup = injectedScript->objectGroupName(*remoteId); |
|||
return true; |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Runtime::API::RemoteObject> |
|||
V8InspectorSessionImpl::wrapObject(v8::Local<v8::Context> context, |
|||
v8::Local<v8::Value> value, |
|||
const StringView& groupName) { |
|||
return wrapObject(context, value, toString16(groupName), false); |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Runtime::RemoteObject> |
|||
V8InspectorSessionImpl::wrapObject(v8::Local<v8::Context> context, |
|||
v8::Local<v8::Value> value, |
|||
const String16& groupName, |
|||
bool generatePreview) { |
|||
ErrorString errorString; |
|||
InjectedScript* injectedScript = |
|||
findInjectedScript(&errorString, V8Debugger::contextId(context)); |
|||
if (!injectedScript) return nullptr; |
|||
return injectedScript->wrapObject(&errorString, value, groupName, false, |
|||
generatePreview); |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Runtime::RemoteObject> |
|||
V8InspectorSessionImpl::wrapTable(v8::Local<v8::Context> context, |
|||
v8::Local<v8::Value> table, |
|||
v8::Local<v8::Value> columns) { |
|||
ErrorString errorString; |
|||
InjectedScript* injectedScript = |
|||
findInjectedScript(&errorString, V8Debugger::contextId(context)); |
|||
if (!injectedScript) return nullptr; |
|||
return injectedScript->wrapTable(table, columns); |
|||
} |
|||
|
|||
void V8InspectorSessionImpl::setCustomObjectFormatterEnabled(bool enabled) { |
|||
m_customObjectFormatterEnabled = enabled; |
|||
const V8InspectorImpl::ContextByIdMap* contexts = |
|||
m_inspector->contextGroup(m_contextGroupId); |
|||
if (!contexts) return; |
|||
for (auto& idContext : *contexts) { |
|||
InjectedScript* injectedScript = idContext.second->getInjectedScript(); |
|||
if (injectedScript) |
|||
injectedScript->setCustomObjectFormatterEnabled(enabled); |
|||
} |
|||
} |
|||
|
|||
void V8InspectorSessionImpl::reportAllContexts(V8RuntimeAgentImpl* agent) { |
|||
const V8InspectorImpl::ContextByIdMap* contexts = |
|||
m_inspector->contextGroup(m_contextGroupId); |
|||
if (!contexts) return; |
|||
for (auto& idContext : *contexts) |
|||
agent->reportExecutionContextCreated(idContext.second.get()); |
|||
} |
|||
|
|||
void V8InspectorSessionImpl::dispatchProtocolMessage( |
|||
const StringView& message) { |
|||
m_dispatcher.dispatch(protocol::parseJSON(message)); |
|||
} |
|||
|
|||
std::unique_ptr<StringBuffer> V8InspectorSessionImpl::stateJSON() { |
|||
String16 json = m_state->toJSONString(); |
|||
return StringBufferImpl::adopt(json); |
|||
} |
|||
|
|||
std::vector<std::unique_ptr<protocol::Schema::API::Domain>> |
|||
V8InspectorSessionImpl::supportedDomains() { |
|||
std::vector<std::unique_ptr<protocol::Schema::Domain>> domains = |
|||
supportedDomainsImpl(); |
|||
std::vector<std::unique_ptr<protocol::Schema::API::Domain>> result; |
|||
for (size_t i = 0; i < domains.size(); ++i) |
|||
result.push_back(std::move(domains[i])); |
|||
return result; |
|||
} |
|||
|
|||
std::vector<std::unique_ptr<protocol::Schema::Domain>> |
|||
V8InspectorSessionImpl::supportedDomainsImpl() { |
|||
std::vector<std::unique_ptr<protocol::Schema::Domain>> result; |
|||
result.push_back(protocol::Schema::Domain::create() |
|||
.setName(protocol::Runtime::Metainfo::domainName) |
|||
.setVersion(protocol::Runtime::Metainfo::version) |
|||
.build()); |
|||
result.push_back(protocol::Schema::Domain::create() |
|||
.setName(protocol::Debugger::Metainfo::domainName) |
|||
.setVersion(protocol::Debugger::Metainfo::version) |
|||
.build()); |
|||
result.push_back(protocol::Schema::Domain::create() |
|||
.setName(protocol::Profiler::Metainfo::domainName) |
|||
.setVersion(protocol::Profiler::Metainfo::version) |
|||
.build()); |
|||
result.push_back(protocol::Schema::Domain::create() |
|||
.setName(protocol::HeapProfiler::Metainfo::domainName) |
|||
.setVersion(protocol::HeapProfiler::Metainfo::version) |
|||
.build()); |
|||
result.push_back(protocol::Schema::Domain::create() |
|||
.setName(protocol::Schema::Metainfo::domainName) |
|||
.setVersion(protocol::Schema::Metainfo::version) |
|||
.build()); |
|||
return result; |
|||
} |
|||
|
|||
void V8InspectorSessionImpl::addInspectedObject( |
|||
std::unique_ptr<V8InspectorSession::Inspectable> inspectable) { |
|||
m_inspectedObjects.insert(m_inspectedObjects.begin(), std::move(inspectable)); |
|||
if (m_inspectedObjects.size() > kInspectedObjectBufferSize) |
|||
m_inspectedObjects.resize(kInspectedObjectBufferSize); |
|||
} |
|||
|
|||
V8InspectorSession::Inspectable* V8InspectorSessionImpl::inspectedObject( |
|||
unsigned num) { |
|||
if (num >= m_inspectedObjects.size()) return nullptr; |
|||
return m_inspectedObjects[num].get(); |
|||
} |
|||
|
|||
void V8InspectorSessionImpl::schedulePauseOnNextStatement( |
|||
const StringView& breakReason, const StringView& breakDetails) { |
|||
m_debuggerAgent->schedulePauseOnNextStatement( |
|||
toString16(breakReason), |
|||
protocol::DictionaryValue::cast(protocol::parseJSON(breakDetails))); |
|||
} |
|||
|
|||
void V8InspectorSessionImpl::cancelPauseOnNextStatement() { |
|||
m_debuggerAgent->cancelPauseOnNextStatement(); |
|||
} |
|||
|
|||
void V8InspectorSessionImpl::breakProgram(const StringView& breakReason, |
|||
const StringView& breakDetails) { |
|||
m_debuggerAgent->breakProgram( |
|||
toString16(breakReason), |
|||
protocol::DictionaryValue::cast(protocol::parseJSON(breakDetails))); |
|||
} |
|||
|
|||
void V8InspectorSessionImpl::setSkipAllPauses(bool skip) { |
|||
ErrorString errorString; |
|||
m_debuggerAgent->setSkipAllPauses(&errorString, skip); |
|||
} |
|||
|
|||
void V8InspectorSessionImpl::resume() { |
|||
ErrorString errorString; |
|||
m_debuggerAgent->resume(&errorString); |
|||
} |
|||
|
|||
void V8InspectorSessionImpl::stepOver() { |
|||
ErrorString errorString; |
|||
m_debuggerAgent->stepOver(&errorString); |
|||
} |
|||
|
|||
std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>> |
|||
V8InspectorSessionImpl::searchInTextByLines(const StringView& text, |
|||
const StringView& query, |
|||
bool caseSensitive, bool isRegex) { |
|||
// TODO(dgozman): search may operate on StringView and avoid copying |text|.
|
|||
std::vector<std::unique_ptr<protocol::Debugger::SearchMatch>> matches = |
|||
searchInTextByLinesImpl(this, toString16(text), toString16(query), |
|||
caseSensitive, isRegex); |
|||
std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>> result; |
|||
for (size_t i = 0; i < matches.size(); ++i) |
|||
result.push_back(std::move(matches[i])); |
|||
return result; |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,126 @@ |
|||
// 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_V8INSPECTORSESSIONIMPL_H_ |
|||
#define V8_INSPECTOR_V8INSPECTORSESSIONIMPL_H_ |
|||
|
|||
#include <vector> |
|||
|
|||
#include "src/base/macros.h" |
|||
#include "src/inspector/protocol/Forward.h" |
|||
#include "src/inspector/protocol/Runtime.h" |
|||
#include "src/inspector/protocol/Schema.h" |
|||
|
|||
#include "include/v8-inspector.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
class InjectedScript; |
|||
class RemoteObjectIdBase; |
|||
class V8ConsoleAgentImpl; |
|||
class V8DebuggerAgentImpl; |
|||
class V8InspectorImpl; |
|||
class V8HeapProfilerAgentImpl; |
|||
class V8ProfilerAgentImpl; |
|||
class V8RuntimeAgentImpl; |
|||
class V8SchemaAgentImpl; |
|||
|
|||
using protocol::ErrorString; |
|||
|
|||
class V8InspectorSessionImpl : public V8InspectorSession, |
|||
public protocol::FrontendChannel { |
|||
public: |
|||
static std::unique_ptr<V8InspectorSessionImpl> create( |
|||
V8InspectorImpl*, int contextGroupId, V8Inspector::Channel*, |
|||
const StringView& state); |
|||
~V8InspectorSessionImpl(); |
|||
|
|||
V8InspectorImpl* inspector() const { return m_inspector; } |
|||
V8ConsoleAgentImpl* consoleAgent() { return m_consoleAgent.get(); } |
|||
V8DebuggerAgentImpl* debuggerAgent() { return m_debuggerAgent.get(); } |
|||
V8SchemaAgentImpl* schemaAgent() { return m_schemaAgent.get(); } |
|||
V8ProfilerAgentImpl* profilerAgent() { return m_profilerAgent.get(); } |
|||
V8RuntimeAgentImpl* runtimeAgent() { return m_runtimeAgent.get(); } |
|||
int contextGroupId() const { return m_contextGroupId; } |
|||
|
|||
InjectedScript* findInjectedScript(ErrorString*, int contextId); |
|||
InjectedScript* findInjectedScript(ErrorString*, RemoteObjectIdBase*); |
|||
void reset(); |
|||
void discardInjectedScripts(); |
|||
void reportAllContexts(V8RuntimeAgentImpl*); |
|||
void setCustomObjectFormatterEnabled(bool); |
|||
std::unique_ptr<protocol::Runtime::RemoteObject> wrapObject( |
|||
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); |
|||
std::vector<std::unique_ptr<protocol::Schema::Domain>> supportedDomainsImpl(); |
|||
bool unwrapObject(ErrorString*, const String16& objectId, |
|||
v8::Local<v8::Value>*, v8::Local<v8::Context>*, |
|||
String16* objectGroup); |
|||
void releaseObjectGroup(const String16& objectGroup); |
|||
|
|||
// V8InspectorSession implementation.
|
|||
void dispatchProtocolMessage(const StringView& message) override; |
|||
std::unique_ptr<StringBuffer> stateJSON() override; |
|||
std::vector<std::unique_ptr<protocol::Schema::API::Domain>> supportedDomains() |
|||
override; |
|||
void addInspectedObject( |
|||
std::unique_ptr<V8InspectorSession::Inspectable>) override; |
|||
void schedulePauseOnNextStatement(const StringView& breakReason, |
|||
const StringView& breakDetails) override; |
|||
void cancelPauseOnNextStatement() override; |
|||
void breakProgram(const StringView& breakReason, |
|||
const StringView& breakDetails) override; |
|||
void setSkipAllPauses(bool) override; |
|||
void resume() override; |
|||
void stepOver() override; |
|||
std::vector<std::unique_ptr<protocol::Debugger::API::SearchMatch>> |
|||
searchInTextByLines(const StringView& text, const StringView& query, |
|||
bool caseSensitive, bool isRegex) override; |
|||
void releaseObjectGroup(const StringView& objectGroup) override; |
|||
bool unwrapObject(std::unique_ptr<StringBuffer>*, const StringView& objectId, |
|||
v8::Local<v8::Value>*, v8::Local<v8::Context>*, |
|||
std::unique_ptr<StringBuffer>* objectGroup) override; |
|||
std::unique_ptr<protocol::Runtime::API::RemoteObject> wrapObject( |
|||
v8::Local<v8::Context>, v8::Local<v8::Value>, |
|||
const StringView& groupName) override; |
|||
|
|||
V8InspectorSession::Inspectable* inspectedObject(unsigned num); |
|||
static const unsigned kInspectedObjectBufferSize = 5; |
|||
|
|||
private: |
|||
V8InspectorSessionImpl(V8InspectorImpl*, int contextGroupId, |
|||
V8Inspector::Channel*, const StringView& state); |
|||
protocol::DictionaryValue* agentState(const String16& name); |
|||
|
|||
// protocol::FrontendChannel implementation.
|
|||
void sendProtocolResponse(int callId, const String16& message) override; |
|||
void sendProtocolNotification(const String16& message) override; |
|||
void flushProtocolNotifications() override; |
|||
|
|||
int m_contextGroupId; |
|||
V8InspectorImpl* m_inspector; |
|||
V8Inspector::Channel* m_channel; |
|||
bool m_customObjectFormatterEnabled; |
|||
|
|||
protocol::UberDispatcher m_dispatcher; |
|||
std::unique_ptr<protocol::DictionaryValue> m_state; |
|||
|
|||
std::unique_ptr<V8RuntimeAgentImpl> m_runtimeAgent; |
|||
std::unique_ptr<V8DebuggerAgentImpl> m_debuggerAgent; |
|||
std::unique_ptr<V8HeapProfilerAgentImpl> m_heapProfilerAgent; |
|||
std::unique_ptr<V8ProfilerAgentImpl> m_profilerAgent; |
|||
std::unique_ptr<V8ConsoleAgentImpl> m_consoleAgent; |
|||
std::unique_ptr<V8SchemaAgentImpl> m_schemaAgent; |
|||
std::vector<std::unique_ptr<V8InspectorSession::Inspectable>> |
|||
m_inspectedObjects; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(V8InspectorSessionImpl); |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_V8INSPECTORSESSIONIMPL_H_
|
@ -0,0 +1,77 @@ |
|||
// 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/protocol-platform.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(); |
|||
return v8::Local<v8::String>(); |
|||
} |
|||
|
|||
} // 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
|
@ -0,0 +1,23 @@ |
|||
// 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_V8INTERNALVALUETYPE_H_ |
|||
#define V8_INSPECTOR_V8INTERNALVALUETYPE_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_V8INTERNALVALUETYPE_H_
|
@ -0,0 +1,321 @@ |
|||
// 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-profiler-agent-impl.h" |
|||
|
|||
#include <vector> |
|||
|
|||
#include "src/base/atomicops.h" |
|||
#include "src/inspector/protocol/Protocol.h" |
|||
#include "src/inspector/string-util.h" |
|||
#include "src/inspector/v8-debugger.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 "include/v8-profiler.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
namespace ProfilerAgentState { |
|||
static const char samplingInterval[] = "samplingInterval"; |
|||
static const char userInitiatedProfiling[] = "userInitiatedProfiling"; |
|||
static const char profilerEnabled[] = "profilerEnabled"; |
|||
} |
|||
|
|||
namespace { |
|||
|
|||
std::unique_ptr<protocol::Array<protocol::Profiler::PositionTickInfo>> |
|||
buildInspectorObjectForPositionTicks(const v8::CpuProfileNode* node) { |
|||
unsigned lineCount = node->GetHitLineCount(); |
|||
if (!lineCount) return nullptr; |
|||
auto array = protocol::Array<protocol::Profiler::PositionTickInfo>::create(); |
|||
std::vector<v8::CpuProfileNode::LineTick> entries(lineCount); |
|||
if (node->GetLineTicks(&entries[0], lineCount)) { |
|||
for (unsigned i = 0; i < lineCount; i++) { |
|||
std::unique_ptr<protocol::Profiler::PositionTickInfo> line = |
|||
protocol::Profiler::PositionTickInfo::create() |
|||
.setLine(entries[i].line) |
|||
.setTicks(entries[i].hit_count) |
|||
.build(); |
|||
array->addItem(std::move(line)); |
|||
} |
|||
} |
|||
return array; |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Profiler::ProfileNode> buildInspectorObjectFor( |
|||
v8::Isolate* isolate, const v8::CpuProfileNode* node) { |
|||
v8::HandleScope handleScope(isolate); |
|||
auto callFrame = |
|||
protocol::Runtime::CallFrame::create() |
|||
.setFunctionName(toProtocolString(node->GetFunctionName())) |
|||
.setScriptId(String16::fromInteger(node->GetScriptId())) |
|||
.setUrl(toProtocolString(node->GetScriptResourceName())) |
|||
.setLineNumber(node->GetLineNumber() - 1) |
|||
.setColumnNumber(node->GetColumnNumber() - 1) |
|||
.build(); |
|||
auto result = protocol::Profiler::ProfileNode::create() |
|||
.setCallFrame(std::move(callFrame)) |
|||
.setHitCount(node->GetHitCount()) |
|||
.setId(node->GetNodeId()) |
|||
.build(); |
|||
|
|||
const int childrenCount = node->GetChildrenCount(); |
|||
if (childrenCount) { |
|||
auto children = protocol::Array<int>::create(); |
|||
for (int i = 0; i < childrenCount; i++) |
|||
children->addItem(node->GetChild(i)->GetNodeId()); |
|||
result->setChildren(std::move(children)); |
|||
} |
|||
|
|||
const char* deoptReason = node->GetBailoutReason(); |
|||
if (deoptReason && deoptReason[0] && strcmp(deoptReason, "no reason")) |
|||
result->setDeoptReason(deoptReason); |
|||
|
|||
auto positionTicks = buildInspectorObjectForPositionTicks(node); |
|||
if (positionTicks) result->setPositionTicks(std::move(positionTicks)); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Array<int>> buildInspectorObjectForSamples( |
|||
v8::CpuProfile* v8profile) { |
|||
auto array = protocol::Array<int>::create(); |
|||
int count = v8profile->GetSamplesCount(); |
|||
for (int i = 0; i < count; i++) |
|||
array->addItem(v8profile->GetSample(i)->GetNodeId()); |
|||
return array; |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Array<int>> buildInspectorObjectForTimestamps( |
|||
v8::CpuProfile* v8profile) { |
|||
auto array = protocol::Array<int>::create(); |
|||
int count = v8profile->GetSamplesCount(); |
|||
uint64_t lastTime = v8profile->GetStartTime(); |
|||
for (int i = 0; i < count; i++) { |
|||
uint64_t ts = v8profile->GetSampleTimestamp(i); |
|||
array->addItem(static_cast<int>(ts - lastTime)); |
|||
lastTime = ts; |
|||
} |
|||
return array; |
|||
} |
|||
|
|||
void flattenNodesTree(v8::Isolate* isolate, const v8::CpuProfileNode* node, |
|||
protocol::Array<protocol::Profiler::ProfileNode>* list) { |
|||
list->addItem(buildInspectorObjectFor(isolate, node)); |
|||
const int childrenCount = node->GetChildrenCount(); |
|||
for (int i = 0; i < childrenCount; i++) |
|||
flattenNodesTree(isolate, node->GetChild(i), list); |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Profiler::Profile> createCPUProfile( |
|||
v8::Isolate* isolate, v8::CpuProfile* v8profile) { |
|||
auto nodes = protocol::Array<protocol::Profiler::ProfileNode>::create(); |
|||
flattenNodesTree(isolate, v8profile->GetTopDownRoot(), nodes.get()); |
|||
return protocol::Profiler::Profile::create() |
|||
.setNodes(std::move(nodes)) |
|||
.setStartTime(static_cast<double>(v8profile->GetStartTime())) |
|||
.setEndTime(static_cast<double>(v8profile->GetEndTime())) |
|||
.setSamples(buildInspectorObjectForSamples(v8profile)) |
|||
.setTimeDeltas(buildInspectorObjectForTimestamps(v8profile)) |
|||
.build(); |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Debugger::Location> currentDebugLocation( |
|||
V8InspectorImpl* inspector) { |
|||
std::unique_ptr<V8StackTraceImpl> callStack = |
|||
inspector->debugger()->captureStackTrace(false /* fullStack */); |
|||
auto location = protocol::Debugger::Location::create() |
|||
.setScriptId(toString16(callStack->topScriptId())) |
|||
.setLineNumber(callStack->topLineNumber()) |
|||
.build(); |
|||
location->setColumnNumber(callStack->topColumnNumber()); |
|||
return location; |
|||
} |
|||
|
|||
volatile int s_lastProfileId = 0; |
|||
|
|||
} // namespace
|
|||
|
|||
class V8ProfilerAgentImpl::ProfileDescriptor { |
|||
public: |
|||
ProfileDescriptor(const String16& id, const String16& title) |
|||
: m_id(id), m_title(title) {} |
|||
String16 m_id; |
|||
String16 m_title; |
|||
}; |
|||
|
|||
V8ProfilerAgentImpl::V8ProfilerAgentImpl( |
|||
V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel, |
|||
protocol::DictionaryValue* state) |
|||
: m_session(session), |
|||
m_isolate(m_session->inspector()->isolate()), |
|||
m_profiler(nullptr), |
|||
m_state(state), |
|||
m_frontend(frontendChannel), |
|||
m_enabled(false), |
|||
m_recordingCPUProfile(false) {} |
|||
|
|||
V8ProfilerAgentImpl::~V8ProfilerAgentImpl() { |
|||
if (m_profiler) m_profiler->Dispose(); |
|||
} |
|||
|
|||
void V8ProfilerAgentImpl::consoleProfile(const String16& title) { |
|||
if (!m_enabled) return; |
|||
String16 id = nextProfileId(); |
|||
m_startedProfiles.push_back(ProfileDescriptor(id, title)); |
|||
startProfiling(id); |
|||
m_frontend.consoleProfileStarted( |
|||
id, currentDebugLocation(m_session->inspector()), title); |
|||
} |
|||
|
|||
void V8ProfilerAgentImpl::consoleProfileEnd(const String16& title) { |
|||
if (!m_enabled) return; |
|||
String16 id; |
|||
String16 resolvedTitle; |
|||
// Take last started profile if no title was passed.
|
|||
if (title.isEmpty()) { |
|||
if (m_startedProfiles.empty()) return; |
|||
id = m_startedProfiles.back().m_id; |
|||
resolvedTitle = m_startedProfiles.back().m_title; |
|||
m_startedProfiles.pop_back(); |
|||
} else { |
|||
for (size_t i = 0; i < m_startedProfiles.size(); i++) { |
|||
if (m_startedProfiles[i].m_title == title) { |
|||
resolvedTitle = title; |
|||
id = m_startedProfiles[i].m_id; |
|||
m_startedProfiles.erase(m_startedProfiles.begin() + i); |
|||
break; |
|||
} |
|||
} |
|||
if (id.isEmpty()) return; |
|||
} |
|||
std::unique_ptr<protocol::Profiler::Profile> profile = |
|||
stopProfiling(id, true); |
|||
if (!profile) return; |
|||
std::unique_ptr<protocol::Debugger::Location> location = |
|||
currentDebugLocation(m_session->inspector()); |
|||
m_frontend.consoleProfileFinished(id, std::move(location), std::move(profile), |
|||
resolvedTitle); |
|||
} |
|||
|
|||
void V8ProfilerAgentImpl::enable(ErrorString*) { |
|||
if (m_enabled) return; |
|||
m_enabled = true; |
|||
DCHECK(!m_profiler); |
|||
m_profiler = v8::CpuProfiler::New(m_isolate); |
|||
m_state->setBoolean(ProfilerAgentState::profilerEnabled, true); |
|||
} |
|||
|
|||
void V8ProfilerAgentImpl::disable(ErrorString* errorString) { |
|||
if (!m_enabled) return; |
|||
for (size_t i = m_startedProfiles.size(); i > 0; --i) |
|||
stopProfiling(m_startedProfiles[i - 1].m_id, false); |
|||
m_startedProfiles.clear(); |
|||
stop(nullptr, nullptr); |
|||
m_profiler->Dispose(); |
|||
m_profiler = nullptr; |
|||
m_enabled = false; |
|||
m_state->setBoolean(ProfilerAgentState::profilerEnabled, false); |
|||
} |
|||
|
|||
void V8ProfilerAgentImpl::setSamplingInterval(ErrorString* error, |
|||
int interval) { |
|||
if (m_recordingCPUProfile) { |
|||
*error = "Cannot change sampling interval when profiling."; |
|||
return; |
|||
} |
|||
m_state->setInteger(ProfilerAgentState::samplingInterval, interval); |
|||
m_profiler->SetSamplingInterval(interval); |
|||
} |
|||
|
|||
void V8ProfilerAgentImpl::restore() { |
|||
DCHECK(!m_enabled); |
|||
if (!m_state->booleanProperty(ProfilerAgentState::profilerEnabled, false)) |
|||
return; |
|||
m_enabled = true; |
|||
DCHECK(!m_profiler); |
|||
m_profiler = v8::CpuProfiler::New(m_isolate); |
|||
int interval = 0; |
|||
m_state->getInteger(ProfilerAgentState::samplingInterval, &interval); |
|||
if (interval) m_profiler->SetSamplingInterval(interval); |
|||
if (m_state->booleanProperty(ProfilerAgentState::userInitiatedProfiling, |
|||
false)) { |
|||
ErrorString error; |
|||
start(&error); |
|||
} |
|||
} |
|||
|
|||
void V8ProfilerAgentImpl::start(ErrorString* error) { |
|||
if (m_recordingCPUProfile) return; |
|||
if (!m_enabled) { |
|||
*error = "Profiler is not enabled"; |
|||
return; |
|||
} |
|||
m_recordingCPUProfile = true; |
|||
m_frontendInitiatedProfileId = nextProfileId(); |
|||
startProfiling(m_frontendInitiatedProfileId); |
|||
m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, true); |
|||
} |
|||
|
|||
void V8ProfilerAgentImpl::stop( |
|||
ErrorString* errorString, |
|||
std::unique_ptr<protocol::Profiler::Profile>* profile) { |
|||
if (!m_recordingCPUProfile) { |
|||
if (errorString) *errorString = "No recording profiles found"; |
|||
return; |
|||
} |
|||
m_recordingCPUProfile = false; |
|||
std::unique_ptr<protocol::Profiler::Profile> cpuProfile = |
|||
stopProfiling(m_frontendInitiatedProfileId, !!profile); |
|||
if (profile) { |
|||
*profile = std::move(cpuProfile); |
|||
if (!profile->get() && errorString) *errorString = "Profile is not found"; |
|||
} |
|||
m_frontendInitiatedProfileId = String16(); |
|||
m_state->setBoolean(ProfilerAgentState::userInitiatedProfiling, false); |
|||
} |
|||
|
|||
String16 V8ProfilerAgentImpl::nextProfileId() { |
|||
return String16::fromInteger( |
|||
v8::base::NoBarrier_AtomicIncrement(&s_lastProfileId, 1)); |
|||
} |
|||
|
|||
void V8ProfilerAgentImpl::startProfiling(const String16& title) { |
|||
v8::HandleScope handleScope(m_isolate); |
|||
m_profiler->StartProfiling(toV8String(m_isolate, title), true); |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Profiler::Profile> V8ProfilerAgentImpl::stopProfiling( |
|||
const String16& title, bool serialize) { |
|||
v8::HandleScope handleScope(m_isolate); |
|||
v8::CpuProfile* profile = |
|||
m_profiler->StopProfiling(toV8String(m_isolate, title)); |
|||
if (!profile) return nullptr; |
|||
std::unique_ptr<protocol::Profiler::Profile> result; |
|||
if (serialize) result = createCPUProfile(m_isolate, profile); |
|||
profile->Delete(); |
|||
return result; |
|||
} |
|||
|
|||
bool V8ProfilerAgentImpl::isRecording() const { |
|||
return m_recordingCPUProfile || !m_startedProfiles.empty(); |
|||
} |
|||
|
|||
bool V8ProfilerAgentImpl::idleStarted() { |
|||
if (m_profiler) m_profiler->SetIdle(true); |
|||
return m_profiler; |
|||
} |
|||
|
|||
bool V8ProfilerAgentImpl::idleFinished() { |
|||
if (m_profiler) m_profiler->SetIdle(false); |
|||
return m_profiler; |
|||
} |
|||
|
|||
void V8ProfilerAgentImpl::collectSample() { |
|||
if (m_profiler) m_profiler->CollectSample(); |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,74 @@ |
|||
// 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_V8PROFILERAGENTIMPL_H_ |
|||
#define V8_INSPECTOR_V8PROFILERAGENTIMPL_H_ |
|||
|
|||
#include <vector> |
|||
|
|||
#include "src/base/macros.h" |
|||
#include "src/inspector/protocol/Forward.h" |
|||
#include "src/inspector/protocol/Profiler.h" |
|||
|
|||
namespace v8 { |
|||
class CpuProfiler; |
|||
class Isolate; |
|||
} |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
class V8InspectorSessionImpl; |
|||
|
|||
using protocol::ErrorString; |
|||
|
|||
class V8ProfilerAgentImpl : public protocol::Profiler::Backend { |
|||
public: |
|||
V8ProfilerAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*, |
|||
protocol::DictionaryValue* state); |
|||
~V8ProfilerAgentImpl() override; |
|||
|
|||
bool enabled() const { return m_enabled; } |
|||
void restore(); |
|||
|
|||
void enable(ErrorString*) override; |
|||
void disable(ErrorString*) override; |
|||
void setSamplingInterval(ErrorString*, int) override; |
|||
void start(ErrorString*) override; |
|||
void stop(ErrorString*, |
|||
std::unique_ptr<protocol::Profiler::Profile>*) override; |
|||
|
|||
void consoleProfile(const String16& title); |
|||
void consoleProfileEnd(const String16& title); |
|||
|
|||
bool idleStarted(); |
|||
bool idleFinished(); |
|||
|
|||
void collectSample(); |
|||
|
|||
private: |
|||
String16 nextProfileId(); |
|||
|
|||
void startProfiling(const String16& title); |
|||
std::unique_ptr<protocol::Profiler::Profile> stopProfiling( |
|||
const String16& title, bool serialize); |
|||
|
|||
bool isRecording() const; |
|||
|
|||
V8InspectorSessionImpl* m_session; |
|||
v8::Isolate* m_isolate; |
|||
v8::CpuProfiler* m_profiler; |
|||
protocol::DictionaryValue* m_state; |
|||
protocol::Profiler::Frontend m_frontend; |
|||
bool m_enabled; |
|||
bool m_recordingCPUProfile; |
|||
class ProfileDescriptor; |
|||
std::vector<ProfileDescriptor> m_startedProfiles; |
|||
String16 m_frontendInitiatedProfileId; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(V8ProfilerAgentImpl); |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_V8PROFILERAGENTIMPL_H_
|
@ -0,0 +1,93 @@ |
|||
// 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-regex.h" |
|||
|
|||
#include <limits.h> |
|||
|
|||
#include "src/inspector/string-util.h" |
|||
#include "src/inspector/v8-inspector-impl.h" |
|||
|
|||
#include "include/v8-inspector.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
V8Regex::V8Regex(V8InspectorImpl* inspector, const String16& pattern, |
|||
bool caseSensitive, bool multiline) |
|||
: m_inspector(inspector) { |
|||
v8::Isolate* isolate = m_inspector->isolate(); |
|||
v8::HandleScope handleScope(isolate); |
|||
v8::Local<v8::Context> context = m_inspector->regexContext(); |
|||
v8::Context::Scope contextScope(context); |
|||
v8::TryCatch tryCatch(isolate); |
|||
|
|||
unsigned flags = v8::RegExp::kNone; |
|||
if (!caseSensitive) flags |= v8::RegExp::kIgnoreCase; |
|||
if (multiline) flags |= v8::RegExp::kMultiline; |
|||
|
|||
v8::Local<v8::RegExp> regex; |
|||
if (v8::RegExp::New(context, toV8String(isolate, pattern), |
|||
static_cast<v8::RegExp::Flags>(flags)) |
|||
.ToLocal(®ex)) |
|||
m_regex.Reset(isolate, regex); |
|||
else if (tryCatch.HasCaught()) |
|||
m_errorMessage = toProtocolString(tryCatch.Message()->Get()); |
|||
else |
|||
m_errorMessage = "Internal error"; |
|||
} |
|||
|
|||
int V8Regex::match(const String16& string, int startFrom, |
|||
int* matchLength) const { |
|||
if (matchLength) *matchLength = 0; |
|||
|
|||
if (m_regex.IsEmpty() || string.isEmpty()) return -1; |
|||
|
|||
// v8 strings are limited to int.
|
|||
if (string.length() > INT_MAX) return -1; |
|||
|
|||
v8::Isolate* isolate = m_inspector->isolate(); |
|||
v8::HandleScope handleScope(isolate); |
|||
v8::Local<v8::Context> context = m_inspector->regexContext(); |
|||
v8::MicrotasksScope microtasks(isolate, |
|||
v8::MicrotasksScope::kDoNotRunMicrotasks); |
|||
v8::TryCatch tryCatch(isolate); |
|||
|
|||
v8::Local<v8::RegExp> regex = m_regex.Get(isolate); |
|||
v8::Local<v8::Value> exec; |
|||
if (!regex->Get(context, toV8StringInternalized(isolate, "exec")) |
|||
.ToLocal(&exec)) |
|||
return -1; |
|||
v8::Local<v8::Value> argv[] = { |
|||
toV8String(isolate, string.substring(startFrom))}; |
|||
v8::Local<v8::Value> returnValue; |
|||
if (!exec.As<v8::Function>() |
|||
->Call(context, regex, arraysize(argv), argv) |
|||
.ToLocal(&returnValue)) |
|||
return -1; |
|||
|
|||
// RegExp#exec returns null if there's no match, otherwise it returns an
|
|||
// Array of strings with the first being the whole match string and others
|
|||
// being subgroups. The Array also has some random properties tacked on like
|
|||
// "index" which is the offset of the match.
|
|||
//
|
|||
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/RegExp/exec
|
|||
|
|||
DCHECK(!returnValue.IsEmpty()); |
|||
if (!returnValue->IsArray()) return -1; |
|||
|
|||
v8::Local<v8::Array> result = returnValue.As<v8::Array>(); |
|||
v8::Local<v8::Value> matchOffset; |
|||
if (!result->Get(context, toV8StringInternalized(isolate, "index")) |
|||
.ToLocal(&matchOffset)) |
|||
return -1; |
|||
if (matchLength) { |
|||
v8::Local<v8::Value> match; |
|||
if (!result->Get(context, 0).ToLocal(&match)) return -1; |
|||
*matchLength = match.As<v8::String>()->Length(); |
|||
} |
|||
|
|||
return matchOffset.As<v8::Int32>()->Value() + startFrom; |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,37 @@ |
|||
// 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_V8REGEX_H_ |
|||
#define V8_INSPECTOR_V8REGEX_H_ |
|||
|
|||
#include "src/base/macros.h" |
|||
#include "src/inspector/string-16.h" |
|||
|
|||
#include "include/v8.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
class V8InspectorImpl; |
|||
|
|||
enum MultilineMode { MultilineDisabled, MultilineEnabled }; |
|||
|
|||
class V8Regex { |
|||
public: |
|||
V8Regex(V8InspectorImpl*, const String16&, bool caseSensitive, |
|||
bool multiline = false); |
|||
int match(const String16&, int startFrom = 0, int* matchLength = 0) const; |
|||
bool isValid() const { return !m_regex.IsEmpty(); } |
|||
const String16& errorMessage() const { return m_errorMessage; } |
|||
|
|||
private: |
|||
V8InspectorImpl* m_inspector; |
|||
v8::Global<v8::RegExp> m_regex; |
|||
String16 m_errorMessage; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(V8Regex); |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_V8REGEX_H_
|
@ -0,0 +1,738 @@ |
|||
/*
|
|||
* 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. |
|||
*/ |
|||
|
|||
#include "src/inspector/v8-runtime-agent-impl.h" |
|||
|
|||
#include "src/inspector/injected-script.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-message.h" |
|||
#include "src/inspector/v8-debugger-agent-impl.h" |
|||
#include "src/inspector/v8-debugger.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 "include/v8-inspector.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
namespace V8RuntimeAgentImplState { |
|||
static const char customObjectFormatterEnabled[] = |
|||
"customObjectFormatterEnabled"; |
|||
static const char runtimeEnabled[] = "runtimeEnabled"; |
|||
}; |
|||
|
|||
using protocol::Runtime::RemoteObject; |
|||
|
|||
static bool hasInternalError(ErrorString* errorString, bool hasError) { |
|||
if (hasError) *errorString = "Internal error"; |
|||
return hasError; |
|||
} |
|||
|
|||
namespace { |
|||
|
|||
template <typename Callback> |
|||
class ProtocolPromiseHandler { |
|||
public: |
|||
static void add(V8InspectorImpl* inspector, v8::Local<v8::Context> context, |
|||
v8::MaybeLocal<v8::Value> value, |
|||
const String16& notPromiseError, int contextGroupId, |
|||
int executionContextId, const String16& objectGroup, |
|||
bool returnByValue, bool generatePreview, |
|||
std::unique_ptr<Callback> callback) { |
|||
if (value.IsEmpty()) { |
|||
callback->sendFailure("Internal error"); |
|||
return; |
|||
} |
|||
if (!value.ToLocalChecked()->IsPromise()) { |
|||
callback->sendFailure(notPromiseError); |
|||
return; |
|||
} |
|||
v8::MicrotasksScope microtasks_scope(inspector->isolate(), |
|||
v8::MicrotasksScope::kRunMicrotasks); |
|||
v8::Local<v8::Promise> promise = |
|||
v8::Local<v8::Promise>::Cast(value.ToLocalChecked()); |
|||
Callback* rawCallback = callback.get(); |
|||
ProtocolPromiseHandler<Callback>* handler = new ProtocolPromiseHandler( |
|||
inspector, contextGroupId, executionContextId, objectGroup, |
|||
returnByValue, generatePreview, std::move(callback)); |
|||
v8::Local<v8::Value> wrapper = handler->m_wrapper.Get(inspector->isolate()); |
|||
|
|||
v8::Local<v8::Function> thenCallbackFunction = |
|||
v8::Function::New(context, thenCallback, wrapper, 0, |
|||
v8::ConstructorBehavior::kThrow) |
|||
.ToLocalChecked(); |
|||
if (promise->Then(context, thenCallbackFunction).IsEmpty()) { |
|||
rawCallback->sendFailure("Internal error"); |
|||
return; |
|||
} |
|||
v8::Local<v8::Function> catchCallbackFunction = |
|||
v8::Function::New(context, catchCallback, wrapper, 0, |
|||
v8::ConstructorBehavior::kThrow) |
|||
.ToLocalChecked(); |
|||
if (promise->Catch(context, catchCallbackFunction).IsEmpty()) { |
|||
rawCallback->sendFailure("Internal error"); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
private: |
|||
static void thenCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ProtocolPromiseHandler<Callback>* handler = |
|||
static_cast<ProtocolPromiseHandler<Callback>*>( |
|||
info.Data().As<v8::External>()->Value()); |
|||
DCHECK(handler); |
|||
v8::Local<v8::Value> value = |
|||
info.Length() > 0 |
|||
? info[0] |
|||
: v8::Local<v8::Value>::Cast(v8::Undefined(info.GetIsolate())); |
|||
std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue( |
|||
handler->wrapObject(value)); |
|||
if (!wrappedValue) return; |
|||
handler->m_callback->sendSuccess( |
|||
std::move(wrappedValue), Maybe<protocol::Runtime::ExceptionDetails>()); |
|||
} |
|||
|
|||
static void catchCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { |
|||
ProtocolPromiseHandler<Callback>* handler = |
|||
static_cast<ProtocolPromiseHandler<Callback>*>( |
|||
info.Data().As<v8::External>()->Value()); |
|||
DCHECK(handler); |
|||
v8::Local<v8::Value> value = |
|||
info.Length() > 0 |
|||
? info[0] |
|||
: v8::Local<v8::Value>::Cast(v8::Undefined(info.GetIsolate())); |
|||
|
|||
std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue( |
|||
handler->wrapObject(value)); |
|||
if (!wrappedValue) return; |
|||
|
|||
std::unique_ptr<V8StackTraceImpl> stack = |
|||
handler->m_inspector->debugger()->captureStackTrace(true); |
|||
std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails = |
|||
protocol::Runtime::ExceptionDetails::create() |
|||
.setExceptionId(handler->m_inspector->nextExceptionId()) |
|||
.setText("Uncaught (in promise)") |
|||
.setLineNumber(stack && !stack->isEmpty() ? stack->topLineNumber() |
|||
: 0) |
|||
.setColumnNumber( |
|||
stack && !stack->isEmpty() ? stack->topColumnNumber() : 0) |
|||
.setException(wrappedValue->clone()) |
|||
.build(); |
|||
if (stack) |
|||
exceptionDetails->setStackTrace(stack->buildInspectorObjectImpl()); |
|||
if (stack && !stack->isEmpty()) |
|||
exceptionDetails->setScriptId(toString16(stack->topScriptId())); |
|||
handler->m_callback->sendSuccess(std::move(wrappedValue), |
|||
std::move(exceptionDetails)); |
|||
} |
|||
|
|||
ProtocolPromiseHandler(V8InspectorImpl* inspector, int contextGroupId, |
|||
int executionContextId, const String16& objectGroup, |
|||
bool returnByValue, bool generatePreview, |
|||
std::unique_ptr<Callback> callback) |
|||
: m_inspector(inspector), |
|||
m_contextGroupId(contextGroupId), |
|||
m_executionContextId(executionContextId), |
|||
m_objectGroup(objectGroup), |
|||
m_returnByValue(returnByValue), |
|||
m_generatePreview(generatePreview), |
|||
m_callback(std::move(callback)), |
|||
m_wrapper(inspector->isolate(), |
|||
v8::External::New(inspector->isolate(), this)) { |
|||
m_wrapper.SetWeak(this, cleanup, v8::WeakCallbackType::kParameter); |
|||
} |
|||
|
|||
static void cleanup( |
|||
const v8::WeakCallbackInfo<ProtocolPromiseHandler<Callback>>& data) { |
|||
if (!data.GetParameter()->m_wrapper.IsEmpty()) { |
|||
data.GetParameter()->m_wrapper.Reset(); |
|||
data.SetSecondPassCallback(cleanup); |
|||
} else { |
|||
data.GetParameter()->m_callback->sendFailure("Promise was collected"); |
|||
delete data.GetParameter(); |
|||
} |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Runtime::RemoteObject> wrapObject( |
|||
v8::Local<v8::Value> value) { |
|||
ErrorString errorString; |
|||
InjectedScript::ContextScope scope(&errorString, m_inspector, |
|||
m_contextGroupId, m_executionContextId); |
|||
if (!scope.initialize()) { |
|||
m_callback->sendFailure(errorString); |
|||
return nullptr; |
|||
} |
|||
std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue = |
|||
scope.injectedScript()->wrapObject(&errorString, value, m_objectGroup, |
|||
m_returnByValue, m_generatePreview); |
|||
if (!wrappedValue) { |
|||
m_callback->sendFailure(errorString); |
|||
return nullptr; |
|||
} |
|||
return wrappedValue; |
|||
} |
|||
|
|||
V8InspectorImpl* m_inspector; |
|||
int m_contextGroupId; |
|||
int m_executionContextId; |
|||
String16 m_objectGroup; |
|||
bool m_returnByValue; |
|||
bool m_generatePreview; |
|||
std::unique_ptr<Callback> m_callback; |
|||
v8::Global<v8::External> m_wrapper; |
|||
}; |
|||
|
|||
template <typename Callback> |
|||
bool wrapEvaluateResultAsync(InjectedScript* injectedScript, |
|||
v8::MaybeLocal<v8::Value> maybeResultValue, |
|||
const v8::TryCatch& tryCatch, |
|||
const String16& objectGroup, bool returnByValue, |
|||
bool generatePreview, Callback* callback) { |
|||
std::unique_ptr<RemoteObject> result; |
|||
Maybe<protocol::Runtime::ExceptionDetails> exceptionDetails; |
|||
|
|||
ErrorString errorString; |
|||
injectedScript->wrapEvaluateResult( |
|||
&errorString, maybeResultValue, tryCatch, objectGroup, returnByValue, |
|||
generatePreview, &result, &exceptionDetails); |
|||
if (errorString.isEmpty()) { |
|||
callback->sendSuccess(std::move(result), exceptionDetails); |
|||
return true; |
|||
} |
|||
callback->sendFailure(errorString); |
|||
return false; |
|||
} |
|||
|
|||
int ensureContext(ErrorString* errorString, V8InspectorImpl* inspector, |
|||
int contextGroupId, const Maybe<int>& executionContextId) { |
|||
int contextId; |
|||
if (executionContextId.isJust()) { |
|||
contextId = executionContextId.fromJust(); |
|||
} else { |
|||
v8::HandleScope handles(inspector->isolate()); |
|||
v8::Local<v8::Context> defaultContext = |
|||
inspector->client()->ensureDefaultContextInGroup(contextGroupId); |
|||
if (defaultContext.IsEmpty()) { |
|||
*errorString = "Cannot find default execution context"; |
|||
return 0; |
|||
} |
|||
contextId = V8Debugger::contextId(defaultContext); |
|||
} |
|||
return contextId; |
|||
} |
|||
|
|||
} // namespace
|
|||
|
|||
V8RuntimeAgentImpl::V8RuntimeAgentImpl( |
|||
V8InspectorSessionImpl* session, protocol::FrontendChannel* FrontendChannel, |
|||
protocol::DictionaryValue* state) |
|||
: m_session(session), |
|||
m_state(state), |
|||
m_frontend(FrontendChannel), |
|||
m_inspector(session->inspector()), |
|||
m_enabled(false) {} |
|||
|
|||
V8RuntimeAgentImpl::~V8RuntimeAgentImpl() {} |
|||
|
|||
void V8RuntimeAgentImpl::evaluate( |
|||
const String16& expression, const Maybe<String16>& objectGroup, |
|||
const Maybe<bool>& includeCommandLineAPI, const Maybe<bool>& silent, |
|||
const Maybe<int>& executionContextId, const Maybe<bool>& returnByValue, |
|||
const Maybe<bool>& generatePreview, const Maybe<bool>& userGesture, |
|||
const Maybe<bool>& awaitPromise, |
|||
std::unique_ptr<EvaluateCallback> callback) { |
|||
ErrorString errorString; |
|||
int contextId = |
|||
ensureContext(&errorString, m_inspector, m_session->contextGroupId(), |
|||
executionContextId); |
|||
if (!errorString.isEmpty()) { |
|||
callback->sendFailure(errorString); |
|||
return; |
|||
} |
|||
|
|||
InjectedScript::ContextScope scope(&errorString, m_inspector, |
|||
m_session->contextGroupId(), contextId); |
|||
if (!scope.initialize()) { |
|||
callback->sendFailure(errorString); |
|||
return; |
|||
} |
|||
|
|||
if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole(); |
|||
if (userGesture.fromMaybe(false)) scope.pretendUserGesture(); |
|||
|
|||
if (includeCommandLineAPI.fromMaybe(false) && |
|||
!scope.installCommandLineAPI()) { |
|||
callback->sendFailure(errorString); |
|||
return; |
|||
} |
|||
|
|||
bool evalIsDisabled = !scope.context()->IsCodeGenerationFromStringsAllowed(); |
|||
// Temporarily enable allow evals for inspector.
|
|||
if (evalIsDisabled) scope.context()->AllowCodeGenerationFromStrings(true); |
|||
|
|||
v8::MaybeLocal<v8::Value> maybeResultValue; |
|||
v8::Local<v8::Script> script = m_inspector->compileScript( |
|||
scope.context(), toV8String(m_inspector->isolate(), expression), |
|||
String16(), false); |
|||
if (!script.IsEmpty()) |
|||
maybeResultValue = m_inspector->runCompiledScript(scope.context(), script); |
|||
|
|||
if (evalIsDisabled) scope.context()->AllowCodeGenerationFromStrings(false); |
|||
|
|||
// Re-initialize after running client's code, as it could have destroyed
|
|||
// context or session.
|
|||
if (!scope.initialize()) { |
|||
callback->sendFailure(errorString); |
|||
return; |
|||
} |
|||
|
|||
if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) { |
|||
wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue, |
|||
scope.tryCatch(), objectGroup.fromMaybe(""), |
|||
returnByValue.fromMaybe(false), |
|||
generatePreview.fromMaybe(false), callback.get()); |
|||
return; |
|||
} |
|||
ProtocolPromiseHandler<EvaluateCallback>::add( |
|||
m_inspector, scope.context(), maybeResultValue, |
|||
"Result of the evaluation is not a promise", m_session->contextGroupId(), |
|||
scope.injectedScript()->context()->contextId(), objectGroup.fromMaybe(""), |
|||
returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), |
|||
std::move(callback)); |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::awaitPromise( |
|||
const String16& promiseObjectId, const Maybe<bool>& returnByValue, |
|||
const Maybe<bool>& generatePreview, |
|||
std::unique_ptr<AwaitPromiseCallback> callback) { |
|||
ErrorString errorString; |
|||
InjectedScript::ObjectScope scope( |
|||
&errorString, m_inspector, m_session->contextGroupId(), promiseObjectId); |
|||
if (!scope.initialize()) { |
|||
callback->sendFailure(errorString); |
|||
return; |
|||
} |
|||
ProtocolPromiseHandler<AwaitPromiseCallback>::add( |
|||
m_inspector, scope.context(), scope.object(), |
|||
"Could not find promise with given id", m_session->contextGroupId(), |
|||
scope.injectedScript()->context()->contextId(), scope.objectGroupName(), |
|||
returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), |
|||
std::move(callback)); |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::callFunctionOn( |
|||
const String16& objectId, const String16& expression, |
|||
const Maybe<protocol::Array<protocol::Runtime::CallArgument>>& |
|||
optionalArguments, |
|||
const Maybe<bool>& silent, const Maybe<bool>& returnByValue, |
|||
const Maybe<bool>& generatePreview, const Maybe<bool>& userGesture, |
|||
const Maybe<bool>& awaitPromise, |
|||
std::unique_ptr<CallFunctionOnCallback> callback) { |
|||
ErrorString errorString; |
|||
InjectedScript::ObjectScope scope(&errorString, m_inspector, |
|||
m_session->contextGroupId(), objectId); |
|||
if (!scope.initialize()) { |
|||
callback->sendFailure(errorString); |
|||
return; |
|||
} |
|||
|
|||
std::unique_ptr<v8::Local<v8::Value>[]> argv = nullptr; |
|||
int argc = 0; |
|||
if (optionalArguments.isJust()) { |
|||
protocol::Array<protocol::Runtime::CallArgument>* arguments = |
|||
optionalArguments.fromJust(); |
|||
argc = static_cast<int>(arguments->length()); |
|||
argv.reset(new v8::Local<v8::Value>[argc]); |
|||
for (int i = 0; i < argc; ++i) { |
|||
v8::Local<v8::Value> argumentValue; |
|||
if (!scope.injectedScript() |
|||
->resolveCallArgument(&errorString, arguments->get(i)) |
|||
.ToLocal(&argumentValue)) { |
|||
callback->sendFailure(errorString); |
|||
return; |
|||
} |
|||
argv[i] = argumentValue; |
|||
} |
|||
} |
|||
|
|||
if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole(); |
|||
if (userGesture.fromMaybe(false)) scope.pretendUserGesture(); |
|||
|
|||
v8::MaybeLocal<v8::Value> maybeFunctionValue = |
|||
m_inspector->compileAndRunInternalScript( |
|||
scope.context(), |
|||
toV8String(m_inspector->isolate(), "(" + expression + ")")); |
|||
// Re-initialize after running client's code, as it could have destroyed
|
|||
// context or session.
|
|||
if (!scope.initialize()) { |
|||
callback->sendFailure(errorString); |
|||
return; |
|||
} |
|||
|
|||
if (scope.tryCatch().HasCaught()) { |
|||
wrapEvaluateResultAsync(scope.injectedScript(), maybeFunctionValue, |
|||
scope.tryCatch(), scope.objectGroupName(), false, |
|||
false, callback.get()); |
|||
return; |
|||
} |
|||
|
|||
v8::Local<v8::Value> functionValue; |
|||
if (!maybeFunctionValue.ToLocal(&functionValue) || |
|||
!functionValue->IsFunction()) { |
|||
callback->sendFailure("Given expression does not evaluate to a function"); |
|||
return; |
|||
} |
|||
|
|||
v8::MaybeLocal<v8::Value> maybeResultValue = m_inspector->callFunction( |
|||
functionValue.As<v8::Function>(), scope.context(), scope.object(), argc, |
|||
argv.get()); |
|||
// Re-initialize after running client's code, as it could have destroyed
|
|||
// context or session.
|
|||
if (!scope.initialize()) { |
|||
callback->sendFailure(errorString); |
|||
return; |
|||
} |
|||
|
|||
if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) { |
|||
wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue, |
|||
scope.tryCatch(), scope.objectGroupName(), |
|||
returnByValue.fromMaybe(false), |
|||
generatePreview.fromMaybe(false), callback.get()); |
|||
return; |
|||
} |
|||
|
|||
ProtocolPromiseHandler<CallFunctionOnCallback>::add( |
|||
m_inspector, scope.context(), maybeResultValue, |
|||
"Result of the function call is not a promise", |
|||
m_session->contextGroupId(), |
|||
scope.injectedScript()->context()->contextId(), scope.objectGroupName(), |
|||
returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), |
|||
std::move(callback)); |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::getProperties( |
|||
ErrorString* errorString, const String16& objectId, |
|||
const Maybe<bool>& ownProperties, const Maybe<bool>& accessorPropertiesOnly, |
|||
const Maybe<bool>& generatePreview, |
|||
std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>>* |
|||
result, |
|||
Maybe<protocol::Array<protocol::Runtime::InternalPropertyDescriptor>>* |
|||
internalProperties, |
|||
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) { |
|||
using protocol::Runtime::InternalPropertyDescriptor; |
|||
|
|||
InjectedScript::ObjectScope scope(errorString, m_inspector, |
|||
m_session->contextGroupId(), objectId); |
|||
if (!scope.initialize()) return; |
|||
|
|||
scope.ignoreExceptionsAndMuteConsole(); |
|||
if (!scope.object()->IsObject()) { |
|||
*errorString = "Value with given id is not an object"; |
|||
return; |
|||
} |
|||
|
|||
v8::Local<v8::Object> object = scope.object().As<v8::Object>(); |
|||
scope.injectedScript()->getProperties( |
|||
errorString, object, scope.objectGroupName(), |
|||
ownProperties.fromMaybe(false), accessorPropertiesOnly.fromMaybe(false), |
|||
generatePreview.fromMaybe(false), result, exceptionDetails); |
|||
if (!errorString->isEmpty() || exceptionDetails->isJust() || |
|||
accessorPropertiesOnly.fromMaybe(false)) |
|||
return; |
|||
v8::Local<v8::Array> propertiesArray; |
|||
if (hasInternalError(errorString, !m_inspector->debugger() |
|||
->internalProperties(scope.context(), |
|||
scope.object()) |
|||
.ToLocal(&propertiesArray))) |
|||
return; |
|||
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 (hasInternalError( |
|||
errorString, |
|||
!propertiesArray->Get(scope.context(), i).ToLocal(&name)) || |
|||
!name->IsString()) |
|||
return; |
|||
v8::Local<v8::Value> value; |
|||
if (hasInternalError( |
|||
errorString, |
|||
!propertiesArray->Get(scope.context(), i + 1).ToLocal(&value))) |
|||
return; |
|||
std::unique_ptr<RemoteObject> wrappedValue = |
|||
scope.injectedScript()->wrapObject(errorString, value, |
|||
scope.objectGroupName()); |
|||
if (!wrappedValue) return; |
|||
propertiesProtocolArray->addItem( |
|||
InternalPropertyDescriptor::create() |
|||
.setName(toProtocolString(name.As<v8::String>())) |
|||
.setValue(std::move(wrappedValue)) |
|||
.build()); |
|||
} |
|||
if (!propertiesProtocolArray->length()) return; |
|||
*internalProperties = std::move(propertiesProtocolArray); |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::releaseObject(ErrorString* errorString, |
|||
const String16& objectId) { |
|||
InjectedScript::ObjectScope scope(errorString, m_inspector, |
|||
m_session->contextGroupId(), objectId); |
|||
if (!scope.initialize()) return; |
|||
scope.injectedScript()->releaseObject(objectId); |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::releaseObjectGroup(ErrorString*, |
|||
const String16& objectGroup) { |
|||
m_session->releaseObjectGroup(objectGroup); |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::runIfWaitingForDebugger(ErrorString* errorString) { |
|||
m_inspector->client()->runIfWaitingForDebugger(m_session->contextGroupId()); |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::setCustomObjectFormatterEnabled(ErrorString*, |
|||
bool enabled) { |
|||
m_state->setBoolean(V8RuntimeAgentImplState::customObjectFormatterEnabled, |
|||
enabled); |
|||
m_session->setCustomObjectFormatterEnabled(enabled); |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::discardConsoleEntries(ErrorString*) { |
|||
V8ConsoleMessageStorage* storage = |
|||
m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId()); |
|||
storage->clear(); |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::compileScript( |
|||
ErrorString* errorString, const String16& expression, |
|||
const String16& sourceURL, bool persistScript, |
|||
const Maybe<int>& executionContextId, Maybe<String16>* scriptId, |
|||
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) { |
|||
if (!m_enabled) { |
|||
*errorString = "Runtime agent is not enabled"; |
|||
return; |
|||
} |
|||
int contextId = |
|||
ensureContext(errorString, m_inspector, m_session->contextGroupId(), |
|||
executionContextId); |
|||
if (!errorString->isEmpty()) return; |
|||
InjectedScript::ContextScope scope(errorString, m_inspector, |
|||
m_session->contextGroupId(), contextId); |
|||
if (!scope.initialize()) return; |
|||
|
|||
if (!persistScript) m_inspector->debugger()->muteScriptParsedEvents(); |
|||
v8::Local<v8::Script> script = m_inspector->compileScript( |
|||
scope.context(), toV8String(m_inspector->isolate(), expression), |
|||
sourceURL, false); |
|||
if (!persistScript) m_inspector->debugger()->unmuteScriptParsedEvents(); |
|||
if (script.IsEmpty()) { |
|||
if (scope.tryCatch().HasCaught()) |
|||
*exceptionDetails = scope.injectedScript()->createExceptionDetails( |
|||
errorString, scope.tryCatch(), String16(), false); |
|||
else |
|||
*errorString = "Script compilation failed"; |
|||
return; |
|||
} |
|||
|
|||
if (!persistScript) return; |
|||
|
|||
String16 scriptValueId = |
|||
String16::fromInteger(script->GetUnboundScript()->GetId()); |
|||
std::unique_ptr<v8::Global<v8::Script>> global( |
|||
new v8::Global<v8::Script>(m_inspector->isolate(), script)); |
|||
m_compiledScripts[scriptValueId] = std::move(global); |
|||
*scriptId = scriptValueId; |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::runScript( |
|||
const String16& scriptId, const Maybe<int>& executionContextId, |
|||
const Maybe<String16>& objectGroup, const Maybe<bool>& silent, |
|||
const Maybe<bool>& includeCommandLineAPI, const Maybe<bool>& returnByValue, |
|||
const Maybe<bool>& generatePreview, const Maybe<bool>& awaitPromise, |
|||
std::unique_ptr<RunScriptCallback> callback) { |
|||
if (!m_enabled) { |
|||
callback->sendFailure("Runtime agent is not enabled"); |
|||
return; |
|||
} |
|||
|
|||
auto it = m_compiledScripts.find(scriptId); |
|||
if (it == m_compiledScripts.end()) { |
|||
callback->sendFailure("No script with given id"); |
|||
return; |
|||
} |
|||
|
|||
ErrorString errorString; |
|||
int contextId = |
|||
ensureContext(&errorString, m_inspector, m_session->contextGroupId(), |
|||
executionContextId); |
|||
if (!errorString.isEmpty()) { |
|||
callback->sendFailure(errorString); |
|||
return; |
|||
} |
|||
|
|||
InjectedScript::ContextScope scope(&errorString, m_inspector, |
|||
m_session->contextGroupId(), contextId); |
|||
if (!scope.initialize()) { |
|||
callback->sendFailure(errorString); |
|||
return; |
|||
} |
|||
|
|||
if (silent.fromMaybe(false)) scope.ignoreExceptionsAndMuteConsole(); |
|||
|
|||
std::unique_ptr<v8::Global<v8::Script>> scriptWrapper = std::move(it->second); |
|||
m_compiledScripts.erase(it); |
|||
v8::Local<v8::Script> script = scriptWrapper->Get(m_inspector->isolate()); |
|||
if (script.IsEmpty()) { |
|||
callback->sendFailure("Script execution failed"); |
|||
return; |
|||
} |
|||
|
|||
if (includeCommandLineAPI.fromMaybe(false) && !scope.installCommandLineAPI()) |
|||
return; |
|||
|
|||
v8::MaybeLocal<v8::Value> maybeResultValue = |
|||
m_inspector->runCompiledScript(scope.context(), script); |
|||
|
|||
// Re-initialize after running client's code, as it could have destroyed
|
|||
// context or session.
|
|||
if (!scope.initialize()) return; |
|||
|
|||
if (!awaitPromise.fromMaybe(false) || scope.tryCatch().HasCaught()) { |
|||
wrapEvaluateResultAsync(scope.injectedScript(), maybeResultValue, |
|||
scope.tryCatch(), objectGroup.fromMaybe(""), |
|||
returnByValue.fromMaybe(false), |
|||
generatePreview.fromMaybe(false), callback.get()); |
|||
return; |
|||
} |
|||
ProtocolPromiseHandler<RunScriptCallback>::add( |
|||
m_inspector, scope.context(), maybeResultValue.ToLocalChecked(), |
|||
"Result of the script execution is not a promise", |
|||
m_session->contextGroupId(), |
|||
scope.injectedScript()->context()->contextId(), objectGroup.fromMaybe(""), |
|||
returnByValue.fromMaybe(false), generatePreview.fromMaybe(false), |
|||
std::move(callback)); |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::restore() { |
|||
if (!m_state->booleanProperty(V8RuntimeAgentImplState::runtimeEnabled, false)) |
|||
return; |
|||
m_frontend.executionContextsCleared(); |
|||
ErrorString error; |
|||
enable(&error); |
|||
if (m_state->booleanProperty( |
|||
V8RuntimeAgentImplState::customObjectFormatterEnabled, false)) |
|||
m_session->setCustomObjectFormatterEnabled(true); |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::enable(ErrorString* errorString) { |
|||
if (m_enabled) return; |
|||
m_inspector->client()->beginEnsureAllContextsInGroup( |
|||
m_session->contextGroupId()); |
|||
m_enabled = true; |
|||
m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, true); |
|||
m_inspector->enableStackCapturingIfNeeded(); |
|||
m_session->reportAllContexts(this); |
|||
V8ConsoleMessageStorage* storage = |
|||
m_inspector->ensureConsoleMessageStorage(m_session->contextGroupId()); |
|||
for (const auto& message : storage->messages()) { |
|||
if (!reportMessage(message.get(), false)) return; |
|||
} |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::disable(ErrorString* errorString) { |
|||
if (!m_enabled) return; |
|||
m_enabled = false; |
|||
m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, false); |
|||
m_inspector->disableStackCapturingIfNeeded(); |
|||
m_session->discardInjectedScripts(); |
|||
reset(); |
|||
m_inspector->client()->endEnsureAllContextsInGroup( |
|||
m_session->contextGroupId()); |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::reset() { |
|||
m_compiledScripts.clear(); |
|||
if (m_enabled) { |
|||
if (const V8InspectorImpl::ContextByIdMap* contexts = |
|||
m_inspector->contextGroup(m_session->contextGroupId())) { |
|||
for (auto& idContext : *contexts) idContext.second->setReported(false); |
|||
} |
|||
m_frontend.executionContextsCleared(); |
|||
} |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::reportExecutionContextCreated( |
|||
InspectedContext* context) { |
|||
if (!m_enabled) return; |
|||
context->setReported(true); |
|||
std::unique_ptr<protocol::Runtime::ExecutionContextDescription> description = |
|||
protocol::Runtime::ExecutionContextDescription::create() |
|||
.setId(context->contextId()) |
|||
.setName(context->humanReadableName()) |
|||
.setOrigin(context->origin()) |
|||
.build(); |
|||
if (!context->auxData().isEmpty()) |
|||
description->setAuxData(protocol::DictionaryValue::cast( |
|||
protocol::parseJSON(context->auxData()))); |
|||
m_frontend.executionContextCreated(std::move(description)); |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::reportExecutionContextDestroyed( |
|||
InspectedContext* context) { |
|||
if (m_enabled && context->isReported()) { |
|||
context->setReported(false); |
|||
m_frontend.executionContextDestroyed(context->contextId()); |
|||
} |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::inspect( |
|||
std::unique_ptr<protocol::Runtime::RemoteObject> objectToInspect, |
|||
std::unique_ptr<protocol::DictionaryValue> hints) { |
|||
if (m_enabled) |
|||
m_frontend.inspectRequested(std::move(objectToInspect), std::move(hints)); |
|||
} |
|||
|
|||
void V8RuntimeAgentImpl::messageAdded(V8ConsoleMessage* message) { |
|||
if (m_enabled) reportMessage(message, true); |
|||
} |
|||
|
|||
bool V8RuntimeAgentImpl::reportMessage(V8ConsoleMessage* message, |
|||
bool generatePreview) { |
|||
message->reportToFrontend(&m_frontend, m_session, generatePreview); |
|||
m_frontend.flush(); |
|||
return m_inspector->hasConsoleMessageStorage(m_session->contextGroupId()); |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,131 @@ |
|||
/*
|
|||
* 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. |
|||
*/ |
|||
|
|||
#ifndef V8_INSPECTOR_V8RUNTIMEAGENTIMPL_H_ |
|||
#define V8_INSPECTOR_V8RUNTIMEAGENTIMPL_H_ |
|||
|
|||
#include "src/base/macros.h" |
|||
#include "src/inspector/protocol/Forward.h" |
|||
#include "src/inspector/protocol/Runtime.h" |
|||
|
|||
#include "include/v8.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
class InjectedScript; |
|||
class InspectedContext; |
|||
class RemoteObjectIdBase; |
|||
class V8ConsoleMessage; |
|||
class V8InspectorImpl; |
|||
class V8InspectorSessionImpl; |
|||
|
|||
using protocol::ErrorString; |
|||
using protocol::Maybe; |
|||
|
|||
class V8RuntimeAgentImpl : public protocol::Runtime::Backend { |
|||
public: |
|||
V8RuntimeAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*, |
|||
protocol::DictionaryValue* state); |
|||
~V8RuntimeAgentImpl() override; |
|||
void restore(); |
|||
|
|||
// Part of the protocol.
|
|||
void enable(ErrorString*) override; |
|||
void disable(ErrorString*) override; |
|||
void evaluate(const String16& expression, const Maybe<String16>& objectGroup, |
|||
const Maybe<bool>& includeCommandLineAPI, |
|||
const Maybe<bool>& silent, const Maybe<int>& executionContextId, |
|||
const Maybe<bool>& returnByValue, |
|||
const Maybe<bool>& generatePreview, |
|||
const Maybe<bool>& userGesture, const Maybe<bool>& awaitPromise, |
|||
std::unique_ptr<EvaluateCallback>) override; |
|||
void awaitPromise(const String16& promiseObjectId, |
|||
const Maybe<bool>& returnByValue, |
|||
const Maybe<bool>& generatePreview, |
|||
std::unique_ptr<AwaitPromiseCallback>) override; |
|||
void callFunctionOn( |
|||
const String16& objectId, const String16& expression, |
|||
const Maybe<protocol::Array<protocol::Runtime::CallArgument>>& |
|||
optionalArguments, |
|||
const Maybe<bool>& silent, const Maybe<bool>& returnByValue, |
|||
const Maybe<bool>& generatePreview, const Maybe<bool>& userGesture, |
|||
const Maybe<bool>& awaitPromise, |
|||
std::unique_ptr<CallFunctionOnCallback>) override; |
|||
void releaseObject(ErrorString*, const String16& objectId) override; |
|||
void getProperties( |
|||
ErrorString*, const String16& objectId, const Maybe<bool>& ownProperties, |
|||
const Maybe<bool>& accessorPropertiesOnly, |
|||
const Maybe<bool>& generatePreview, |
|||
std::unique_ptr<protocol::Array<protocol::Runtime::PropertyDescriptor>>* |
|||
result, |
|||
Maybe<protocol::Array<protocol::Runtime::InternalPropertyDescriptor>>* |
|||
internalProperties, |
|||
Maybe<protocol::Runtime::ExceptionDetails>*) override; |
|||
void releaseObjectGroup(ErrorString*, const String16& objectGroup) override; |
|||
void runIfWaitingForDebugger(ErrorString*) override; |
|||
void setCustomObjectFormatterEnabled(ErrorString*, bool) override; |
|||
void discardConsoleEntries(ErrorString*) override; |
|||
void compileScript(ErrorString*, const String16& expression, |
|||
const String16& sourceURL, bool persistScript, |
|||
const Maybe<int>& executionContextId, Maybe<String16>*, |
|||
Maybe<protocol::Runtime::ExceptionDetails>*) override; |
|||
void runScript(const String16&, const Maybe<int>& executionContextId, |
|||
const Maybe<String16>& objectGroup, const Maybe<bool>& silent, |
|||
const Maybe<bool>& includeCommandLineAPI, |
|||
const Maybe<bool>& returnByValue, |
|||
const Maybe<bool>& generatePreview, |
|||
const Maybe<bool>& awaitPromise, |
|||
std::unique_ptr<RunScriptCallback>) override; |
|||
|
|||
void reset(); |
|||
void reportExecutionContextCreated(InspectedContext*); |
|||
void reportExecutionContextDestroyed(InspectedContext*); |
|||
void inspect(std::unique_ptr<protocol::Runtime::RemoteObject> objectToInspect, |
|||
std::unique_ptr<protocol::DictionaryValue> hints); |
|||
void messageAdded(V8ConsoleMessage*); |
|||
bool enabled() const { return m_enabled; } |
|||
|
|||
private: |
|||
bool reportMessage(V8ConsoleMessage*, bool generatePreview); |
|||
|
|||
V8InspectorSessionImpl* m_session; |
|||
protocol::DictionaryValue* m_state; |
|||
protocol::Runtime::Frontend m_frontend; |
|||
V8InspectorImpl* m_inspector; |
|||
bool m_enabled; |
|||
protocol::HashMap<String16, std::unique_ptr<v8::Global<v8::Script>>> |
|||
m_compiledScripts; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(V8RuntimeAgentImpl); |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_V8RUNTIMEAGENTIMPL_H_
|
@ -0,0 +1,29 @@ |
|||
// Copyright 2016 the V8 project authors. All rights reserved.
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
|||
// found in the LICENSE file.
|
|||
|
|||
#include "src/inspector/v8-schema-agent-impl.h" |
|||
|
|||
#include "src/inspector/protocol/Protocol.h" |
|||
#include "src/inspector/v8-inspector-session-impl.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
V8SchemaAgentImpl::V8SchemaAgentImpl(V8InspectorSessionImpl* session, |
|||
protocol::FrontendChannel* frontendChannel, |
|||
protocol::DictionaryValue* state) |
|||
: m_session(session), m_frontend(frontendChannel) {} |
|||
|
|||
V8SchemaAgentImpl::~V8SchemaAgentImpl() {} |
|||
|
|||
void V8SchemaAgentImpl::getDomains( |
|||
ErrorString*, |
|||
std::unique_ptr<protocol::Array<protocol::Schema::Domain>>* result) { |
|||
std::vector<std::unique_ptr<protocol::Schema::Domain>> domains = |
|||
m_session->supportedDomainsImpl(); |
|||
*result = protocol::Array<protocol::Schema::Domain>::create(); |
|||
for (size_t i = 0; i < domains.size(); ++i) |
|||
(*result)->addItem(std::move(domains[i])); |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,37 @@ |
|||
// 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_V8SCHEMAAGENTIMPL_H_ |
|||
#define V8_INSPECTOR_V8SCHEMAAGENTIMPL_H_ |
|||
|
|||
#include "src/base/macros.h" |
|||
#include "src/inspector/protocol/Forward.h" |
|||
#include "src/inspector/protocol/Schema.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
class V8InspectorSessionImpl; |
|||
|
|||
using protocol::ErrorString; |
|||
|
|||
class V8SchemaAgentImpl : public protocol::Schema::Backend { |
|||
public: |
|||
V8SchemaAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*, |
|||
protocol::DictionaryValue* state); |
|||
~V8SchemaAgentImpl() override; |
|||
|
|||
void getDomains( |
|||
ErrorString*, |
|||
std::unique_ptr<protocol::Array<protocol::Schema::Domain>>*) override; |
|||
|
|||
private: |
|||
V8InspectorSessionImpl* m_session; |
|||
protocol::Schema::Frontend m_frontend; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(V8SchemaAgentImpl); |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_V8SCHEMAAGENTIMPL_H_
|
@ -0,0 +1,281 @@ |
|||
// 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-stack-trace-impl.h" |
|||
|
|||
#include "src/inspector/string-util.h" |
|||
#include "src/inspector/v8-debugger.h" |
|||
#include "src/inspector/v8-inspector-impl.h" |
|||
#include "src/inspector/v8-profiler-agent-impl.h" |
|||
|
|||
#include "include/v8-debug.h" |
|||
#include "include/v8-profiler.h" |
|||
#include "include/v8-version.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
namespace { |
|||
|
|||
static const v8::StackTrace::StackTraceOptions stackTraceOptions = |
|||
static_cast<v8::StackTrace::StackTraceOptions>( |
|||
v8::StackTrace::kLineNumber | v8::StackTrace::kColumnOffset | |
|||
v8::StackTrace::kScriptId | v8::StackTrace::kScriptNameOrSourceURL | |
|||
v8::StackTrace::kFunctionName); |
|||
|
|||
V8StackTraceImpl::Frame toFrame(v8::Local<v8::StackFrame> frame) { |
|||
String16 scriptId = String16::fromInteger(frame->GetScriptId()); |
|||
String16 sourceName; |
|||
v8::Local<v8::String> sourceNameValue(frame->GetScriptNameOrSourceURL()); |
|||
if (!sourceNameValue.IsEmpty()) |
|||
sourceName = toProtocolString(sourceNameValue); |
|||
|
|||
String16 functionName; |
|||
v8::Local<v8::String> functionNameValue(frame->GetFunctionName()); |
|||
if (!functionNameValue.IsEmpty()) |
|||
functionName = toProtocolString(functionNameValue); |
|||
|
|||
int sourceLineNumber = frame->GetLineNumber(); |
|||
int sourceColumn = frame->GetColumn(); |
|||
return V8StackTraceImpl::Frame(functionName, scriptId, sourceName, |
|||
sourceLineNumber, sourceColumn); |
|||
} |
|||
|
|||
void toFramesVector(v8::Local<v8::StackTrace> stackTrace, |
|||
std::vector<V8StackTraceImpl::Frame>& frames, |
|||
size_t maxStackSize, v8::Isolate* isolate) { |
|||
DCHECK(isolate->InContext()); |
|||
int frameCount = stackTrace->GetFrameCount(); |
|||
if (frameCount > static_cast<int>(maxStackSize)) |
|||
frameCount = static_cast<int>(maxStackSize); |
|||
for (int i = 0; i < frameCount; i++) { |
|||
v8::Local<v8::StackFrame> stackFrame = stackTrace->GetFrame(i); |
|||
frames.push_back(toFrame(stackFrame)); |
|||
} |
|||
} |
|||
|
|||
} // namespace
|
|||
|
|||
V8StackTraceImpl::Frame::Frame() |
|||
: m_functionName("undefined"), |
|||
m_scriptId(""), |
|||
m_scriptName("undefined"), |
|||
m_lineNumber(0), |
|||
m_columnNumber(0) {} |
|||
|
|||
V8StackTraceImpl::Frame::Frame(const String16& functionName, |
|||
const String16& scriptId, |
|||
const String16& scriptName, int lineNumber, |
|||
int column) |
|||
: m_functionName(functionName), |
|||
m_scriptId(scriptId), |
|||
m_scriptName(scriptName), |
|||
m_lineNumber(lineNumber), |
|||
m_columnNumber(column) { |
|||
DCHECK(m_lineNumber != v8::Message::kNoLineNumberInfo); |
|||
DCHECK(m_columnNumber != v8::Message::kNoColumnInfo); |
|||
} |
|||
|
|||
V8StackTraceImpl::Frame::~Frame() {} |
|||
|
|||
// buildInspectorObject() and SourceLocation's toTracedValue() should set the
|
|||
// same fields.
|
|||
// If either of them is modified, the other should be also modified.
|
|||
std::unique_ptr<protocol::Runtime::CallFrame> |
|||
V8StackTraceImpl::Frame::buildInspectorObject() const { |
|||
return protocol::Runtime::CallFrame::create() |
|||
.setFunctionName(m_functionName) |
|||
.setScriptId(m_scriptId) |
|||
.setUrl(m_scriptName) |
|||
.setLineNumber(m_lineNumber - 1) |
|||
.setColumnNumber(m_columnNumber - 1) |
|||
.build(); |
|||
} |
|||
|
|||
V8StackTraceImpl::Frame V8StackTraceImpl::Frame::clone() const { |
|||
return Frame(m_functionName, m_scriptId, m_scriptName, m_lineNumber, |
|||
m_columnNumber); |
|||
} |
|||
|
|||
// static
|
|||
void V8StackTraceImpl::setCaptureStackTraceForUncaughtExceptions( |
|||
v8::Isolate* isolate, bool capture) { |
|||
isolate->SetCaptureStackTraceForUncaughtExceptions( |
|||
capture, V8StackTraceImpl::maxCallStackSizeToCapture, stackTraceOptions); |
|||
} |
|||
|
|||
// static
|
|||
std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::create( |
|||
V8Debugger* debugger, int contextGroupId, |
|||
v8::Local<v8::StackTrace> stackTrace, size_t maxStackSize, |
|||
const String16& description) { |
|||
v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
|||
v8::HandleScope scope(isolate); |
|||
std::vector<V8StackTraceImpl::Frame> frames; |
|||
if (!stackTrace.IsEmpty()) |
|||
toFramesVector(stackTrace, frames, maxStackSize, isolate); |
|||
|
|||
int maxAsyncCallChainDepth = 1; |
|||
V8StackTraceImpl* asyncCallChain = nullptr; |
|||
if (debugger && maxStackSize > 1) { |
|||
asyncCallChain = debugger->currentAsyncCallChain(); |
|||
maxAsyncCallChainDepth = debugger->maxAsyncCallChainDepth(); |
|||
} |
|||
// Do not accidentally append async call chain from another group. This should
|
|||
// not
|
|||
// happen if we have proper instrumentation, but let's double-check to be
|
|||
// safe.
|
|||
if (contextGroupId && asyncCallChain && asyncCallChain->m_contextGroupId && |
|||
asyncCallChain->m_contextGroupId != contextGroupId) { |
|||
asyncCallChain = nullptr; |
|||
maxAsyncCallChainDepth = 1; |
|||
} |
|||
|
|||
// Only the top stack in the chain may be empty, so ensure that second stack
|
|||
// is non-empty (it's the top of appended chain).
|
|||
if (asyncCallChain && asyncCallChain->isEmpty()) |
|||
asyncCallChain = asyncCallChain->m_parent.get(); |
|||
|
|||
if (stackTrace.IsEmpty() && !asyncCallChain) return nullptr; |
|||
|
|||
std::unique_ptr<V8StackTraceImpl> result(new V8StackTraceImpl( |
|||
contextGroupId, description, frames, |
|||
asyncCallChain ? asyncCallChain->cloneImpl() : nullptr)); |
|||
|
|||
// Crop to not exceed maxAsyncCallChainDepth.
|
|||
V8StackTraceImpl* deepest = result.get(); |
|||
while (deepest && maxAsyncCallChainDepth) { |
|||
deepest = deepest->m_parent.get(); |
|||
maxAsyncCallChainDepth--; |
|||
} |
|||
if (deepest) deepest->m_parent.reset(); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
// static
|
|||
std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::capture( |
|||
V8Debugger* debugger, int contextGroupId, size_t maxStackSize, |
|||
const String16& description) { |
|||
v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
|||
v8::HandleScope handleScope(isolate); |
|||
v8::Local<v8::StackTrace> stackTrace; |
|||
if (isolate->InContext()) { |
|||
if (debugger) { |
|||
V8InspectorImpl* inspector = debugger->inspector(); |
|||
V8ProfilerAgentImpl* profilerAgent = |
|||
inspector->enabledProfilerAgentForGroup(contextGroupId); |
|||
if (profilerAgent) profilerAgent->collectSample(); |
|||
} |
|||
stackTrace = v8::StackTrace::CurrentStackTrace( |
|||
isolate, static_cast<int>(maxStackSize), stackTraceOptions); |
|||
} |
|||
return V8StackTraceImpl::create(debugger, contextGroupId, stackTrace, |
|||
maxStackSize, description); |
|||
} |
|||
|
|||
std::unique_ptr<V8StackTraceImpl> V8StackTraceImpl::cloneImpl() { |
|||
std::vector<Frame> framesCopy(m_frames); |
|||
return wrapUnique( |
|||
new V8StackTraceImpl(m_contextGroupId, m_description, framesCopy, |
|||
m_parent ? m_parent->cloneImpl() : nullptr)); |
|||
} |
|||
|
|||
std::unique_ptr<V8StackTrace> V8StackTraceImpl::clone() { |
|||
std::vector<Frame> frames; |
|||
for (size_t i = 0; i < m_frames.size(); i++) |
|||
frames.push_back(m_frames.at(i).clone()); |
|||
return wrapUnique( |
|||
new V8StackTraceImpl(m_contextGroupId, m_description, frames, nullptr)); |
|||
} |
|||
|
|||
V8StackTraceImpl::V8StackTraceImpl(int contextGroupId, |
|||
const String16& description, |
|||
std::vector<Frame>& frames, |
|||
std::unique_ptr<V8StackTraceImpl> parent) |
|||
: m_contextGroupId(contextGroupId), |
|||
m_description(description), |
|||
m_parent(std::move(parent)) { |
|||
m_frames.swap(frames); |
|||
} |
|||
|
|||
V8StackTraceImpl::~V8StackTraceImpl() {} |
|||
|
|||
StringView V8StackTraceImpl::topSourceURL() const { |
|||
DCHECK(m_frames.size()); |
|||
return toStringView(m_frames[0].m_scriptName); |
|||
} |
|||
|
|||
int V8StackTraceImpl::topLineNumber() const { |
|||
DCHECK(m_frames.size()); |
|||
return m_frames[0].m_lineNumber; |
|||
} |
|||
|
|||
int V8StackTraceImpl::topColumnNumber() const { |
|||
DCHECK(m_frames.size()); |
|||
return m_frames[0].m_columnNumber; |
|||
} |
|||
|
|||
StringView V8StackTraceImpl::topFunctionName() const { |
|||
DCHECK(m_frames.size()); |
|||
return toStringView(m_frames[0].m_functionName); |
|||
} |
|||
|
|||
StringView V8StackTraceImpl::topScriptId() const { |
|||
DCHECK(m_frames.size()); |
|||
return toStringView(m_frames[0].m_scriptId); |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Runtime::StackTrace> |
|||
V8StackTraceImpl::buildInspectorObjectImpl() const { |
|||
std::unique_ptr<protocol::Array<protocol::Runtime::CallFrame>> frames = |
|||
protocol::Array<protocol::Runtime::CallFrame>::create(); |
|||
for (size_t i = 0; i < m_frames.size(); i++) |
|||
frames->addItem(m_frames.at(i).buildInspectorObject()); |
|||
|
|||
std::unique_ptr<protocol::Runtime::StackTrace> stackTrace = |
|||
protocol::Runtime::StackTrace::create() |
|||
.setCallFrames(std::move(frames)) |
|||
.build(); |
|||
if (!m_description.isEmpty()) stackTrace->setDescription(m_description); |
|||
if (m_parent) stackTrace->setParent(m_parent->buildInspectorObjectImpl()); |
|||
return stackTrace; |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Runtime::StackTrace> |
|||
V8StackTraceImpl::buildInspectorObjectForTail(V8Debugger* debugger) const { |
|||
v8::HandleScope handleScope(v8::Isolate::GetCurrent()); |
|||
// Next call collapses possible empty stack and ensures
|
|||
// maxAsyncCallChainDepth.
|
|||
std::unique_ptr<V8StackTraceImpl> fullChain = V8StackTraceImpl::create( |
|||
debugger, m_contextGroupId, v8::Local<v8::StackTrace>(), |
|||
V8StackTraceImpl::maxCallStackSizeToCapture); |
|||
if (!fullChain || !fullChain->m_parent) return nullptr; |
|||
return fullChain->m_parent->buildInspectorObjectImpl(); |
|||
} |
|||
|
|||
std::unique_ptr<protocol::Runtime::API::StackTrace> |
|||
V8StackTraceImpl::buildInspectorObject() const { |
|||
return buildInspectorObjectImpl(); |
|||
} |
|||
|
|||
std::unique_ptr<StringBuffer> V8StackTraceImpl::toString() const { |
|||
String16Builder stackTrace; |
|||
for (size_t i = 0; i < m_frames.size(); ++i) { |
|||
const Frame& frame = m_frames[i]; |
|||
stackTrace.append("\n at " + (frame.functionName().length() |
|||
? frame.functionName() |
|||
: "(anonymous function)")); |
|||
stackTrace.append(" ("); |
|||
stackTrace.append(frame.sourceURL()); |
|||
stackTrace.append(':'); |
|||
stackTrace.append(String16::fromInteger(frame.lineNumber())); |
|||
stackTrace.append(':'); |
|||
stackTrace.append(String16::fromInteger(frame.columnNumber())); |
|||
stackTrace.append(')'); |
|||
} |
|||
String16 string = stackTrace.toString(); |
|||
return StringBufferImpl::adopt(string); |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,99 @@ |
|||
// 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_V8STACKTRACEIMPL_H_ |
|||
#define V8_INSPECTOR_V8STACKTRACEIMPL_H_ |
|||
|
|||
#include <vector> |
|||
|
|||
#include "src/base/macros.h" |
|||
#include "src/inspector/protocol/Forward.h" |
|||
#include "src/inspector/protocol/Runtime.h" |
|||
|
|||
#include "include/v8-inspector.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
class TracedValue; |
|||
class V8Debugger; |
|||
|
|||
// Note: async stack trace may have empty top stack with non-empty tail to
|
|||
// indicate
|
|||
// that current native-only state had some async story.
|
|||
// On the other hand, any non-top async stack is guaranteed to be non-empty.
|
|||
class V8StackTraceImpl final : public V8StackTrace { |
|||
public: |
|||
static const size_t maxCallStackSizeToCapture = 200; |
|||
|
|||
class Frame { |
|||
public: |
|||
Frame(); |
|||
Frame(const String16& functionName, const String16& scriptId, |
|||
const String16& scriptName, int lineNumber, int column = 0); |
|||
~Frame(); |
|||
|
|||
const String16& functionName() const { return m_functionName; } |
|||
const String16& scriptId() const { return m_scriptId; } |
|||
const String16& sourceURL() const { return m_scriptName; } |
|||
int lineNumber() const { return m_lineNumber; } |
|||
int columnNumber() const { return m_columnNumber; } |
|||
Frame clone() const; |
|||
|
|||
private: |
|||
friend class V8StackTraceImpl; |
|||
std::unique_ptr<protocol::Runtime::CallFrame> buildInspectorObject() const; |
|||
void toTracedValue(TracedValue*) const; |
|||
|
|||
String16 m_functionName; |
|||
String16 m_scriptId; |
|||
String16 m_scriptName; |
|||
int m_lineNumber; |
|||
int m_columnNumber; |
|||
}; |
|||
|
|||
static void setCaptureStackTraceForUncaughtExceptions(v8::Isolate*, |
|||
bool capture); |
|||
static std::unique_ptr<V8StackTraceImpl> create( |
|||
V8Debugger*, int contextGroupId, v8::Local<v8::StackTrace>, |
|||
size_t maxStackSize, const String16& description = String16()); |
|||
static std::unique_ptr<V8StackTraceImpl> capture( |
|||
V8Debugger*, int contextGroupId, size_t maxStackSize, |
|||
const String16& description = String16()); |
|||
|
|||
// This method drops the async chain. Use cloneImpl() instead.
|
|||
std::unique_ptr<V8StackTrace> clone() override; |
|||
std::unique_ptr<V8StackTraceImpl> cloneImpl(); |
|||
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectForTail( |
|||
V8Debugger*) const; |
|||
std::unique_ptr<protocol::Runtime::StackTrace> buildInspectorObjectImpl() |
|||
const; |
|||
~V8StackTraceImpl() override; |
|||
|
|||
// V8StackTrace implementation.
|
|||
bool isEmpty() const override { return !m_frames.size(); }; |
|||
StringView topSourceURL() const override; |
|||
int topLineNumber() const override; |
|||
int topColumnNumber() const override; |
|||
StringView topScriptId() const override; |
|||
StringView topFunctionName() const override; |
|||
std::unique_ptr<protocol::Runtime::API::StackTrace> buildInspectorObject() |
|||
const override; |
|||
std::unique_ptr<StringBuffer> toString() const override; |
|||
|
|||
private: |
|||
V8StackTraceImpl(int contextGroupId, const String16& description, |
|||
std::vector<Frame>& frames, |
|||
std::unique_ptr<V8StackTraceImpl> parent); |
|||
|
|||
int m_contextGroupId; |
|||
String16 m_description; |
|||
std::vector<Frame> m_frames; |
|||
std::unique_ptr<V8StackTraceImpl> m_parent; |
|||
|
|||
DISALLOW_COPY_AND_ASSIGN(V8StackTraceImpl); |
|||
}; |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_V8STACKTRACEIMPL_H_
|
@ -0,0 +1,110 @@ |
|||
// 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-value-copier.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
namespace { |
|||
|
|||
static int kMaxDepth = 20; |
|||
static int kMaxCalls = 1000; |
|||
|
|||
class V8ValueCopier { |
|||
public: |
|||
v8::MaybeLocal<v8::Value> copy(v8::Local<v8::Value> value, int depth) { |
|||
if (++m_calls > kMaxCalls || depth > kMaxDepth) |
|||
return v8::MaybeLocal<v8::Value>(); |
|||
|
|||
if (value.IsEmpty()) return v8::MaybeLocal<v8::Value>(); |
|||
if (value->IsNull() || value->IsUndefined() || value->IsBoolean() || |
|||
value->IsString() || value->IsNumber()) |
|||
return value; |
|||
if (!value->IsObject()) return v8::MaybeLocal<v8::Value>(); |
|||
v8::Local<v8::Object> object = value.As<v8::Object>(); |
|||
if (object->CreationContext() != m_from) return value; |
|||
|
|||
if (object->IsArray()) { |
|||
v8::Local<v8::Array> array = object.As<v8::Array>(); |
|||
v8::Local<v8::Array> result = v8::Array::New(m_isolate, array->Length()); |
|||
if (!result->SetPrototype(m_to, v8::Null(m_isolate)).FromMaybe(false)) |
|||
return v8::MaybeLocal<v8::Value>(); |
|||
for (uint32_t i = 0; i < array->Length(); ++i) { |
|||
v8::Local<v8::Value> item; |
|||
if (!array->Get(m_from, i).ToLocal(&item)) |
|||
return v8::MaybeLocal<v8::Value>(); |
|||
v8::Local<v8::Value> copied; |
|||
if (!copy(item, depth + 1).ToLocal(&copied)) |
|||
return v8::MaybeLocal<v8::Value>(); |
|||
if (!createDataProperty(m_to, result, i, copied).FromMaybe(false)) |
|||
return v8::MaybeLocal<v8::Value>(); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
v8::Local<v8::Object> result = v8::Object::New(m_isolate); |
|||
if (!result->SetPrototype(m_to, v8::Null(m_isolate)).FromMaybe(false)) |
|||
return v8::MaybeLocal<v8::Value>(); |
|||
v8::Local<v8::Array> properties; |
|||
if (!object->GetOwnPropertyNames(m_from).ToLocal(&properties)) |
|||
return v8::MaybeLocal<v8::Value>(); |
|||
for (uint32_t i = 0; i < properties->Length(); ++i) { |
|||
v8::Local<v8::Value> name; |
|||
if (!properties->Get(m_from, i).ToLocal(&name) || !name->IsString()) |
|||
return v8::MaybeLocal<v8::Value>(); |
|||
v8::Local<v8::Value> property; |
|||
if (!object->Get(m_from, name).ToLocal(&property)) |
|||
return v8::MaybeLocal<v8::Value>(); |
|||
v8::Local<v8::Value> copied; |
|||
if (!copy(property, depth + 1).ToLocal(&copied)) |
|||
return v8::MaybeLocal<v8::Value>(); |
|||
if (!createDataProperty(m_to, result, v8::Local<v8::String>::Cast(name), |
|||
copied) |
|||
.FromMaybe(false)) |
|||
return v8::MaybeLocal<v8::Value>(); |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
v8::Isolate* m_isolate; |
|||
v8::Local<v8::Context> m_from; |
|||
v8::Local<v8::Context> m_to; |
|||
int m_calls; |
|||
}; |
|||
|
|||
} // namespace
|
|||
|
|||
v8::MaybeLocal<v8::Value> copyValueFromDebuggerContext( |
|||
v8::Isolate* isolate, v8::Local<v8::Context> debuggerContext, |
|||
v8::Local<v8::Context> toContext, v8::Local<v8::Value> value) { |
|||
V8ValueCopier copier; |
|||
copier.m_isolate = isolate; |
|||
copier.m_from = debuggerContext; |
|||
copier.m_to = toContext; |
|||
copier.m_calls = 0; |
|||
return copier.copy(value, 0); |
|||
} |
|||
|
|||
v8::Maybe<bool> createDataProperty(v8::Local<v8::Context> context, |
|||
v8::Local<v8::Object> object, |
|||
v8::Local<v8::Name> key, |
|||
v8::Local<v8::Value> value) { |
|||
v8::TryCatch tryCatch(context->GetIsolate()); |
|||
v8::Isolate::DisallowJavascriptExecutionScope throwJs( |
|||
context->GetIsolate(), |
|||
v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE); |
|||
return object->CreateDataProperty(context, key, value); |
|||
} |
|||
|
|||
v8::Maybe<bool> createDataProperty(v8::Local<v8::Context> context, |
|||
v8::Local<v8::Array> array, int index, |
|||
v8::Local<v8::Value> value) { |
|||
v8::TryCatch tryCatch(context->GetIsolate()); |
|||
v8::Isolate::DisallowJavascriptExecutionScope throwJs( |
|||
context->GetIsolate(), |
|||
v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE); |
|||
return array->CreateDataProperty(context, index, value); |
|||
} |
|||
|
|||
} // namespace v8_inspector
|
@ -0,0 +1,24 @@ |
|||
// 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_V8VALUECOPIER_H_ |
|||
#define V8_INSPECTOR_V8VALUECOPIER_H_ |
|||
|
|||
#include "include/v8.h" |
|||
|
|||
namespace v8_inspector { |
|||
|
|||
v8::MaybeLocal<v8::Value> copyValueFromDebuggerContext( |
|||
v8::Isolate*, v8::Local<v8::Context> debuggerContext, |
|||
v8::Local<v8::Context> toContext, v8::Local<v8::Value>); |
|||
v8::Maybe<bool> createDataProperty(v8::Local<v8::Context>, |
|||
v8::Local<v8::Object>, |
|||
v8::Local<v8::Name> key, |
|||
v8::Local<v8::Value>); |
|||
v8::Maybe<bool> createDataProperty(v8::Local<v8::Context>, v8::Local<v8::Array>, |
|||
int index, v8::Local<v8::Value>); |
|||
|
|||
} // namespace v8_inspector
|
|||
|
|||
#endif // V8_INSPECTOR_V8VALUECOPIER_H_
|
@ -0,0 +1,39 @@ |
|||
#!/usr/bin/env python |
|||
# Copyright 2016 The Chromium Authors. All rights reserved. |
|||
# Use of this source code is governed by a BSD-style license that can be |
|||
# found in the LICENSE file. |
|||
|
|||
import os.path |
|||
import sys |
|||
|
|||
try: |
|||
import json |
|||
except ImportError: |
|||
import simplejson as json |
|||
|
|||
|
|||
def main(argv): |
|||
if len(argv) < 1: |
|||
sys.stderr.write("Usage: %s <protocol-1> [<protocol-2> [, <protocol-3>...]] <output-file>\n" % sys.argv[0]) |
|||
return 1 |
|||
|
|||
domains = [] |
|||
version = None |
|||
for protocol in argv[:-1]: |
|||
file_name = os.path.normpath(protocol) |
|||
if not os.path.isfile(file_name): |
|||
sys.stderr.write("Cannot find %s\n" % file_name) |
|||
return 1 |
|||
input_file = open(file_name, "r") |
|||
json_string = input_file.read() |
|||
parsed_json = json.loads(json_string) |
|||
domains += parsed_json["domains"] |
|||
version = parsed_json["version"] |
|||
|
|||
output_file = open(argv[-1], "w") |
|||
json.dump({"version": version, "domains": domains}, output_file, indent=4, sort_keys=False, separators=(',', ': ')) |
|||
output_file.close() |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
sys.exit(main(sys.argv[1:])) |
@ -0,0 +1,82 @@ |
|||
# Copyright 2016 The Chromium Authors. All rights reserved. |
|||
# Use of this source code is governed by a BSD-style license that can be |
|||
# found in the LICENSE file. |
|||
|
|||
# This template will generate inspector protocol source code. The code will |
|||
# not be compiled, use get_target_outputs(<name>) to compile them. |
|||
# |
|||
# Inputs |
|||
# |
|||
# config_file (required) |
|||
# Path to json file specifying inspector protocol configuration. |
|||
# |
|||
# out_dir (required) |
|||
# Path to put the generated files in. It must be inside output or |
|||
# generated file directory. |
|||
# |
|||
# outputs (required) |
|||
# Files generated. Relative to out_dir. |
|||
# |
|||
# inputs (optional) |
|||
# Extra inputs specified by the config file. |
|||
template("inspector_protocol_generate") { |
|||
assert(defined(invoker.config_file)) |
|||
assert(defined(invoker.out_dir)) |
|||
assert(defined(invoker.outputs)) |
|||
|
|||
inspector_protocol_dir = |
|||
"//third_party/WebKit/Source/platform/inspector_protocol" |
|||
|
|||
action(target_name) { |
|||
script = "$inspector_protocol_dir/CodeGenerator.py" |
|||
|
|||
inputs = [ |
|||
invoker.config_file, |
|||
"$inspector_protocol_dir/lib/Allocator_h.template", |
|||
"$inspector_protocol_dir/lib/Array_h.template", |
|||
"$inspector_protocol_dir/lib/BackendCallback_h.template", |
|||
"$inspector_protocol_dir/lib/Collections_h.template", |
|||
"$inspector_protocol_dir/lib/DispatcherBase_cpp.template", |
|||
"$inspector_protocol_dir/lib/DispatcherBase_h.template", |
|||
"$inspector_protocol_dir/lib/ErrorSupport_cpp.template", |
|||
"$inspector_protocol_dir/lib/ErrorSupport_h.template", |
|||
"$inspector_protocol_dir/lib/Forward_h.template", |
|||
"$inspector_protocol_dir/lib/FrontendChannel_h.template", |
|||
"$inspector_protocol_dir/lib/Maybe_h.template", |
|||
"$inspector_protocol_dir/lib/Object_cpp.template", |
|||
"$inspector_protocol_dir/lib/Object_h.template", |
|||
"$inspector_protocol_dir/lib/Parser_cpp.template", |
|||
"$inspector_protocol_dir/lib/Parser_h.template", |
|||
"$inspector_protocol_dir/lib/Protocol_cpp.template", |
|||
"$inspector_protocol_dir/lib/ValueConversions_h.template", |
|||
"$inspector_protocol_dir/lib/Values_cpp.template", |
|||
"$inspector_protocol_dir/lib/Values_h.template", |
|||
"$inspector_protocol_dir/templates/Exported_h.template", |
|||
"$inspector_protocol_dir/templates/Imported_h.template", |
|||
"$inspector_protocol_dir/templates/TypeBuilder_cpp.template", |
|||
"$inspector_protocol_dir/templates/TypeBuilder_h.template", |
|||
] |
|||
if (defined(invoker.inputs)) { |
|||
inputs += invoker.inputs |
|||
} |
|||
|
|||
args = [ |
|||
"--jinja_dir", |
|||
rebase_path("//third_party/", root_build_dir), # jinja is in chromium's third_party |
|||
"--output_base", |
|||
rebase_path(invoker.out_dir, root_build_dir), |
|||
"--config", |
|||
rebase_path(invoker.config_file, root_build_dir), |
|||
] |
|||
|
|||
outputs = get_path_info(rebase_path(invoker.outputs, ".", invoker.out_dir), |
|||
"abspath") |
|||
|
|||
forward_variables_from(invoker, |
|||
[ |
|||
"visibility", |
|||
"deps", |
|||
"public_deps", |
|||
]) |
|||
} |
|||
} |
@ -0,0 +1,36 @@ |
|||
# Copyright 2016 The Chromium Authors. All rights reserved. |
|||
# Use of this source code is governed by a BSD-style license that can be |
|||
# found in the LICENSE file. |
|||
|
|||
# Note: do not remove this file, it's used by v8's gyp. |
|||
# It will be moved out from platform/ soon. |
|||
{ |
|||
'variables': { |
|||
'inspector_protocol_files': [ |
|||
'lib/Allocator_h.template', |
|||
'lib/Array_h.template', |
|||
'lib/BackendCallback_h.template', |
|||
'lib/Collections_h.template', |
|||
'lib/DispatcherBase_cpp.template', |
|||
'lib/DispatcherBase_h.template', |
|||
'lib/ErrorSupport_cpp.template', |
|||
'lib/ErrorSupport_h.template', |
|||
'lib/Forward_h.template', |
|||
'lib/FrontendChannel_h.template', |
|||
'lib/Maybe_h.template', |
|||
'lib/Object_cpp.template', |
|||
'lib/Object_h.template', |
|||
'lib/Parser_cpp.template', |
|||
'lib/Parser_h.template', |
|||
'lib/Protocol_cpp.template', |
|||
'lib/ValueConversions_h.template', |
|||
'lib/Values_cpp.template', |
|||
'lib/Values_h.template', |
|||
'templates/Exported_h.template', |
|||
'templates/Imported_h.template', |
|||
'templates/TypeBuilder_cpp.template', |
|||
'templates/TypeBuilder_h.template', |
|||
'CodeGenerator.py', |
|||
] |
|||
} |
|||
} |
@ -0,0 +1,24 @@ |
|||
// Copyright (c) 2016 The Chromium Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style license that can be |
|||
// found in the LICENSE file. |
|||
|
|||
#ifndef {{"_".join(config.protocol.namespace)}}_BackendCallback_h |
|||
#define {{"_".join(config.protocol.namespace)}}_BackendCallback_h |
|||
|
|||
//#include "Forward.h" |
|||
|
|||
{% for namespace in config.protocol.namespace %} |
|||
namespace {{namespace}} { |
|||
{% endfor %} |
|||
|
|||
class {{config.lib.export_macro}} BackendCallback { |
|||
public: |
|||
virtual ~BackendCallback() { } |
|||
virtual void sendFailure(const ErrorString&) = 0; |
|||
}; |
|||
|
|||
{% for namespace in config.protocol.namespace %} |
|||
} // namespace {{namespace}} |
|||
{% endfor %} |
|||
|
|||
#endif // !defined({{"_".join(config.protocol.namespace)}}_BackendCallback_h) |
@ -0,0 +1,43 @@ |
|||
// Copyright 2016 The Chromium Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style license that can be |
|||
// found in the LICENSE file. |
|||
|
|||
#ifndef {{"_".join(config.protocol.namespace)}}_Collections_h |
|||
#define {{"_".join(config.protocol.namespace)}}_Collections_h |
|||
|
|||
#include "{{config.protocol.package}}/Forward.h" |
|||
#include <cstddef> |
|||
|
|||
#if defined(__APPLE__) && !defined(_LIBCPP_VERSION) |
|||
#include <map> |
|||
#include <set> |
|||
|
|||
{% for namespace in config.protocol.namespace %} |
|||
namespace {{namespace}} { |
|||
{% endfor %} |
|||
|
|||
template <class Key, class T> using HashMap = std::map<Key, T>; |
|||
template <class Key> using HashSet = std::set<Key>; |
|||
|
|||
{% for namespace in config.protocol.namespace %} |
|||
} // namespace {{namespace}} |
|||
{% endfor %} |
|||
|
|||
#else |
|||
#include <unordered_map> |
|||
#include <unordered_set> |
|||
|
|||
{% for namespace in config.protocol.namespace %} |
|||
namespace {{namespace}} { |
|||
{% endfor %} |
|||
|
|||
template <class Key, class T> using HashMap = std::unordered_map<Key, T>; |
|||
template <class Key> using HashSet = std::unordered_set<Key>; |
|||
|
|||
{% for namespace in config.protocol.namespace %} |
|||
} // namespace {{namespace}} |
|||
{% endfor %} |
|||
|
|||
#endif // defined(__APPLE__) && !defined(_LIBCPP_VERSION) |
|||
|
|||
#endif // !defined({{"_".join(config.protocol.namespace)}}_Collections_h) |
@ -0,0 +1,37 @@ |
|||
// Copyright 2016 The Chromium Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style license that can be |
|||
// found in the LICENSE file. |
|||
|
|||
#ifndef {{"_".join(config.protocol.namespace)}}_ErrorSupport_h |
|||
#define {{"_".join(config.protocol.namespace)}}_ErrorSupport_h |
|||
|
|||
//#include "Forward.h" |
|||
|
|||
{% for namespace in config.protocol.namespace %} |
|||
namespace {{namespace}} { |
|||
{% endfor %} |
|||
|
|||
class {{config.lib.export_macro}} ErrorSupport { |
|||
public: |
|||
ErrorSupport(); |
|||
ErrorSupport(String* errorString); |
|||
~ErrorSupport(); |
|||
|
|||
void push(); |
|||
void setName(const String&); |
|||
void pop(); |
|||
void addError(const String&); |
|||
bool hasErrors(); |
|||
String errors(); |
|||
|
|||
private: |
|||
std::vector<String> m_path; |
|||
std::vector<String> m_errors; |
|||
String* m_errorString; |
|||
}; |
|||
|
|||
{% for namespace in config.protocol.namespace %} |
|||
} // namespace {{namespace}} |
|||
{% endfor %} |
|||
|
|||
#endif // !defined({{"_".join(config.protocol.namespace)}}_ErrorSupport_h) |
@ -0,0 +1,37 @@ |
|||
// Copyright 2016 The Chromium Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style license that can be |
|||
// found in the LICENSE file. |
|||
|
|||
#ifndef {{"_".join(config.protocol.namespace)}}_Forward_h |
|||
#define {{"_".join(config.protocol.namespace)}}_Forward_h |
|||
|
|||
{% if config.lib.export_header %} |
|||
#include {{format_include(config.lib.export_header)}} |
|||
{% endif %} |
|||
#include {{format_include(config.lib.platform_header)}} |
|||
#include {{format_include(config.lib.string_header)}} |
|||
|
|||
#include <vector> |
|||
|
|||
{% for namespace in config.protocol.namespace %} |
|||
namespace {{namespace}} { |
|||
{% endfor %} |
|||
|
|||
template<typename T> class Array; |
|||
class DictionaryValue; |
|||
using ErrorString = String; |
|||
class ErrorSupport; |
|||
class FundamentalValue; |
|||
class ListValue; |
|||
template<typename T> class Maybe; |
|||
class Object; |
|||
class SerializedValue; |
|||
class StringValue; |
|||
class UberDispatcher; |
|||
class Value; |
|||
|
|||
{% for namespace in config.protocol.namespace %} |
|||
} // namespace {{namespace}} |
|||
{% endfor %} |
|||
|
|||
#endif // !defined({{"_".join(config.protocol.namespace)}}_Forward_h) |
@ -0,0 +1,24 @@ |
|||
// Copyright 2016 The Chromium Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style license that can be |
|||
// found in the LICENSE file. |
|||
|
|||
#ifndef {{"_".join(config.protocol.namespace)}}_FrontendChannel_h |
|||
#define {{"_".join(config.protocol.namespace)}}_FrontendChannel_h |
|||
|
|||
{% for namespace in config.protocol.namespace %} |
|||
namespace {{namespace}} { |
|||
{% endfor %} |
|||
|
|||
class {{config.lib.export_macro}} FrontendChannel { |
|||
public: |
|||
virtual ~FrontendChannel() { } |
|||
virtual void sendProtocolResponse(int callId, const String& message) = 0; |
|||
virtual void sendProtocolNotification(const String& message) = 0; |
|||
virtual void flushProtocolNotifications() = 0; |
|||
}; |
|||
|
|||
{% for namespace in config.protocol.namespace %} |
|||
} // namespace {{namespace}} |
|||
{% endfor %} |
|||
|
|||
#endif // !defined({{"_".join(config.protocol.namespace)}}_FrontendChannel_h) |
@ -0,0 +1,22 @@ |
|||
// Copyright 2016 The Chromium Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style license that can be |
|||
// found in the LICENSE file. |
|||
|
|||
#ifndef {{"_".join(config.protocol.namespace)}}_Parser_h |
|||
#define {{"_".join(config.protocol.namespace)}}_Parser_h |
|||
|
|||
//#include "Forward.h" |
|||
//#include "Values.h" |
|||
|
|||
{% for namespace in config.protocol.namespace %} |
|||
namespace {{namespace}} { |
|||
{% endfor %} |
|||
|
|||
{{config.lib.export_macro}} std::unique_ptr<Value> parseJSON(const uint8_t*, unsigned); |
|||
{{config.lib.export_macro}} std::unique_ptr<Value> parseJSON(const uint16_t*, unsigned); |
|||
|
|||
{% for namespace in config.protocol.namespace %} |
|||
} // namespace {{namespace}} |
|||
{% endfor %} |
|||
|
|||
#endif // !defined({{"_".join(config.protocol.namespace)}}_Parser_h) |
@ -1,10 +1,12 @@ |
|||
// This file is generated. |
|||
|
|||
// Copyright 2016 The Chromium Authors. All rights reserved. |
|||
// Use of this source code is governed by a BSD-style license that can be |
|||
// found in the LICENSE file. |
|||
|
|||
#ifndef protocol_Platform_h |
|||
#define protocol_Platform_h |
|||
#include "{{config.protocol.package}}/Protocol.h" |
|||
|
|||
#include "{{config.lib.platform_header}}" |
|||
#include <algorithm> |
|||
#include <cmath> |
|||
|
|||
#endif // !defined(protocol_Platform_h) |
|||
#include <cstring> |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue