Commit 3dfaf826 authored by Alexey Kozyatinskiy's avatar Alexey Kozyatinskiy Committed by Commit Bot

[debug] liveedit in native

Liveedit step-by-step:
1. calculate diff between old source and new source,
2. map function literals from old source to new source,
3. create new script for new_source,
4. mark literals with changed code as changed, all others as unchanged,
5. check that for changed literals there are no:
  - running generators in the heap,
  - non droppable frames (e.g. running generator) above them on stack.
6. mark the bottom most frame with changed function as scheduled for
   restart if any.
7. for unchanged functions:
  - deoptimize,
  - remove from cache,
  - update source positions,
  - move to new script,
  - reset feedback information and preparsed scope information if any,
  - replace any sfi in constant pool with changed one if any.
8. for changed functions:
  - deoptimize
  - remove from cache,
  - reset feedback information,
  - update all links from js functions to old shared with new one.
9. swap scripts.

TBR=ulan@chromium.org

Bug: v8:7862,v8:5713
Cq-Include-Trybots: luci.chromium.try:linux_chromium_headless_rel;luci.chromium.try:linux_chromium_rel_ng;master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I8f6f6156318cc82d6f36d7ebc1c9f7d5f3aa1461
Reviewed-on: https://chromium-review.googlesource.com/1105493Reviewed-by: 's avatarAleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Reviewed-by: 's avatarDmitry Gozman <dgozman@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54146}
parent e04b1986
......@@ -661,7 +661,6 @@ action("js2c") {
"src/js/prologue.js",
"src/js/array.js",
"src/js/typedarray.js",
"src/debug/liveedit.js",
]
outputs = [
......@@ -2289,7 +2288,6 @@ v8_source_set("v8_base") {
"src/runtime/runtime-interpreter.cc",
"src/runtime/runtime-intl.cc",
"src/runtime/runtime-literals.cc",
"src/runtime/runtime-liveedit.cc",
"src/runtime/runtime-maths.cc",
"src/runtime/runtime-module.cc",
"src/runtime/runtime-numbers.cc",
......
......@@ -1204,37 +1204,9 @@ bool Compiler::CompileOptimized(Handle<JSFunction> function,
return true;
}
MaybeHandle<JSArray> Compiler::CompileForLiveEdit(Handle<Script> script) {
Isolate* isolate = script->GetIsolate();
DCHECK(AllowCompilation::IsAllowed(isolate));
// In order to ensure that live edit function info collection finds the newly
// generated shared function infos, clear the script's list temporarily
// and restore it at the end of this method.
Handle<WeakFixedArray> old_function_infos(script->shared_function_infos(),
isolate);
script->set_shared_function_infos(isolate->heap()->empty_weak_fixed_array());
// Start a compilation.
ParseInfo parse_info(isolate, script);
parse_info.set_eager();
// TODO(635): support extensions.
Handle<JSArray> infos;
Handle<SharedFunctionInfo> shared_info;
if (CompileToplevel(&parse_info, isolate).ToHandle(&shared_info)) {
// Check postconditions on success.
DCHECK(!isolate->has_pending_exception());
infos = LiveEditFunctionTracker::Collect(parse_info.literal(), script,
parse_info.zone(), isolate);
}
// Restore the original function info list in order to remain side-effect
// free as much as possible, since some code expects the old shared function
// infos to stick around.
script->set_shared_function_infos(*old_function_infos);
return infos;
MaybeHandle<SharedFunctionInfo> Compiler::CompileForLiveEdit(
ParseInfo* parse_info, Isolate* isolate) {
return CompileToplevel(parse_info, isolate);
}
MaybeHandle<JSFunction> Compiler::GetFunctionFromEval(
......
......@@ -57,7 +57,9 @@ class V8_EXPORT_PRIVATE Compiler : public AllStatic {
ClearExceptionFlag flag);
static bool Compile(Handle<JSFunction> function, ClearExceptionFlag flag);
static bool CompileOptimized(Handle<JSFunction> function, ConcurrencyMode);
static MaybeHandle<JSArray> CompileForLiveEdit(Handle<Script> script);
V8_WARN_UNUSED_RESULT static MaybeHandle<SharedFunctionInfo>
CompileForLiveEdit(ParseInfo* parse_info, Isolate* isolate);
// Creates a new task that when run will parse and compile the streamed
// script associated with |streaming_data| and can be finalized with
......
......@@ -79,6 +79,8 @@ void BreakRightNow(Isolate* isolate);
bool AllFramesOnStackAreBlackboxed(Isolate* isolate);
class Script;
struct LiveEditResult {
enum Status {
OK,
......@@ -92,6 +94,8 @@ struct LiveEditResult {
};
Status status = OK;
bool stack_changed = false;
// Available only for OK.
v8::Local<v8::debug::Script> script;
// Fields below are available only for COMPILE_ERROR.
v8::Local<v8::String> message;
int line_number = -1;
......
......@@ -1910,62 +1910,12 @@ bool Debug::CanBreakAtEntry(Handle<SharedFunctionInfo> shared) {
}
bool Debug::SetScriptSource(Handle<Script> script, Handle<String> source,
bool preview, debug::LiveEditResult* output) {
SaveContext save(isolate_);
StackFrame::Id frame_id = break_frame_id();
bool preview, debug::LiveEditResult* result) {
DebugScope debug_scope(this);
if (debug_scope.failed()) return false;
isolate_->set_context(*debug_context());
if (frame_id != StackFrame::NO_ID) {
thread_local_.break_frame_id_ = frame_id;
}
Handle<Object> script_wrapper = Script::GetWrapper(script);
Handle<Object> argv[] = {script_wrapper, source,
isolate_->factory()->ToBoolean(preview),
isolate_->factory()->NewJSArray(0)};
Handle<Object> result;
MaybeHandle<Object> maybe_exception;
running_live_edit_ = true;
if (!CallFunction("SetScriptSource", arraysize(argv), argv, &maybe_exception)
.ToHandle(&result)) {
Handle<Object> pending_exception = maybe_exception.ToHandleChecked();
if (pending_exception->IsJSObject()) {
Handle<JSObject> exception = Handle<JSObject>::cast(pending_exception);
Handle<String> message = Handle<String>::cast(
JSReceiver::GetProperty(isolate_, exception, "message")
.ToHandleChecked());
Handle<String> blocked_message =
isolate_->factory()->NewStringFromAsciiChecked(
"Blocked by functions on stack");
if (blocked_message->Equals(*message)) {
output->status = debug::LiveEditResult::
BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME;
} else {
Handle<JSObject> details = Handle<JSObject>::cast(
JSReceiver::GetProperty(isolate_, exception, "details")
.ToHandleChecked());
Handle<String> error = Handle<String>::cast(
JSReceiver::GetProperty(isolate_, details, "syntaxErrorMessage")
.ToHandleChecked());
output->status = debug::LiveEditResult::COMPILE_ERROR;
output->line_number = kNoSourcePosition;
output->column_number = kNoSourcePosition;
output->message = Utils::ToLocal(error);
}
}
running_live_edit_ = false;
return false;
}
Handle<Object> stack_changed_value =
JSReceiver::GetProperty(isolate_, Handle<JSObject>::cast(result),
"stack_modified")
.ToHandleChecked();
output->stack_changed = stack_changed_value->IsTrue(isolate_);
output->status = debug::LiveEditResult::OK;
LiveEdit::PatchScript(isolate_, script, source, preview, result);
running_live_edit_ = false;
return true;
return result->status == debug::LiveEditResult::OK;
}
void Debug::OnCompileError(Handle<Script> script) {
......@@ -1977,6 +1927,9 @@ void Debug::OnAfterCompile(Handle<Script> script) {
}
void Debug::ProcessCompileEvent(bool has_compile_error, Handle<Script> script) {
// TODO(kozyatinskiy): teach devtools to work with liveedit scripts better
// first and then remove this fast return.
if (running_live_edit_) return;
// Attach the correct debug id to the script. The debug id is used by the
// inspector to filter scripts by native context.
script->set_context_data(isolate_->native_context()->debug_context_id());
......@@ -1996,7 +1949,6 @@ void Debug::ProcessCompileEvent(bool has_compile_error, Handle<Script> script) {
running_live_edit_, has_compile_error);
}
Handle<Context> Debug::GetDebugContext() {
if (!is_loaded()) return Handle<Context>();
DebugScope debug_scope(this);
......
......@@ -396,6 +396,8 @@ class Debug {
// source position for break points.
static const int kBreakAtEntryPosition = 0;
void RemoveBreakInfoAndMaybeFree(Handle<DebugInfo> debug_info);
private:
explicit Debug(Isolate* isolate);
~Debug();
......@@ -474,7 +476,6 @@ class Debug {
typedef std::function<void(Handle<DebugInfo>)> DebugInfoClearFunction;
void ClearAllDebugInfos(DebugInfoClearFunction clear_function);
void RemoveBreakInfoAndMaybeFree(Handle<DebugInfo> debug_info);
void FindDebugInfo(Handle<DebugInfo> debug_info, DebugInfoListNode** prev,
DebugInfoListNode** curr);
void FreeDebugInfoListNode(DebugInfoListNode* prev, DebugInfoListNode* node);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -2149,47 +2149,5 @@ InnerPointerToCodeCache::InnerPointerToCodeCacheEntry*
}
return entry;
}
// -------------------------------------------------------------------------
#define DEFINE_WRAPPER(type, field) \
class field##_Wrapper : public ZoneObject { \
public: /* NOLINT */ \
field##_Wrapper(const field& original) : frame_(original) { \
} \
field frame_; \
};
STACK_FRAME_TYPE_LIST(DEFINE_WRAPPER)
#undef DEFINE_WRAPPER
static StackFrame* AllocateFrameCopy(StackFrame* frame, Zone* zone) {
#define FRAME_TYPE_CASE(type, field) \
case StackFrame::type: { \
field##_Wrapper* wrapper = \
new(zone) field##_Wrapper(*(reinterpret_cast<field*>(frame))); \
return &wrapper->frame_; \
}
switch (frame->type()) {
STACK_FRAME_TYPE_LIST(FRAME_TYPE_CASE)
default: UNREACHABLE();
}
#undef FRAME_TYPE_CASE
return nullptr;
}
Vector<StackFrame*> CreateStackMap(Isolate* isolate, Zone* zone) {
ZoneVector<StackFrame*> frames(zone);
for (StackFrameIterator it(isolate); !it.done(); it.Advance()) {
StackFrame* frame = AllocateFrameCopy(it.frame(), zone);
frames.push_back(frame);
}
return Vector<StackFrame*>(frames.data(), frames.size());
}
} // namespace internal
} // namespace v8
......@@ -1323,11 +1323,6 @@ class SafeStackFrameIterator: public StackFrameIteratorBase {
StackFrame::Type top_frame_type_;
ExternalCallbackScope* external_callback_scope_;
};
// Reads all frames on the current stack and copies them into the current
// zone memory.
Vector<StackFrame*> CreateStackMap(Isolate* isolate, Zone* zone);
} // namespace internal
} // namespace v8
......
......@@ -1567,6 +1567,33 @@ Handle<Script> Factory::NewScriptWithId(Handle<String> source, int script_id,
return script;
}
Handle<Script> Factory::CloneScript(Handle<Script> script) {
Heap* heap = isolate()->heap();
int script_id = isolate()->heap()->NextScriptId();
Handle<Script> new_script =
Handle<Script>::cast(NewStruct(SCRIPT_TYPE, TENURED));
new_script->set_source(script->source());
new_script->set_name(script->name());
new_script->set_id(script_id);
new_script->set_line_offset(script->line_offset());
new_script->set_column_offset(script->column_offset());
new_script->set_context_data(script->context_data());
new_script->set_type(script->type());
new_script->set_wrapper(script->wrapper());
new_script->set_line_ends(heap->undefined_value());
new_script->set_eval_from_shared_or_wrapped_arguments(
script->eval_from_shared_or_wrapped_arguments());
new_script->set_shared_function_infos(*empty_weak_fixed_array(),
SKIP_WRITE_BARRIER);
new_script->set_eval_from_position(script->eval_from_position());
new_script->set_flags(script->flags());
new_script->set_host_defined_options(script->host_defined_options());
heap->set_script_list(
*FixedArrayOfWeakCells::Add(isolate(), script_list(), new_script));
LOG(isolate(), ScriptEvent(Logger::ScriptEventType::kCreate, script_id));
return new_script;
}
Handle<CallableTask> Factory::NewCallableTask(Handle<JSReceiver> callable,
Handle<Context> context) {
DCHECK(callable->IsCallable());
......
......@@ -408,6 +408,7 @@ class V8_EXPORT_PRIVATE Factory {
PretenureFlag tenure = TENURED);
Handle<Script> NewScriptWithId(Handle<String> source, int script_id,
PretenureFlag tenure = TENURED);
Handle<Script> CloneScript(Handle<Script> script);
Handle<BreakPointInfo> NewBreakPointInfo(int source_position);
Handle<BreakPoint> NewBreakPoint(int id, Handle<String> condition);
......
......@@ -114,37 +114,7 @@ class ActualScript : public V8DebuggerScript {
: V8DebuggerScript(isolate, String16::fromInteger(script->Id()),
GetNameOrSourceUrl(script)),
m_isLiveEdit(isLiveEdit) {
v8::Local<v8::String> tmp;
if (script->SourceURL().ToLocal(&tmp)) m_sourceURL = toProtocolString(tmp);
if (script->SourceMappingURL().ToLocal(&tmp))
m_sourceMappingURL = toProtocolString(tmp);
m_startLine = script->LineOffset();
m_startColumn = script->ColumnOffset();
std::vector<int> lineEnds = script->LineEnds();
CHECK(lineEnds.size());
int source_length = lineEnds[lineEnds.size() - 1];
if (lineEnds.size()) {
m_endLine = static_cast<int>(lineEnds.size()) + m_startLine - 1;
if (lineEnds.size() > 1) {
m_endColumn = source_length - lineEnds[lineEnds.size() - 2] - 1;
} else {
m_endColumn = source_length + m_startColumn;
}
} else {
m_endLine = m_startLine;
m_endColumn = m_startColumn;
}
USE(script->ContextId().To(&m_executionContextId));
if (script->Source().ToLocal(&tmp)) {
m_source = toProtocolString(tmp);
}
m_isModule = script->IsModule();
m_script.Reset(m_isolate, script);
m_script.AnnotateStrongRetainer(kGlobalDebuggerScriptHandleLabel);
Initialize(script);
}
bool isLiveEdit() const override { return m_isLiveEdit; }
......@@ -175,8 +145,8 @@ class ActualScript : public V8DebuggerScript {
return;
}
if (preview) return;
m_source = newSource;
m_hash = String16();
Initialize(scope.Escape(result->script));
}
bool getPossibleBreakpoints(
......@@ -259,6 +229,40 @@ class ActualScript : public V8DebuggerScript {
return m_script.Get(m_isolate);
}
void Initialize(v8::Local<v8::debug::Script> script) {
v8::Local<v8::String> tmp;
if (script->SourceURL().ToLocal(&tmp)) m_sourceURL = toProtocolString(tmp);
if (script->SourceMappingURL().ToLocal(&tmp))
m_sourceMappingURL = toProtocolString(tmp);
m_startLine = script->LineOffset();
m_startColumn = script->ColumnOffset();
std::vector<int> lineEnds = script->LineEnds();
CHECK(lineEnds.size());
int source_length = lineEnds[lineEnds.size() - 1];
if (lineEnds.size()) {
m_endLine = static_cast<int>(lineEnds.size()) + m_startLine - 1;
if (lineEnds.size() > 1) {
m_endColumn = source_length - lineEnds[lineEnds.size() - 2] - 1;
} else {
m_endColumn = source_length + m_startColumn;
}
} else {
m_endLine = m_startLine;
m_endColumn = m_startColumn;
}
USE(script->ContextId().To(&m_executionContextId));
if (script->Source().ToLocal(&tmp)) {
m_source = toProtocolString(tmp);
}
m_isModule = script->IsModule();
m_script.Reset(m_isolate, script);
m_script.AnnotateStrongRetainer(kGlobalDebuggerScriptHandleLabel);
}
String16 m_sourceMappingURL;
bool m_isLiveEdit = false;
bool m_isModule = false;
......
......@@ -899,7 +899,7 @@ RUNTIME_FUNCTION(Runtime_LiveEditPatchScript) {
Handle<Script> script(Script::cast(script_function->shared()->script()),
isolate);
v8::debug::LiveEditResult result;
LiveEdit::PatchScript(script, new_source, &result);
LiveEdit::PatchScript(isolate, script, new_source, false, &result);
switch (result.status) {
case v8::debug::LiveEditResult::COMPILE_ERROR:
return isolate->Throw(*isolate->factory()->NewStringFromAsciiChecked(
......
......@@ -287,19 +287,6 @@ namespace internal {
F(CreateObjectLiteral, 4, 1) \
F(CreateRegExpLiteral, 4, 1)
#define FOR_EACH_INTRINSIC_LIVEEDIT(F) \
F(LiveEditCheckAndDropActivations, 3, 1) \
F(LiveEditCompareStrings, 2, 1) \
F(LiveEditFindSharedFunctionInfosForScript, 1, 1) \
F(LiveEditFixupScript, 2, 1) \
F(LiveEditFunctionSetScript, 2, 1) \
F(LiveEditFunctionSourceUpdated, 2, 1) \
F(LiveEditGatherCompileInfo, 2, 1) \
F(LiveEditPatchFunctionPositions, 2, 1) \
F(LiveEditReplaceFunctionCode, 2, 1) \
F(LiveEditReplaceRefToNestedFunction, 3, 1) \
F(LiveEditReplaceScript, 3, 1)
#define FOR_EACH_INTRINSIC_MATHS(F) F(GenerateRandomNumbers, 0, 1)
#define FOR_EACH_INTRINSIC_MODULE(F) \
......@@ -624,7 +611,6 @@ namespace internal {
FOR_EACH_INTRINSIC_INTERPRETER(F) \
FOR_EACH_INTRINSIC_INTL(F) \
FOR_EACH_INTRINSIC_LITERALS(F) \
FOR_EACH_INTRINSIC_LIVEEDIT(F) \
FOR_EACH_INTRINSIC_MATHS(F) \
FOR_EACH_INTRINSIC_MODULE(F) \
FOR_EACH_INTRINSIC_NUMBERS(F) \
......
......@@ -203,7 +203,7 @@ void PatchFunctions(v8::Local<v8::Context> context, const char* source_a,
v8::debug::LiveEditResult* result = nullptr) {
v8::Isolate* isolate = context->GetIsolate();
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
v8::HandleScope scope(isolate);
v8::EscapableHandleScope scope(isolate);
v8::Local<v8::Script> script_a =
v8::Script::Compile(context, v8_str(isolate, source_a)).ToLocalChecked();
script_a->Run(context).ToLocalChecked();
......@@ -213,20 +213,24 @@ void PatchFunctions(v8::Local<v8::Context> context, const char* source_a,
if (result) {
LiveEdit::PatchScript(
i_script_a, i_isolate->factory()->NewStringFromAsciiChecked(source_b),
i_isolate, i_script_a,
i_isolate->factory()->NewStringFromAsciiChecked(source_b), false,
result);
if (result->status == v8::debug::LiveEditResult::COMPILE_ERROR) {
result->message = scope.Escape(result->message);
}
} else {
v8::debug::LiveEditResult result;
LiveEdit::PatchScript(
i_script_a, i_isolate->factory()->NewStringFromAsciiChecked(source_b),
i_isolate, i_script_a,
i_isolate->factory()->NewStringFromAsciiChecked(source_b), false,
&result);
CHECK_EQ(result.status, v8::debug::LiveEditResult::OK);
}
}
} // anonymous namespace
// TODO(kozyatinskiy): enable it with new liveedit implementation.
DISABLED_TEST(LiveEditPatchFunctions) {
TEST(LiveEditPatchFunctions) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Local<v8::Context> context = env.local();
......@@ -367,7 +371,6 @@ DISABLED_TEST(LiveEditPatchFunctions) {
->ToInt32(env->GetIsolate())
->Value(),
20);
// Change inner functions.
PatchFunctions(
context,
......@@ -417,10 +420,53 @@ DISABLED_TEST(LiveEditPatchFunctions) {
v8::String::Utf8Value new_result_utf8(env->GetIsolate(), result);
CHECK_NOT_NULL(strstr(*new_result_utf8, "cb"));
}
// TODO(kozyatinskiy): should work when we remove (.
PatchFunctions(context, "f = () => 2", "f = a => a");
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "f(3)")
->ToInt32(env->GetIsolate())
->Value(),
2);
// Replace function with not a function.
PatchFunctions(context, "f = () => 2", "f = a == 2");
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "f(3)")
->ToInt32(env->GetIsolate())
->Value(),
2);
// TODO(kozyatinskiy): should work when we put function into (...).
PatchFunctions(context, "f = a => 2", "f = (a => 5)()");
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "f()")
->ToInt32(env->GetIsolate())
->Value(),
2);
PatchFunctions(context,
"f2 = null;\n"
"f = () => {\n"
" f2 = () => 5;\n"
" return f2();\n"
"}\n"
"f()\n",
"f2 = null;\n"
"f = () => {\n"
" for (var a = (() => 7)(), b = 0; a < 10; ++a,++b);\n"
" return b;\n"
"}\n"
"f()\n");
// TODO(kozyatinskiy): ditto.
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "f2()")
->ToInt32(env->GetIsolate())
->Value(),
5);
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "f()")
->ToInt32(env->GetIsolate())
->Value(),
3);
}
// TODO(kozyatinskiy): enable it with new liveedit implementation.
DISABLED_TEST(LiveEditCompileError) {
TEST(LiveEditCompileError) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
v8::Local<v8::Context> context = env.local();
......@@ -452,10 +498,8 @@ DISABLED_TEST(LiveEditCompileError) {
PatchFunctions(context, "function foo() {}",
"function foo() { return a # b; }", &result);
CHECK_EQ(result.status, debug::LiveEditResult::COMPILE_ERROR);
// TODO(kozyatinskiy): should be 1.
CHECK_EQ(result.line_number, kNoSourcePosition);
// TODO(kozyatinskiy): should be 26.
CHECK_EQ(result.column_number, kNoSourcePosition);
CHECK_EQ(result.line_number, 1);
CHECK_EQ(result.column_number, 26);
}
TEST(LiveEditFunctionExpression) {
......@@ -482,7 +526,8 @@ TEST(LiveEditFunctionExpression) {
i_isolate);
debug::LiveEditResult result;
LiveEdit::PatchScript(
i_script, i_isolate->factory()->NewStringFromAsciiChecked(updated_source),
i_isolate, i_script,
i_isolate->factory()->NewStringFromAsciiChecked(updated_source), false,
&result);
CHECK_EQ(result.status, debug::LiveEditResult::OK);
{
......
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
Debug = debug.Debug;
function test(i) {
if (i == 3) {
return (function* gen() { yield test(i - 1); })().next().value;
} else if (i > 1) {
return test(i - 1);
} else {
debugger;
return 5;
}
}
let patch = null, exception = null;
Debug.setListener(listener);
patch = ['return 5', 'return 3'];
assertEquals(3, test(2)); // no running generator
patch = ['return 3', 'return -1'];
assertEquals(3, test(3)); // there is running generator
assertEquals(exception,
'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
Debug.setListener(null);
function listener(event) {
if (event != Debug.DebugEvent.Break || !patch) return;
try {
%LiveEditPatchScript(test,
Debug.scriptSource(test).replace(patch[0], patch[1]));
} catch (e) {
exception = e;
}
patch = null;
}
......@@ -53,7 +53,6 @@ Debug.setListener(null);
assertNull(exception);
assertEquals(["Emacs", "Eclipse", "Vim"], results);
// TODO(kozyatinskiy): uncomment lines with new liveedit implementation.
assertEquals([
"debugger;",
"results.push(BestEditor());",
......@@ -62,12 +61,12 @@ assertEquals([
"results.push(BestEditor());",
"results.push(BestEditor());",
" return 'Emacs';",
// " return 'Eclipse';",
" return 'Eclipse';",
" return 'Eclipse';",
"results.push(BestEditor());",
"results.push(BestEditor());",
" return 'Eclipse';",
// " return 'Vim';",
" return 'Vim';",
" return 'Vim';",
"results.push(BestEditor());",
"Debug.setListener(null);"
......
......@@ -46,8 +46,7 @@ function Replace(fun, original, patch) {
try {
%LiveEditPatchScript(fun, Debug.scriptSource(fun).replace(original, patch));
} catch (e) {
// TODO(kozyatinskiy): message should be BLOCKED_BY_NEW_TARGET_IN_RESTART_FRAME.
assertEquals(e, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
assertEquals(e, 'LiveEdit failed: BLOCKED_BY_NEW_TARGET_IN_RESTART_FRAME');
exceptions++;
}
});
......
......@@ -43,8 +43,7 @@ function Replace(fun, original, patch) {
try {
%LiveEditPatchScript(fun, Debug.scriptSource(fun).replace(original, patch));
} catch (e) {
// TODO(kozyatinskiy): message should be BLOCKED_BY_NEW_TARGET_IN_RESTART_FRAME.
assertEquals(e, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
assertEquals(e, 'LiveEdit failed: BLOCKED_BY_NEW_TARGET_IN_RESTART_FRAME');
exceptions++;
}
});
......
......@@ -97,10 +97,9 @@ function patch(fun, from, to) {
// Patching will fail however when a live iterator is suspended.
iter = generator(function(){});
assertIteratorResult(undefined, false, iter.next());
// TODO(kozyatinskiy): message should be BLOCKED_BY_RUNNING_GENERATOR.
assertThrowsEquals(function() {
patch(generator, '\'Capybara\'', '\'Tapir\'')
}, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
}, 'LiveEdit failed: BLOCKED_BY_RUNNING_GENERATOR');
assertIteratorResult("Capybara", true, iter.next());
// Try to patch functions with activations inside and outside generator
......
......@@ -106,10 +106,9 @@ function patch(fun, from, to) {
// Patching will fail however when an async function is suspended.
var resolve;
promise = asyncfn(function(){return new Promise(function(r){resolve = r})});
// TODO(kozyatinskiy): message should be BLOCKED_BY_RUNNING_GENERATOR.
assertThrowsEquals(function() {
patch(asyncfn, '\'Capybara\'', '\'Tapir\'')
}, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
}, 'LiveEdit failed: BLOCKED_BY_RUNNING_GENERATOR');
resolve();
assertPromiseValue("Capybara", promise);
......
Breakpoint in liveedited script
Update script source
{
callFrames : [
]
stackChanged : false
}
Set breakpoint
{
actualLocation : {
columnNumber : 0
lineNumber : 3
scriptId : <scriptId>
}
breakpointId : <breakpointId>
}
Call function foo and dump stack
foo (:3:0)
(anonymous) (:0:0)
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
let {session, contextGroup, Protocol} =
InspectorTest.start('Breakpoint in liveedited script');
contextGroup.addScript(
`function foo() {
}
var f = foo;`);
const newSource = `function boo() {
}
function foo() {
}
f = foo;`;
(async function test() {
session.setupScriptMap();
Protocol.Debugger.enable();
const {params: {scriptId}} = await Protocol.Debugger.onceScriptParsed();
InspectorTest.log('Update script source');
let {result} = await Protocol.Debugger.setScriptSource(
{scriptId, scriptSource: newSource})
InspectorTest.logMessage(result);
InspectorTest.log('Set breakpoint');
({result} = await Protocol.Debugger.setBreakpoint({location:{
scriptId,
lineNumber: 3,
columnNumber: 0
}}));
InspectorTest.logMessage(result);
InspectorTest.log('Call function foo and dump stack');
Protocol.Runtime.evaluate({expression: 'foo()'});
const {params:{callFrames}} = await Protocol.Debugger.oncePaused();
session.logCallFrames(callFrames);
InspectorTest.completeTest();
})();
......@@ -14,10 +14,10 @@ Running test: testSourceWithSyntaxError
id : <messageId>
result : {
exceptionDetails : {
columnNumber : 0
columnNumber : 11
exceptionId : <exceptionId>
lineNumber : 0
text : Invalid or unexpected token
lineNumber : 1
text : Uncaught SyntaxError: Invalid or unexpected token
}
}
}
......@@ -10,10 +10,10 @@ TestExpression(2,4) === 8
Update current script source 'a * b' -> 'a # b'..
{
exceptionDetails : {
columnNumber : 0
columnNumber : 13
exceptionId : <exceptionId>
lineNumber : 0
text : Invalid or unexpected token
lineNumber : 1
text : Uncaught SyntaxError: Invalid or unexpected token
}
}
TestExpression(2,4) === 8
......@@ -297,7 +297,7 @@ class SourceProcessor(SourceFileProcessor):
m = pattern.match(line)
if m:
runtime_functions.append(m.group(1))
if len(runtime_functions) < 475:
if len(runtime_functions) < 450:
print ("Runtime functions list is suspiciously short. "
"Consider updating the presubmit script.")
sys.exit(1)
......
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