Commit 335c8ad0 authored by Franziska Hinkelmann's avatar Franziska Hinkelmann Committed by Commit Bot

[type-profile] Incorporate into inspector protocol.

JavaScript is a dynamically typed language. But most code is 
written with fixed types in mind. When debugging JavaScript, 
it is helpful to know the types of variables and parameters 
at runtime. It is often hard to infer types for complex code. 
Type profiling provides this information at runtime.

Node.js uses the inspector protocol. This CL allows Node.js users 
to access and analyse type profile for via Node modules or the
in-procress api. Type Profile helps developers to analyze 
their code for correctness and performance.  

Design doc: https://docs.google.com/a/google.com/document/d/1O1uepXZXBI6IwiawTrYC3ohhiNgzkyTdjn3R8ysbYgk/edit?usp=sharing

Add `takeTypeProfile` to the inspector protocol. It returns a list
of TypeProfileForScripts, which in turn contains the type profile for
each function. We can use TypeProfile data to annotate JavaScript code. 

Sample script with data from TypeProfile:
function f(/*Object, number, undefined*/a, 
           /*Array, number, null*/b, 
           /*boolean, Object, symbol*/c) {
  return 'bye';
/*string*/};
f({}, [], true);
f(3, 2.3, {a: 42});
f(undefined, null, Symbol('hello'));/*string*/

Bug: v8:5933
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.linux:linux_chromium_rel_ng
Change-Id: I626bfb886b752f90b9c86cc6953601558b18b60d
Reviewed-on: https://chromium-review.googlesource.com/508588
Commit-Queue: Franziska Hinkelmann <franzih@chromium.org>
Reviewed-by: 's avatarPavel Feldman <pfeldman@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarAleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47920}
parent 532c9052
...@@ -10175,6 +10175,51 @@ void debug::Coverage::SelectMode(Isolate* isolate, debug::Coverage::Mode mode) { ...@@ -10175,6 +10175,51 @@ void debug::Coverage::SelectMode(Isolate* isolate, debug::Coverage::Mode mode) {
i::Coverage::SelectMode(reinterpret_cast<i::Isolate*>(isolate), mode); i::Coverage::SelectMode(reinterpret_cast<i::Isolate*>(isolate), mode);
} }
int debug::TypeProfile::Entry::SourcePosition() const {
return entry_->position;
}
std::vector<MaybeLocal<String>> debug::TypeProfile::Entry::Types() const {
std::vector<MaybeLocal<String>> result;
for (const internal::Handle<internal::String>& type : entry_->types) {
result.push_back(ToApiHandle<String>(type));
}
return result;
}
Local<debug::Script> debug::TypeProfile::ScriptData::GetScript() const {
return ToApiHandle<debug::Script>(script_->script);
}
std::vector<debug::TypeProfile::Entry> debug::TypeProfile::ScriptData::Entries()
const {
std::vector<debug::TypeProfile::Entry> result;
for (const internal::TypeProfileEntry& entry : script_->entries) {
result.push_back(debug::TypeProfile::Entry(&entry));
}
return result;
}
debug::TypeProfile debug::TypeProfile::Collect(Isolate* isolate) {
return TypeProfile(
i::TypeProfile::Collect(reinterpret_cast<i::Isolate*>(isolate)));
}
void debug::TypeProfile::SelectMode(Isolate* isolate,
debug::TypeProfile::Mode mode) {
i::TypeProfile::SelectMode(reinterpret_cast<i::Isolate*>(isolate), mode);
}
size_t debug::TypeProfile::ScriptCount() const { return type_profile_->size(); }
debug::TypeProfile::ScriptData debug::TypeProfile::GetScriptData(
size_t i) const {
// TODO(franzih): ScriptData is invalid after ~TypeProfile. Same in Coverage.
return ScriptData(&type_profile_->at(i));
}
debug::TypeProfile::~TypeProfile() { delete type_profile_; }
const char* CpuProfileNode::GetFunctionNameStr() const { const char* CpuProfileNode::GetFunctionNameStr() const {
const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this); const i::ProfileNode* node = reinterpret_cast<const i::ProfileNode*>(this);
return node->entry()->name(); return node->entry()->name();
......
...@@ -564,5 +564,91 @@ void Coverage::SelectMode(Isolate* isolate, debug::Coverage::Mode mode) { ...@@ -564,5 +564,91 @@ void Coverage::SelectMode(Isolate* isolate, debug::Coverage::Mode mode) {
isolate->set_code_coverage_mode(mode); isolate->set_code_coverage_mode(mode);
} }
TypeProfile* TypeProfile::Collect(Isolate* isolate) {
TypeProfile* result = new TypeProfile();
// Collect existing feedback vectors.
std::vector<Handle<FeedbackVector>> feedback_vectors;
{
HeapIterator heap_iterator(isolate->heap());
while (HeapObject* current_obj = heap_iterator.next()) {
if (current_obj->IsFeedbackVector()) {
FeedbackVector* vector = FeedbackVector::cast(current_obj);
SharedFunctionInfo* shared = vector->shared_function_info();
if (!shared->IsSubjectToDebugging()) continue;
feedback_vectors.emplace_back(vector, isolate);
}
}
}
Script::Iterator scripts(isolate);
while (Script* script = scripts.Next()) {
if (!script->IsUserJavaScript()) {
continue;
}
Handle<Script> script_handle(script, isolate);
TypeProfileScript type_profile_script(script_handle);
std::vector<TypeProfileEntry>* entries = &type_profile_script.entries;
for (const auto& vector : feedback_vectors) {
SharedFunctionInfo* info = vector->shared_function_info();
DCHECK(info->IsSubjectToDebugging());
// Match vectors with script.
if (script != info->script()) {
continue;
}
if (info->feedback_metadata()->is_empty() ||
!info->feedback_metadata()->HasTypeProfileSlot()) {
continue;
}
FeedbackSlot slot = vector->GetTypeProfileSlot();
CollectTypeProfileNexus nexus(vector, slot);
Handle<String> name(info->DebugName(), isolate);
std::vector<int> source_positions = nexus.GetSourcePositions();
for (int position : source_positions) {
DCHECK_GE(position, 0);
entries->emplace_back(position, nexus.GetTypesForSourcePositions(
static_cast<uint32_t>(position)));
}
// Releases type profile data collected so far.
nexus.Clear();
}
if (!entries->empty()) {
result->emplace_back(type_profile_script);
}
}
return result;
}
void TypeProfile::SelectMode(Isolate* isolate, debug::TypeProfile::Mode mode) {
isolate->set_type_profile_mode(mode);
HandleScope handle_scope(isolate);
if (mode == debug::TypeProfile::Mode::kNone) {
// Release type profile data collected so far.
{
HeapIterator heap_iterator(isolate->heap());
while (HeapObject* current_obj = heap_iterator.next()) {
if (current_obj->IsFeedbackVector()) {
FeedbackVector* vector = FeedbackVector::cast(current_obj);
SharedFunctionInfo* info = vector->shared_function_info();
if (!info->IsSubjectToDebugging() ||
info->feedback_metadata()->is_empty() ||
!info->feedback_metadata()->HasTypeProfileSlot())
continue;
FeedbackSlot slot = vector->GetTypeProfileSlot();
CollectTypeProfileNexus nexus(vector, slot);
nexus.Clear();
}
}
}
}
}
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -66,6 +66,29 @@ class Coverage : public std::vector<CoverageScript> { ...@@ -66,6 +66,29 @@ class Coverage : public std::vector<CoverageScript> {
Coverage() {} Coverage() {}
}; };
struct TypeProfileEntry {
explicit TypeProfileEntry(
int pos, std::vector<v8::internal::Handle<internal::String>> t)
: position(pos), types(std::move(t)) {}
int position;
std::vector<v8::internal::Handle<internal::String>> types;
};
struct TypeProfileScript {
explicit TypeProfileScript(Handle<Script> s) : script(s) {}
Handle<Script> script;
std::vector<TypeProfileEntry> entries;
};
class TypeProfile : public std::vector<TypeProfileScript> {
public:
static TypeProfile* Collect(Isolate* isolate);
static void SelectMode(Isolate* isolate, debug::TypeProfile::Mode mode);
private:
TypeProfile() {}
};
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -18,9 +18,12 @@ namespace internal { ...@@ -18,9 +18,12 @@ namespace internal {
struct CoverageBlock; struct CoverageBlock;
struct CoverageFunction; struct CoverageFunction;
struct CoverageScript; struct CoverageScript;
struct TypeProfileEntry;
struct TypeProfileScript;
class Coverage; class Coverage;
class Script; class Script;
} class TypeProfile;
} // namespace internal
namespace debug { namespace debug {
...@@ -335,6 +338,62 @@ class V8_EXPORT_PRIVATE Coverage { ...@@ -335,6 +338,62 @@ class V8_EXPORT_PRIVATE Coverage {
i::Coverage* coverage_; i::Coverage* coverage_;
}; };
/*
* Provide API layer between inspector and type profile.
*/
class V8_EXPORT_PRIVATE TypeProfile {
public:
MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(TypeProfile);
enum Mode {
kNone,
kCollect,
};
class ScriptData; // Forward declaration.
class V8_EXPORT_PRIVATE Entry {
public:
MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(Entry);
int SourcePosition() const;
std::vector<MaybeLocal<String>> Types() const;
private:
explicit Entry(const i::TypeProfileEntry* entry) : entry_(entry) {}
const i::TypeProfileEntry* entry_;
friend class v8::debug::TypeProfile::ScriptData;
};
class V8_EXPORT_PRIVATE ScriptData {
public:
MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(ScriptData);
Local<debug::Script> GetScript() const;
std::vector<Entry> Entries() const;
private:
explicit ScriptData(i::TypeProfileScript* script) : script_(script) {}
i::TypeProfileScript* script_;
friend class v8::debug::TypeProfile;
};
static TypeProfile Collect(Isolate* isolate);
static void SelectMode(Isolate* isolate, Mode mode);
size_t ScriptCount() const;
ScriptData GetScriptData(size_t i) const;
~TypeProfile();
private:
explicit TypeProfile(i::TypeProfile* type_profile)
: type_profile_(type_profile) {}
i::TypeProfile* type_profile_;
};
class ScopeIterator { class ScopeIterator {
public: public:
static std::unique_ptr<ScopeIterator> CreateForFunction( static std::unique_ptr<ScopeIterator> CreateForFunction(
......
...@@ -31,7 +31,6 @@ FeedbackSlot FeedbackVectorSpecBase<Derived>::AddSlot(FeedbackSlotKind kind) { ...@@ -31,7 +31,6 @@ FeedbackSlot FeedbackVectorSpecBase<Derived>::AddSlot(FeedbackSlotKind kind) {
template <typename Derived> template <typename Derived>
FeedbackSlot FeedbackVectorSpecBase<Derived>::AddTypeProfileSlot() { FeedbackSlot FeedbackVectorSpecBase<Derived>::AddTypeProfileSlot() {
DCHECK(FLAG_type_profile);
FeedbackSlot slot = AddSlot(FeedbackSlotKind::kTypeProfile); FeedbackSlot slot = AddSlot(FeedbackSlotKind::kTypeProfile);
CHECK_EQ(FeedbackVectorSpec::kTypeProfileSlotIndex, CHECK_EQ(FeedbackVectorSpec::kTypeProfileSlotIndex,
FeedbackVector::GetIndex(slot)); FeedbackVector::GetIndex(slot));
......
...@@ -173,7 +173,8 @@ const char* FeedbackMetadata::Kind2String(FeedbackSlotKind kind) { ...@@ -173,7 +173,8 @@ const char* FeedbackMetadata::Kind2String(FeedbackSlotKind kind) {
bool FeedbackMetadata::HasTypeProfileSlot() const { bool FeedbackMetadata::HasTypeProfileSlot() const {
FeedbackSlot slot = FeedbackSlot slot =
FeedbackVector::ToSlot(FeedbackVectorSpec::kTypeProfileSlotIndex); FeedbackVector::ToSlot(FeedbackVectorSpec::kTypeProfileSlotIndex);
return GetKind(slot) == FeedbackSlotKind::kTypeProfile; return slot.ToInt() < this->length() &&
GetKind(slot) == FeedbackSlotKind::kTypeProfile;
} }
FeedbackSlotKind FeedbackVector::GetKind(FeedbackSlot slot) const { FeedbackSlotKind FeedbackVector::GetKind(FeedbackSlot slot) const {
...@@ -954,6 +955,19 @@ InlineCacheState CollectTypeProfileNexus::StateFromFeedback() const { ...@@ -954,6 +955,19 @@ InlineCacheState CollectTypeProfileNexus::StateFromFeedback() const {
return MONOMORPHIC; return MONOMORPHIC;
} }
namespace {
bool InList(Handle<ArrayList> types, Handle<String> type) {
for (int i = 0; i < types->Length(); i++) {
Object* obj = types->Get(i);
if (String::cast(obj)->Equals(*type)) {
return true;
}
}
return false;
}
} // anonymous namespace
void CollectTypeProfileNexus::Collect(Handle<String> type, int position) { void CollectTypeProfileNexus::Collect(Handle<String> type, int position) {
DCHECK_GE(position, 0); DCHECK_GE(position, 0);
Isolate* isolate = GetIsolate(); Isolate* isolate = GetIsolate();
...@@ -974,16 +988,76 @@ void CollectTypeProfileNexus::Collect(Handle<String> type, int position) { ...@@ -974,16 +988,76 @@ void CollectTypeProfileNexus::Collect(Handle<String> type, int position) {
int entry = types->FindEntry(position); int entry = types->FindEntry(position);
if (entry == UnseededNumberDictionary::kNotFound) { if (entry == UnseededNumberDictionary::kNotFound) {
position_specific_types = ArrayList::New(isolate, 1); position_specific_types = ArrayList::New(isolate, 1);
types = UnseededNumberDictionary::Set(
types, position, ArrayList::Add(position_specific_types, type));
} else { } else {
DCHECK(types->ValueAt(entry)->IsArrayList()); DCHECK(types->ValueAt(entry)->IsArrayList());
position_specific_types = handle(ArrayList::cast(types->ValueAt(entry))); position_specific_types = handle(ArrayList::cast(types->ValueAt(entry)));
if (!InList(position_specific_types, type)) { // Add type
types = UnseededNumberDictionary::Set(
types, position, ArrayList::Add(position_specific_types, type));
}
} }
types = UnseededNumberDictionary::Set(
types, position, ArrayList::Add(position_specific_types, type));
SetFeedback(*types); SetFeedback(*types);
} }
void CollectTypeProfileNexus::Clear() {
SetFeedback(*FeedbackVector::UninitializedSentinel(GetIsolate()));
}
std::vector<int> CollectTypeProfileNexus::GetSourcePositions() const {
std::vector<int> source_positions;
Isolate* isolate = GetIsolate();
Object* const feedback = GetFeedback();
if (feedback == *FeedbackVector::UninitializedSentinel(isolate)) {
return source_positions;
}
Handle<UnseededNumberDictionary> types = Handle<UnseededNumberDictionary>(
UnseededNumberDictionary::cast(feedback), isolate);
for (int index = UnseededNumberDictionary::kElementsStartIndex;
index < types->length(); index += UnseededNumberDictionary::kEntrySize) {
int key_index = index + UnseededNumberDictionary::kEntryKeyIndex;
Object* key = types->get(key_index);
if (key->IsSmi()) {
int position = Smi::cast(key)->value();
source_positions.push_back(position);
}
}
return source_positions;
}
std::vector<Handle<String>> CollectTypeProfileNexus::GetTypesForSourcePositions(
uint32_t position) const {
Isolate* isolate = GetIsolate();
Object* const feedback = GetFeedback();
std::vector<Handle<String>> types_for_position;
if (feedback == *FeedbackVector::UninitializedSentinel(isolate)) {
return types_for_position;
}
Handle<UnseededNumberDictionary> types = Handle<UnseededNumberDictionary>(
UnseededNumberDictionary::cast(feedback), isolate);
int entry = types->FindEntry(position);
if (entry == UnseededNumberDictionary::kNotFound) {
return types_for_position;
}
DCHECK(types->ValueAt(entry)->IsArrayList());
Handle<ArrayList> position_specific_types =
Handle<ArrayList>(ArrayList::cast(types->ValueAt(entry)));
for (int i = 0; i < position_specific_types->Length(); i++) {
Object* t = position_specific_types->Get(i);
types_for_position.push_back(Handle<String>(String::cast(t), isolate));
}
return types_for_position;
}
namespace { namespace {
Handle<JSObject> ConvertToJSObject(Isolate* isolate, Handle<JSObject> ConvertToJSObject(Isolate* isolate,
......
...@@ -855,6 +855,11 @@ class CollectTypeProfileNexus : public FeedbackNexus { ...@@ -855,6 +855,11 @@ class CollectTypeProfileNexus : public FeedbackNexus {
void Collect(Handle<String> type, int position); void Collect(Handle<String> type, int position);
JSObject* GetTypeProfile() const; JSObject* GetTypeProfile() const;
std::vector<int> GetSourcePositions() const;
std::vector<Handle<String>> GetTypesForSourcePositions(uint32_t pos) const;
void Clear() override;
InlineCacheState StateFromFeedback() const override; InlineCacheState StateFromFeedback() const override;
}; };
......
...@@ -289,7 +289,6 @@ DEFINE_IMPLICATION(track_computed_fields, track_fields) ...@@ -289,7 +289,6 @@ DEFINE_IMPLICATION(track_computed_fields, track_fields)
DEFINE_BOOL(track_field_types, true, "track field types") DEFINE_BOOL(track_field_types, true, "track field types")
DEFINE_IMPLICATION(track_field_types, track_fields) DEFINE_IMPLICATION(track_field_types, track_fields)
DEFINE_IMPLICATION(track_field_types, track_heap_object_fields) DEFINE_IMPLICATION(track_field_types, track_heap_object_fields)
DEFINE_BOOL(type_profile, false, "collect type information")
DEFINE_BOOL(block_coverage, true, "enable block code coverage") DEFINE_BOOL(block_coverage, true, "enable block code coverage")
DEFINE_BOOL(trace_block_coverage, false, DEFINE_BOOL(trace_block_coverage, false,
"trace collected block coverage information") "trace collected block coverage information")
......
...@@ -891,6 +891,34 @@ ...@@ -891,6 +891,34 @@
{ "name": "functions", "type": "array", "items": { "$ref": "FunctionCoverage" }, "description": "Functions contained in the script that has coverage data." } { "name": "functions", "type": "array", "items": { "$ref": "FunctionCoverage" }, "description": "Functions contained in the script that has coverage data." }
], ],
"experimental": true "experimental": true
},
{ "id": "TypeObject",
"type": "object",
"description": "Describes a type collected during runtime.",
"properties": [
{ "name": "name", "type": "string", "description": "Name of a type collected with type profiling." }
],
"experimental": true
},
{ "id": "TypeProfileEntry",
"type": "object",
"description": "Source offset and types for a parameter or return value.",
"properties": [
{ "name": "offset", "type": "integer", "description": "Source offset of the parameter or end of function for return values." },
{ "name": "types", "type": "array", "items": {"$ref": "TypeObject"}, "description": "The types for this parameter or return value."}
],
"experimental": true
},
{
"id": "ScriptTypeProfile",
"type": "object",
"description": "Type profile data collected during runtime for a JavaScript script.",
"properties": [
{ "name": "scriptId", "$ref": "Runtime.ScriptId", "description": "JavaScript script id." },
{ "name": "url", "type": "string", "description": "JavaScript script name or url." },
{ "name": "entries", "type": "array", "items": { "$ref": "TypeProfileEntry" }, "description": "Type profile entries for parameters and return values of the functions in the script." }
],
"experimental": true
} }
], ],
"commands": [ "commands": [
...@@ -945,6 +973,24 @@ ...@@ -945,6 +973,24 @@
], ],
"description": "Collect coverage data for the current isolate. The coverage data may be incomplete due to garbage collection.", "description": "Collect coverage data for the current isolate. The coverage data may be incomplete due to garbage collection.",
"experimental": true "experimental": true
},
{
"name": "startTypeProfile",
"description": "Enable type profile.",
"experimental": true
},
{
"name": "stopTypeProfile",
"description": "Disable type profile. Disabling releases type profile data collected so far.",
"experimental": true
},
{
"name": "takeTypeProfile",
"returns": [
{ "name": "result", "type": "array", "items": { "$ref": "ScriptTypeProfile" }, "description": "Type profile for all scripts since startTypeProfile() was turned on." }
],
"description": "Collect type profile.",
"experimental": true
} }
], ],
"events": [ "events": [
......
...@@ -26,6 +26,7 @@ static const char profilerEnabled[] = "profilerEnabled"; ...@@ -26,6 +26,7 @@ static const char profilerEnabled[] = "profilerEnabled";
static const char preciseCoverageStarted[] = "preciseCoverageStarted"; static const char preciseCoverageStarted[] = "preciseCoverageStarted";
static const char preciseCoverageCallCount[] = "preciseCoverageCallCount"; static const char preciseCoverageCallCount[] = "preciseCoverageCallCount";
static const char preciseCoverageDetailed[] = "preciseCoverageDetailed"; static const char preciseCoverageDetailed[] = "preciseCoverageDetailed";
static const char typeProfileStarted[] = "typeProfileStarted";
} }
namespace { namespace {
...@@ -396,6 +397,76 @@ Response V8ProfilerAgentImpl::getBestEffortCoverage( ...@@ -396,6 +397,76 @@ Response V8ProfilerAgentImpl::getBestEffortCoverage(
return coverageToProtocol(m_isolate, coverage, out_result); return coverageToProtocol(m_isolate, coverage, out_result);
} }
namespace {
std::unique_ptr<protocol::Array<protocol::Profiler::ScriptTypeProfile>>
typeProfileToProtocol(v8::Isolate* isolate,
const v8::debug::TypeProfile& type_profile) {
std::unique_ptr<protocol::Array<protocol::Profiler::ScriptTypeProfile>>
result = protocol::Array<protocol::Profiler::ScriptTypeProfile>::create();
for (size_t i = 0; i < type_profile.ScriptCount(); i++) {
v8::debug::TypeProfile::ScriptData script_data =
type_profile.GetScriptData(i);
v8::Local<v8::debug::Script> script = script_data.GetScript();
std::unique_ptr<protocol::Array<protocol::Profiler::TypeProfileEntry>>
entries =
protocol::Array<protocol::Profiler::TypeProfileEntry>::create();
for (const auto& entry : script_data.Entries()) {
std::unique_ptr<protocol::Array<protocol::Profiler::TypeObject>> types =
protocol::Array<protocol::Profiler::TypeObject>::create();
for (const auto& type : entry.Types()) {
types->addItem(protocol::Profiler::TypeObject::create()
.setName(toProtocolString(
type.FromMaybe(v8::Local<v8::String>())))
.build());
}
entries->addItem(protocol::Profiler::TypeProfileEntry::create()
.setOffset(entry.SourcePosition())
.setTypes(std::move(types))
.build());
}
String16 url;
v8::Local<v8::String> name;
if (script->Name().ToLocal(&name) || script->SourceURL().ToLocal(&name)) {
url = toProtocolString(name);
}
result->addItem(protocol::Profiler::ScriptTypeProfile::create()
.setScriptId(String16::fromInteger(script->Id()))
.setUrl(url)
.setEntries(std::move(entries))
.build());
}
return result;
}
} // anonymous namespace
Response V8ProfilerAgentImpl::startTypeProfile() {
m_state->setBoolean(ProfilerAgentState::typeProfileStarted, true);
v8::debug::TypeProfile::SelectMode(m_isolate,
v8::debug::TypeProfile::kCollect);
return Response::OK();
}
Response V8ProfilerAgentImpl::stopTypeProfile() {
m_state->setBoolean(ProfilerAgentState::typeProfileStarted, false);
v8::debug::TypeProfile::SelectMode(m_isolate, v8::debug::TypeProfile::kNone);
return Response::OK();
}
Response V8ProfilerAgentImpl::takeTypeProfile(
std::unique_ptr<protocol::Array<protocol::Profiler::ScriptTypeProfile>>*
out_result) {
if (!m_state->booleanProperty(ProfilerAgentState::typeProfileStarted,
false)) {
return Response::Error("Type profile has not been started.");
}
v8::HandleScope handle_scope(m_isolate);
v8::debug::TypeProfile type_profile =
v8::debug::TypeProfile::Collect(m_isolate);
*out_result = typeProfileToProtocol(m_isolate, type_profile);
return Response::OK();
}
String16 V8ProfilerAgentImpl::nextProfileId() { String16 V8ProfilerAgentImpl::nextProfileId() {
return String16::fromInteger( return String16::fromInteger(
v8::base::Relaxed_AtomicIncrement(&s_lastProfileId, 1)); v8::base::Relaxed_AtomicIncrement(&s_lastProfileId, 1));
......
...@@ -48,6 +48,12 @@ class V8ProfilerAgentImpl : public protocol::Profiler::Backend { ...@@ -48,6 +48,12 @@ class V8ProfilerAgentImpl : public protocol::Profiler::Backend {
std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>* std::unique_ptr<protocol::Array<protocol::Profiler::ScriptCoverage>>*
out_result) override; out_result) override;
Response startTypeProfile() override;
Response stopTypeProfile() override;
Response takeTypeProfile(
std::unique_ptr<protocol::Array<protocol::Profiler::ScriptTypeProfile>>*
out_result) override;
void consoleProfile(const String16& title); void consoleProfile(const String16& title);
void consoleProfileEnd(const String16& title); void consoleProfileEnd(const String16& title);
......
...@@ -35,7 +35,6 @@ ...@@ -35,7 +35,6 @@
#include "src/inspector/inspected-context.h" #include "src/inspector/inspected-context.h"
#include "src/inspector/protocol/Protocol.h" #include "src/inspector/protocol/Protocol.h"
#include "src/inspector/remote-object-id.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-console-message.h"
#include "src/inspector/v8-debugger-agent-impl.h" #include "src/inspector/v8-debugger-agent-impl.h"
#include "src/inspector/v8-debugger.h" #include "src/inspector/v8-debugger.h"
......
...@@ -821,7 +821,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreDataPropertyInLiteral( ...@@ -821,7 +821,6 @@ BytecodeArrayBuilder& BytecodeArrayBuilder::StoreDataPropertyInLiteral(
} }
BytecodeArrayBuilder& BytecodeArrayBuilder::CollectTypeProfile(int position) { BytecodeArrayBuilder& BytecodeArrayBuilder::CollectTypeProfile(int position) {
DCHECK(FLAG_type_profile);
OutputCollectTypeProfile(position); OutputCollectTypeProfile(position);
return *this; return *this;
} }
......
...@@ -448,6 +448,7 @@ typedef std::vector<HeapObject*> DebugObjectCache; ...@@ -448,6 +448,7 @@ typedef std::vector<HeapObject*> DebugObjectCache;
V(bool, needs_side_effect_check, false) \ V(bool, needs_side_effect_check, false) \
/* Current code coverage mode */ \ /* Current code coverage mode */ \
V(debug::Coverage::Mode, code_coverage_mode, debug::Coverage::kBestEffort) \ V(debug::Coverage::Mode, code_coverage_mode, debug::Coverage::kBestEffort) \
V(debug::TypeProfile::Mode, type_profile_mode, debug::TypeProfile::kNone) \
V(int, last_stack_frame_info_id, 0) \ V(int, last_stack_frame_info_id, 0) \
V(int, last_console_context_id, 0) \ V(int, last_console_context_id, 0) \
ISOLATE_INIT_SIMULATOR_LIST(V) ISOLATE_INIT_SIMULATOR_LIST(V)
...@@ -1041,6 +1042,10 @@ class Isolate { ...@@ -1041,6 +1042,10 @@ class Isolate {
return is_block_count_code_coverage() || is_block_binary_code_coverage(); return is_block_count_code_coverage() || is_block_binary_code_coverage();
} }
bool is_collecting_type_profile() const {
return type_profile_mode() == debug::TypeProfile::kCollect;
}
void SetCodeCoverageList(Object* value); void SetCodeCoverageList(Object* value);
double time_millis_since_init() { double time_millis_since_init() {
......
...@@ -72,9 +72,10 @@ ParseInfo::ParseInfo(Handle<SharedFunctionInfo> shared) ...@@ -72,9 +72,10 @@ ParseInfo::ParseInfo(Handle<SharedFunctionInfo> shared)
// FeedbackMetadata, we can only collect type profile if the feedback vector // FeedbackMetadata, we can only collect type profile if the feedback vector
// has the appropriate slots. // has the appropriate slots.
set_collect_type_profile( set_collect_type_profile(
shared->feedback_metadata()->length() == 0 isolate->is_collecting_type_profile() &&
? FLAG_type_profile && script->IsUserJavaScript() (shared->feedback_metadata()->length() == 0
: shared->feedback_metadata()->HasTypeProfileSlot()); ? script->IsUserJavaScript()
: shared->feedback_metadata()->HasTypeProfileSlot()));
if (block_coverage_enabled() && script->IsUserJavaScript()) { if (block_coverage_enabled() && script->IsUserJavaScript()) {
AllocateSourceRangeMap(); AllocateSourceRangeMap();
} }
...@@ -91,7 +92,8 @@ ParseInfo::ParseInfo(Handle<Script> script) ...@@ -91,7 +92,8 @@ ParseInfo::ParseInfo(Handle<Script> script)
set_native(script->type() == Script::TYPE_NATIVE); set_native(script->type() == Script::TYPE_NATIVE);
set_eval(script->compilation_type() == Script::COMPILATION_TYPE_EVAL); set_eval(script->compilation_type() == Script::COMPILATION_TYPE_EVAL);
set_collect_type_profile(FLAG_type_profile && script->IsUserJavaScript()); set_collect_type_profile(script->GetIsolate()->is_collecting_type_profile() &&
script->IsUserJavaScript());
if (block_coverage_enabled() && script->IsUserJavaScript()) { if (block_coverage_enabled() && script->IsUserJavaScript()) {
AllocateSourceRangeMap(); AllocateSourceRangeMap();
} }
...@@ -154,6 +156,9 @@ void ParseInfo::InitFromIsolate(Isolate* isolate) { ...@@ -154,6 +156,9 @@ void ParseInfo::InitFromIsolate(Isolate* isolate) {
if (FLAG_block_coverage && isolate->is_block_code_coverage()) { if (FLAG_block_coverage && isolate->is_block_code_coverage()) {
set_block_coverage_enabled(); set_block_coverage_enabled();
} }
if (isolate->is_collecting_type_profile()) {
set_collect_type_profile();
}
} }
void ParseInfo::UpdateStatisticsAfterBackgroundParse(Isolate* isolate) { void ParseInfo::UpdateStatisticsAfterBackgroundParse(Isolate* isolate) {
......
...@@ -811,12 +811,14 @@ RUNTIME_FUNCTION(Runtime_CollectTypeProfile) { ...@@ -811,12 +811,14 @@ RUNTIME_FUNCTION(Runtime_CollectTypeProfile) {
CONVERT_ARG_HANDLE_CHECKED(Object, value, 1); CONVERT_ARG_HANDLE_CHECKED(Object, value, 1);
CONVERT_ARG_HANDLE_CHECKED(FeedbackVector, vector, 2); CONVERT_ARG_HANDLE_CHECKED(FeedbackVector, vector, 2);
DCHECK(FLAG_type_profile);
Handle<String> type = Object::TypeOf(isolate, value); Handle<String> type = Object::TypeOf(isolate, value);
if (value->IsJSReceiver()) { if (value->IsJSReceiver()) {
Handle<JSReceiver> object = Handle<JSReceiver>::cast(value); Handle<JSReceiver> object = Handle<JSReceiver>::cast(value);
type = JSReceiver::GetConstructorName(object); type = JSReceiver::GetConstructorName(object);
} else if (value->IsNull(isolate)) {
// typeof(null) is object. But it's more user-friendly to annotate
// null as type "null".
type = Handle<String>(isolate->heap()->null_string());
} }
DCHECK(vector->metadata()->HasTypeProfileSlot()); DCHECK(vector->metadata()->HasTypeProfileSlot());
......
...@@ -185,10 +185,6 @@ RUNTIME_FUNCTION(Runtime_TypeProfile) { ...@@ -185,10 +185,6 @@ RUNTIME_FUNCTION(Runtime_TypeProfile) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(1, args.length()); DCHECK_EQ(1, args.length());
if (!FLAG_type_profile) {
return isolate->heap()->undefined_value();
}
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0);
if (function->has_feedback_vector()) { if (function->has_feedback_vector()) {
FeedbackVector* vector = function->feedback_vector(); FeedbackVector* vector = function->feedback_vector();
......
...@@ -226,7 +226,7 @@ InspectorTest.runTestSuite([ ...@@ -226,7 +226,7 @@ InspectorTest.runTestSuite([
{ {
// Enabling the debugger holds onto script objects even though its // Enabling the debugger holds onto script objects even though its
// functions can be garbage collected. We would get empty ScriptCoverage // functions can be garbage collected. We would get empty ScriptCoverage
// entires unless we remove them. // entries unless we remove them.
Protocol.Debugger.enable() Protocol.Debugger.enable()
.then(Protocol.Runtime.enable) .then(Protocol.Runtime.enable)
.then(() => Protocol.Runtime.compileScript({ expression: source, sourceURL: arguments.callee.name, persistScript: true })) .then(() => Protocol.Runtime.compileScript({ expression: source, sourceURL: arguments.callee.name, persistScript: true }))
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
'./runtime/', './runtime/',
'./sessions/', './sessions/',
'./testcfg.py', './testcfg.py',
'./type-profiler/',
'../../src/inspector/injected-script-source.js', '../../src/inspector/injected-script-source.js',
'<(PRODUCT_DIR)/inspector-test<(EXECUTABLE_SUFFIX)', '<(PRODUCT_DIR)/inspector-test<(EXECUTABLE_SUFFIX)',
], ],
......
...@@ -277,6 +277,25 @@ InspectorTest.Session = class { ...@@ -277,6 +277,25 @@ InspectorTest.Session = class {
} }
} }
async logTypeProfile(typeProfile, source) {
let entries = typeProfile.entries;
// Sort in reverse order so we can replace entries without invalidating
// the other offsets.
entries = entries.sort((a, b) => b.offset - a.offset);
for (let entry of entries) {
source = source.slice(0, entry.offset) + typeAnnotation(entry.types) +
source.slice(entry.offset);
}
InspectorTest.log(source);
return typeProfile;
function typeAnnotation(types) {
return `/*${types.map(t => t.name).join(', ')}*/`;
}
}
logAsyncStackTrace(asyncStackTrace) { logAsyncStackTrace(asyncStackTrace) {
while (asyncStackTrace) { while (asyncStackTrace) {
if (asyncStackTrace.promiseCreationFrame) { if (asyncStackTrace.promiseCreationFrame) {
......
Turn Profiler.startTypeProfile on and off.
function g(/*Object*/a, /*Array*/b, /*null*/c) {
return 'bye';
/*string*/};
g({}, [], null);
[
]
\ No newline at end of file
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --type-profile
const source =
`
function g(a, b, c) {
return 'bye';
};
g({}, [], null);
`;
let {session, contextGroup, Protocol} = InspectorTest.start("Turn " +
"Profiler.startTypeProfile on and off.");
(async function testTypeProfile() {
Protocol.Runtime.enable();
let {result: {scriptId}} = await Protocol.Runtime.compileScript({
expression: source,
sourceURL: arguments.callee.name, persistScript: true
});
await Protocol.Profiler.enable();
// Start, run, take.
await Protocol.Profiler.startTypeProfile();
Protocol.Runtime.runScript({scriptId});
let typeProfiles = await Protocol.Profiler.takeTypeProfile();
session.logTypeProfile(typeProfiles.result.result[0],
source);
// This should delete all data.
Protocol.Profiler.stopTypeProfile();
await Protocol.Profiler.startTypeProfile();
typeProfiles = await Protocol.Profiler.takeTypeProfile();
// Should be empty because no code was run since start.
InspectorTest.logMessage(typeProfiles.result.result);
Protocol.Profiler.stopTypeProfile();
Protocol.Profiler.disable();
await Protocol.Runtime.disable();
InspectorTest.completeTest();
})();
Test collecting type profile data with Profiler.takeTypeProfile.
function f(/*Object, number, undefined*/a, /*Array, number, null*/b, /*boolean, Object, symbol*/c) {
return 'bye';
/*string*/};
f({}, [], true);
f(3, 2.3, {a: 42});
f(undefined, null, Symbol('hello'));/*string*/
Turn Profiler.startTypeProfile on and off.
Running test: testTypeProfile
function g(/*Object*/a, /*Array*/b, /*null*/c) {
return 'first';
/*string*/};
g({}, [], null);
Running test: testTypeProfileFromDifferentSource
function f(/*null*/a) {
return 'second';
/*string*/};
f(null);
Running test: testStopTypeProfileDeletesFeedback
[
]
Running test: testTypeProfileWithoutStartingItFirst
Type profile has not been started.
Running test: testTypeProfileAfterStoppingIt
Type profile has not been started.
Running test: testStartTypeProfileAfterRunning
{
id : <messageId>
result : {
result : [
]
}
}
Running test: testTypeProfileForTwoSources
function g(/*Object*/a, /*Array*/b, /*null*/c) {
return 'first';
/*string*/};
g({}, [], null);
function f(/*null*/a) {
return 'second';
/*string*/};
f(null);
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --type-profile
const source1 =
`
function g(a, b, c) {
return 'first';
};
g({}, [], null);
`;
const source2 =
`
function f(a) {
return 'second';
};
f(null);
`;
let {session, contextGroup, Protocol} = InspectorTest.start("Turn " +
"Profiler.startTypeProfile on and off.");
InspectorTest.runAsyncTestSuite([
async function testTypeProfile() {
Protocol.Runtime.enable();
let {result: {scriptId}} = await Protocol.Runtime.compileScript({
expression: source1,
sourceURL: arguments.callee.name, persistScript: true
});
await Protocol.Profiler.enable();
// Start, run, take.
await Protocol.Profiler.startTypeProfile();
Protocol.Runtime.runScript({scriptId});
let typeProfiles = await Protocol.Profiler.takeTypeProfile();
await session.logTypeProfile(typeProfiles.result.result[0],
source1);
Protocol.Profiler.stopTypeProfile();
Protocol.Profiler.disable();
await Protocol.Runtime.disable();
},
async function testTypeProfileFromDifferentSource() {
Protocol.Runtime.enable();
let {result: {scriptId}} = await Protocol.Runtime.compileScript({
expression: source2,
sourceURL: arguments.callee.name, persistScript: true
});
await Protocol.Profiler.enable();
// Start, run different script, take.
await Protocol.Profiler.startTypeProfile();
Protocol.Runtime.runScript({scriptId});
let typeProfiles = await Protocol.Profiler.takeTypeProfile();
await session.logTypeProfile(typeProfiles.result.result[0],
source2);
Protocol.Profiler.stopTypeProfile();
Protocol.Profiler.disable();
await Protocol.Runtime.disable();
},
async function testStopTypeProfileDeletesFeedback() {
Protocol.Runtime.enable();
let {result: {scriptId}} = await Protocol.Runtime.compileScript({
expression: source1,
sourceURL: arguments.callee.name, persistScript: true
});
await Protocol.Profiler.enable();
// Start, run, stop.
await Protocol.Profiler.startTypeProfile();
Protocol.Runtime.runScript({scriptId});
await Protocol.Profiler.stopTypeProfile();
// Start, take. Should be empty, because no code was run.
await Protocol.Profiler.startTypeProfile();
let typeProfiles = await Protocol.Profiler.takeTypeProfile();
InspectorTest.logMessage(typeProfiles.result.result);
await Protocol.Profiler.stopTypeProfile();
Protocol.Profiler.disable();
await Protocol.Runtime.disable();
},
async function testTypeProfileWithoutStartingItFirst() {
Protocol.Runtime.enable();
let {result: {scriptId}} = await Protocol.Runtime.compileScript({ expression: source1,
sourceURL: arguments.callee.name, persistScript: true });
Protocol.Runtime.runScript({ scriptId });
await Protocol.Profiler.enable();
// This should return an error because type profile was never started.
let typeProfiles = await Protocol.Profiler.takeTypeProfile();
InspectorTest.logObject(typeProfiles.error.message);
Protocol.Profiler.disable();
await Protocol.Runtime.disable();
},
async function testTypeProfileAfterStoppingIt() {
Protocol.Runtime.enable();
let {result: {scriptId}} = await Protocol.Runtime.compileScript({ expression: source1,
sourceURL: arguments.callee.name, persistScript: true });
Protocol.Runtime.runScript({ scriptId });
await Protocol.Profiler.enable();
await Protocol.Profiler.startTypeProfile();
// Make sure that this turns off type profile.
await Protocol.Profiler.stopTypeProfile();
// This should return an error because type profile was stopped.
let typeProfiles = await Protocol.Profiler.takeTypeProfile();
InspectorTest.logObject(typeProfiles.error.message);
Protocol.Profiler.disable();
await Protocol.Runtime.disable();
},
async function testStartTypeProfileAfterRunning() {
Protocol.Runtime.enable();
let {result: {scriptId}} = await Protocol.Runtime.compileScript({
expression: source1,
sourceURL: arguments.callee.name, persistScript: true
});
Protocol.Runtime.runScript({scriptId});
await Protocol.Profiler.enable();
await Protocol.Profiler.startTypeProfile();
let typeProfiles = await Protocol.Profiler.takeTypeProfile();
// This should be empty because type profile was started after compilation.
// Only the outer script is annotated with return value "string" because
// that does not depend on runScript().
InspectorTest.logMessage(typeProfiles);
Protocol.Profiler.stopTypeProfile();
Protocol.Profiler.disable();
await Protocol.Runtime.disable();
},
async function testTypeProfileForTwoSources() {
Protocol.Runtime.enable();
let {result: {scriptId: scriptId1}} = await Protocol.Runtime.compileScript({
expression: source1,
sourceURL: arguments.callee.name, persistScript: true
});
let {result: {scriptId: scriptId2}} = await Protocol.Runtime.compileScript({
expression: source2,
sourceURL: arguments.callee.name, persistScript: true
});
await Protocol.Profiler.enable();
// Start, run different script, take.
await Protocol.Profiler.startTypeProfile();
Protocol.Runtime.runScript({scriptId: scriptId1});
Protocol.Runtime.runScript({scriptId: scriptId2});
let typeProfiles = await Protocol.Profiler.takeTypeProfile();
await session.logTypeProfile(typeProfiles.result.result[0],
source1);
await session.logTypeProfile(typeProfiles.result.result[1],
source2);
Protocol.Profiler.stopTypeProfile();
Protocol.Profiler.disable();
await Protocol.Runtime.disable();
}
]);
Test collecting type profile data with Profiler.takeTypeProfile.
function f(/*number*/n) {
/*undefined*/};
f(5);
function g(/*Object, number*/a, /*Array, number*/b, /*Flower, Object*/c) {
return 'bye';
/*string*/};
/*undefined*/class Tree {};
/*Flower*/class Flower extends Tree{};
var f = new Flower();
f.constructor = {};
f.constructor.name = "Not a flower.";
g({}, [], f);
g(3, 2.3, {a: 42});/*string*/
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --type-profile
const source =
`
function f(n) {
};
f(5);
function g(a, b, c) {
return 'bye';
};
class Tree {};
class Flower extends Tree{};
var f = new Flower();
f.constructor = {};
f.constructor.name = "Not a flower.";
g({}, [], f);
g(3, 2.3, {a: 42});
`;
let {session, contextGroup, Protocol} = InspectorTest.start("Test collecting type profile data with Profiler.takeTypeProfile.");
(async function testTypeProfile(next) {
await Protocol.Profiler.enable();
await Protocol.Profiler.startTypeProfile();
Protocol.Runtime.enable();
let {result: {scriptId}} = await Protocol.Runtime.compileScript({ expression: source,
sourceURL: arguments.callee.name, persistScript: true });
Protocol.Runtime.runScript({ scriptId });
let typeProfiles = await Protocol.Profiler.takeTypeProfile();
await session.logTypeProfile(typeProfiles.result.result[0],
source);
Protocol.Profiler.disable();
await Protocol.Runtime.disable();
InspectorTest.completeTest();
})();
Test collecting type profile data with Profiler.takeTypeProfile.
function g(/*Object, number*/a, /*Array, number*/b, /*Dog, Object*/c) {
return 'bye';
/*string*/};
/*undefined*/class Tree {};
/*Flower*/class Flower extends Tree{};
var f = new Flower();
// We store the type when a variable is used. If a toStringTag is
// changes the type, we want to collect that changed feedback.
// This tests ensures that we collect that information rather than
// for example infer the types from the internal map, which wouldn't
// know about a toStringTag.
f[Symbol.toStringTag] = 'Dog';
g({}, [], f);
g(3, 2.3, {a: 42});/*string*/
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --type-profile
const source =
`
function g(a, b, c) {
return 'bye';
};
class Tree {};
class Flower extends Tree{};
var f = new Flower();
// We store the type when a variable is used. If a toStringTag is
// changes the type, we want to collect that changed feedback.
// This tests ensures that we collect that information rather than
// for example infer the types from the internal map, which wouldn't
// know about a toStringTag.
f[Symbol.toStringTag] = 'Dog';
g({}, [], f);
g(3, 2.3, {a: 42});
`;
let {session, contextGroup, Protocol} = InspectorTest.start("Test collecting " +
"type profile data with Profiler.takeTypeProfile.");
(async function testTypeProfile() {
await Protocol.Profiler.enable();
await Protocol.Profiler.startTypeProfile();
Protocol.Runtime.enable();
let {result: {scriptId}} = await Protocol.Runtime.compileScript({ expression: source,
sourceURL: arguments.callee.name, persistScript: true });
Protocol.Runtime.runScript({ scriptId });
await Protocol.Profiler.startTypeProfile();
let typeProfiles = await Protocol.Profiler.takeTypeProfile();
await session.logTypeProfile(typeProfiles.result.result[0],
source);
Protocol.Profiler.stopTypeProfile();
Protocol.Profiler.disable();
await Protocol.Runtime.disable();
InspectorTest.completeTest();
})();
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --type-profile
const source =
`
function f(a, b, c) {
return 'bye';
};
f({}, [], true);
f(3, 2.3, {a: 42});
f(undefined, null, Symbol('hello'));
`;
let {session, contextGroup, Protocol} = InspectorTest.start("Test collecting type profile data with Profiler.takeTypeProfile.");
(async function testTypeProfile() {
await Protocol.Profiler.enable();
await Protocol.Profiler.startTypeProfile();
Protocol.Runtime.enable();
let {result: {scriptId}} = await Protocol.Runtime.compileScript({
expression: source,
sourceURL: arguments.callee.name,
persistScript: true
});
Protocol.Runtime.runScript({ scriptId });
let typeProfiles = await Protocol.Profiler.takeTypeProfile();
await session.logTypeProfile(typeProfiles.result.result[0],
source);
Protocol.Profiler.stoptTypeProfile();
Protocol.Profiler.disable();
await Protocol.Runtime.disable();
InspectorTest.completeTest();
})();
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --type-profile --allow-natives-syntax
function check_collect_types(name, expected) {
const type_profile = %TypeProfile(name);
if (type_profile !== undefined) {
const result = JSON.stringify(type_profile);
print(result);
assertEquals(expected, result, name + " failed");
}
}
function testFunction(param, flag) {
// We want to test 2 different return positions in one function.
if (flag) {
var first_var = param;
return first_var;
}
var second_var = param;
return second_var;
}
class MyClass {
constructor() {}
}
var expected = `{}`;
check_collect_types(testFunction, expected);
testFunction({});
testFunction(123, true);
testFunction('hello');
testFunction(123);
expected = `{\"495\":[\"Object\",\"number\",\"string\",\"number\"],\"502\":[\"undefined\",\"boolean\",\"undefined\",\"undefined\"],\"691\":[\"Object\",\"number\",\"string\",\"number\"]}`;
check_collect_types(testFunction, expected);
testFunction(undefined);
testFunction('hello', true);
testFunction({x: 12}, true);
testFunction({x: 12});
testFunction(new MyClass());
expected = `{\"495\":[\"Object\",\"number\",\"string\",\"number\",\"undefined\",\"string\",\"Object\",\"Object\",\"MyClass\"],\"502\":[\"undefined\",\"boolean\",\"undefined\",\"undefined\",\"undefined\",\"boolean\",\"boolean\",\"undefined\",\"undefined\"],\"691\":[\"Object\",\"number\",\"string\",\"number\",\"undefined\",\"string\",\"Object\",\"Object\",\"MyClass\"]}`;
check_collect_types(testFunction, expected);
function testReturnOfNonVariable() {
return 32;
}
testReturnOfNonVariable();
expected = `{\"1724\":[\"number\"]}`;
check_collect_types(testReturnOfNonVariable, expected);
// Return statement is reached but its expression is never really returned.
function try_finally() {
try {
return 23;
} finally {
return "nope, string is better"
}
}
try_finally();
expected = `{\"2026\":[\"string\"]}`;
check_collect_types(try_finally, expected);
// Fall-off return.
function fall_off() {
//nothing
}
fall_off();
expected = `{\"2180\":[\"undefined\"]}`;
check_collect_types(fall_off, expected);
// Do not collect types when the function is never run.
function never_called() {}
expected = `{}`;
check_collect_types(never_called, expected);
function several_params(a, b, c, d) {
//nothing
}
several_params(2, 'foo', {}, new MyClass());
expected = `{\"2448\":[\"number\"],\"2451\":[\"string\"],\"2454\":[\"Object\"],\"2457\":[\"MyClass\"],\"2474\":[\"undefined\"]}`;
check_collect_types(several_params, expected);
...@@ -421,11 +421,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) { ...@@ -421,11 +421,9 @@ TEST_F(BytecodeArrayBuilderTest, AllBytecodesGenerated) {
// Insert entry for illegal bytecode as this is never willingly emitted. // Insert entry for illegal bytecode as this is never willingly emitted.
scorecard[Bytecodes::ToByte(Bytecode::kIllegal)] = 1; scorecard[Bytecodes::ToByte(Bytecode::kIllegal)] = 1;
if (!FLAG_type_profile) { // Bytecode for CollectTypeProfile is only emitted when
// Bytecode for CollectTypeProfile is only emitted when // Type Information for DevTools is turned on.
// Type Information for DevTools is turned on. scorecard[Bytecodes::ToByte(Bytecode::kCollectTypeProfile)] = 1;
scorecard[Bytecodes::ToByte(Bytecode::kCollectTypeProfile)] = 1;
}
// Check return occurs at the end and only once in the BytecodeArray. // Check return occurs at the end and only once in the BytecodeArray.
CHECK_EQ(final_bytecode, Bytecode::kReturn); CHECK_EQ(final_bytecode, Bytecode::kReturn);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment