[debug] migrate all liveedit tests to use LiveEdit::PatchScript
After this CL all liveedit tests call the same LiveEdit::PatchScript method. This method will be updated later. As well some new liveedit cctests added, unfortunately part of them do not work with current implementation. R=dgozman@chromium.org,yangguo@chromium.org Bug: v8:7862 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: I3521af12b0f95b39d13aaafb1d1cf60f3f642a97 Reviewed-on: https://chromium-review.googlesource.com/1108382 Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org> Reviewed-by: Yang Guo <yangguo@chromium.org> Reviewed-by: Dmitry Gozman <dgozman@chromium.org> Cr-Commit-Position: refs/heads/master@{#53936}
This commit is contained in:
parent
1cda2a6c29
commit
69d166fcef
@ -33,6 +33,7 @@
|
||||
#include "src/debug/debug-evaluate.h"
|
||||
#include "src/debug/debug-type-profile.h"
|
||||
#include "src/debug/debug.h"
|
||||
#include "src/debug/liveedit.h"
|
||||
#include "src/deoptimizer.h"
|
||||
#include "src/detachable-vector.h"
|
||||
#include "src/execution.h"
|
||||
@ -9404,11 +9405,12 @@ v8::debug::Location debug::Script::GetSourceLocation(int offset) const {
|
||||
}
|
||||
|
||||
bool debug::Script::SetScriptSource(v8::Local<v8::String> newSource,
|
||||
bool preview, bool* stack_changed) const {
|
||||
bool preview,
|
||||
debug::LiveEditResult* result) const {
|
||||
i::Handle<i::Script> script = Utils::OpenHandle(this);
|
||||
i::Isolate* isolate = script->GetIsolate();
|
||||
return isolate->debug()->SetScriptSource(
|
||||
script, Utils::OpenHandle(*newSource), preview, stack_changed);
|
||||
script, Utils::OpenHandle(*newSource), preview, result);
|
||||
}
|
||||
|
||||
bool debug::Script::SetBreakpoint(v8::Local<v8::String> condition,
|
||||
|
@ -86,6 +86,25 @@ void BreakRightNow(Isolate* isolate);
|
||||
|
||||
bool AllFramesOnStackAreBlackboxed(Isolate* isolate);
|
||||
|
||||
struct LiveEditResult {
|
||||
enum Status {
|
||||
OK,
|
||||
COMPILE_ERROR,
|
||||
BLOCKED_BY_RUNNING_GENERATOR,
|
||||
BLOCKED_BY_FUNCTION_ABOVE_BREAK_FRAME,
|
||||
BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME,
|
||||
BLOCKED_BY_ACTIVE_FUNCTION,
|
||||
BLOCKED_BY_NEW_TARGET_IN_RESTART_FRAME,
|
||||
FRAME_RESTART_IS_NOT_SUPPORTED
|
||||
};
|
||||
Status status = OK;
|
||||
bool stack_changed = false;
|
||||
// Fields below are available only for COMPILE_ERROR.
|
||||
v8::Local<v8::String> message;
|
||||
int line_number = -1;
|
||||
int column_number = -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Native wrapper around v8::internal::Script object.
|
||||
*/
|
||||
@ -114,7 +133,7 @@ class V8_EXPORT_PRIVATE Script {
|
||||
int GetSourceOffset(const debug::Location& location) const;
|
||||
v8::debug::Location GetSourceLocation(int offset) const;
|
||||
bool SetScriptSource(v8::Local<v8::String> newSource, bool preview,
|
||||
bool* stack_changed) const;
|
||||
LiveEditResult* result) const;
|
||||
bool SetBreakpoint(v8::Local<v8::String> condition, debug::Location* location,
|
||||
BreakpointId* id) const;
|
||||
};
|
||||
|
@ -601,7 +601,7 @@ bool Debug::IsMutedAtCurrentLocation(JavaScriptFrame* frame) {
|
||||
|
||||
MaybeHandle<Object> Debug::CallFunction(const char* name, int argc,
|
||||
Handle<Object> args[],
|
||||
bool catch_exceptions) {
|
||||
MaybeHandle<Object>* maybe_exception) {
|
||||
AllowJavascriptExecutionDebugOnly allow_script(isolate_);
|
||||
PostponeInterruptsScope no_interrupts(isolate_);
|
||||
AssertDebugContext();
|
||||
@ -610,17 +610,11 @@ MaybeHandle<Object> Debug::CallFunction(const char* name, int argc,
|
||||
Handle<JSFunction> fun = Handle<JSFunction>::cast(
|
||||
JSReceiver::GetProperty(isolate_, holder, name).ToHandleChecked());
|
||||
Handle<Object> undefined = isolate_->factory()->undefined_value();
|
||||
if (catch_exceptions) {
|
||||
MaybeHandle<Object> maybe_exception;
|
||||
return Execution::TryCall(isolate_, fun, undefined, argc, args,
|
||||
Execution::MessageHandling::kReport,
|
||||
&maybe_exception);
|
||||
} else {
|
||||
return Execution::Call(isolate_, fun, undefined, argc, args);
|
||||
}
|
||||
maybe_exception);
|
||||
}
|
||||
|
||||
|
||||
// Check whether a single break point object is triggered.
|
||||
bool Debug::CheckBreakPoint(Handle<BreakPoint> break_point,
|
||||
bool is_break_at_entry) {
|
||||
@ -1907,22 +1901,52 @@ bool Debug::CanBreakAtEntry(Handle<SharedFunctionInfo> shared) {
|
||||
}
|
||||
|
||||
bool Debug::SetScriptSource(Handle<Script> script, Handle<String> source,
|
||||
bool preview, bool* stack_changed) {
|
||||
bool preview, debug::LiveEditResult* output) {
|
||||
SaveContext save(isolate_);
|
||||
StackFrame::Id frame_id = break_frame_id();
|
||||
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;
|
||||
}
|
||||
|
||||
set_live_edit_enabled(true);
|
||||
Handle<Object> script_wrapper = Script::GetWrapper(script);
|
||||
Handle<Object> argv[] = {script_wrapper, source,
|
||||
isolate_->factory()->ToBoolean(preview),
|
||||
isolate_->factory()->NewJSArray(0)};
|
||||
Handle<Object> result;
|
||||
if (!CallFunction("SetScriptSource", arraysize(argv), argv, false)
|
||||
MaybeHandle<Object> maybe_exception;
|
||||
if (!CallFunction("SetScriptSource", arraysize(argv), argv, &maybe_exception)
|
||||
.ToHandle(&result)) {
|
||||
isolate_->OptionalRescheduleException(false);
|
||||
Handle<Object> pending_exception = maybe_exception.ToHandleChecked();
|
||||
set_live_edit_enabled(false);
|
||||
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);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
set_live_edit_enabled(false);
|
||||
@ -1930,7 +1954,8 @@ bool Debug::SetScriptSource(Handle<Script> script, Handle<String> source,
|
||||
JSReceiver::GetProperty(isolate_, Handle<JSObject>::cast(result),
|
||||
"stack_modified")
|
||||
.ToHandleChecked();
|
||||
*stack_changed = stack_changed_value->IsTrue(isolate_);
|
||||
output->stack_changed = stack_changed_value->IsTrue(isolate_);
|
||||
output->status = debug::LiveEditResult::OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -313,7 +313,7 @@ class Debug {
|
||||
// change. stack_changed is true if after editing script on pause stack is
|
||||
// changed and client should request stack trace again.
|
||||
bool SetScriptSource(Handle<Script> script, Handle<String> source,
|
||||
bool preview, bool* stack_changed);
|
||||
bool preview, debug::LiveEditResult* result);
|
||||
|
||||
// Threading support.
|
||||
char* ArchiveDebug(char* to);
|
||||
@ -467,7 +467,7 @@ class Debug {
|
||||
bool CheckBreakPoint(Handle<BreakPoint> break_point, bool is_break_at_entry);
|
||||
MaybeHandle<Object> CallFunction(const char* name, int argc,
|
||||
Handle<Object> args[],
|
||||
bool catch_exceptions = true);
|
||||
MaybeHandle<Object>* maybe_exception);
|
||||
|
||||
inline void AssertDebugContext() {
|
||||
DCHECK(in_debug_scope());
|
||||
|
@ -1597,5 +1597,10 @@ int LiveEdit::TranslatePosition(const std::vector<SourceChangeRange>& changes,
|
||||
return position + (it->new_end_position - it->end_position);
|
||||
}
|
||||
|
||||
void LiveEdit::PatchScript(Handle<Script> script, Handle<String> new_source,
|
||||
debug::LiveEditResult* result) {
|
||||
script->GetIsolate()->debug()->SetScriptSource(script, new_source, false,
|
||||
result);
|
||||
}
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -29,6 +29,9 @@
|
||||
#include "src/ast/ast-traversal-visitor.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace debug {
|
||||
struct LiveEditResult;
|
||||
}
|
||||
namespace internal {
|
||||
|
||||
class JavaScriptFrame;
|
||||
@ -140,6 +143,8 @@ class LiveEdit : AllStatic {
|
||||
std::vector<SourceChangeRange>* changes);
|
||||
static int TranslatePosition(const std::vector<SourceChangeRange>& changed,
|
||||
int position);
|
||||
static void PatchScript(Handle<Script> script, Handle<String> source,
|
||||
debug::LiveEditResult* result);
|
||||
|
||||
// A copy of this is in liveedit.js.
|
||||
enum FunctionPatchabilityStatus {
|
||||
|
@ -294,62 +294,6 @@ Response buildScopes(v8::debug::ScopeIterator* iterator,
|
||||
return Response::OK();
|
||||
}
|
||||
|
||||
bool liveEditExceptionToDetails(
|
||||
V8InspectorImpl* inspector, v8::Local<v8::Context> context,
|
||||
v8::Local<v8::Value> exceptionValue,
|
||||
Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) {
|
||||
if (!exceptionValue->IsObject()) return false;
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
v8::Local<v8::Object> exception = exceptionValue.As<v8::Object>();
|
||||
v8::Local<v8::Value> detailsValue;
|
||||
if (!exception->Get(context, toV8String(isolate, "details"))
|
||||
.ToLocal(&detailsValue) ||
|
||||
!detailsValue->IsObject()) {
|
||||
return false;
|
||||
}
|
||||
v8::Local<v8::Object> details = detailsValue.As<v8::Object>();
|
||||
v8::Local<v8::Value> message;
|
||||
if (!details->Get(context, toV8String(isolate, "syntaxErrorMessage"))
|
||||
.ToLocal(&message) ||
|
||||
!message->IsString()) {
|
||||
return false;
|
||||
}
|
||||
v8::Local<v8::Value> positionValue;
|
||||
if (!details->Get(context, toV8String(isolate, "position"))
|
||||
.ToLocal(&positionValue) ||
|
||||
!positionValue->IsObject()) {
|
||||
return false;
|
||||
}
|
||||
v8::Local<v8::Value> startPositionValue;
|
||||
if (!positionValue.As<v8::Object>()
|
||||
->Get(context, toV8String(isolate, "start"))
|
||||
.ToLocal(&startPositionValue) ||
|
||||
!startPositionValue->IsObject()) {
|
||||
return false;
|
||||
}
|
||||
v8::Local<v8::Object> startPosition = startPositionValue.As<v8::Object>();
|
||||
v8::Local<v8::Value> lineValue;
|
||||
if (!startPosition->Get(context, toV8String(isolate, "line"))
|
||||
.ToLocal(&lineValue) ||
|
||||
!lineValue->IsInt32()) {
|
||||
return false;
|
||||
}
|
||||
v8::Local<v8::Value> columnValue;
|
||||
if (!startPosition->Get(context, toV8String(isolate, "column"))
|
||||
.ToLocal(&columnValue) ||
|
||||
!columnValue->IsInt32()) {
|
||||
return false;
|
||||
}
|
||||
*exceptionDetails =
|
||||
protocol::Runtime::ExceptionDetails::create()
|
||||
.setExceptionId(inspector->nextExceptionId())
|
||||
.setText(toProtocolString(message.As<v8::String>()))
|
||||
.setLineNumber(lineValue->Int32Value(context).FromJust() - 1)
|
||||
.setColumnNumber(columnValue->Int32Value(context).FromJust() - 1)
|
||||
.build();
|
||||
return true;
|
||||
}
|
||||
|
||||
protocol::DictionaryValue* getOrCreateObject(protocol::DictionaryValue* object,
|
||||
const String16& key) {
|
||||
protocol::DictionaryValue* value = object->getObject(key);
|
||||
@ -927,23 +871,22 @@ Response V8DebuggerAgentImpl::setScriptSource(
|
||||
v8::HandleScope handleScope(m_isolate);
|
||||
v8::Local<v8::Context> context = inspected->context();
|
||||
v8::Context::Scope contextScope(context);
|
||||
v8::TryCatch tryCatch(m_isolate);
|
||||
|
||||
bool stackChangedValue = false;
|
||||
it->second->setSource(newContent, dryRun.fromMaybe(false),
|
||||
&stackChangedValue);
|
||||
if (tryCatch.HasCaught()) {
|
||||
if (liveEditExceptionToDetails(m_inspector, context, tryCatch.Exception(),
|
||||
optOutCompileError)) {
|
||||
v8::debug::LiveEditResult result;
|
||||
it->second->setSource(newContent, dryRun.fromMaybe(false), &result);
|
||||
if (result.status != v8::debug::LiveEditResult::OK) {
|
||||
*optOutCompileError =
|
||||
protocol::Runtime::ExceptionDetails::create()
|
||||
.setExceptionId(m_inspector->nextExceptionId())
|
||||
.setText(toProtocolString(result.message))
|
||||
.setLineNumber(result.line_number != -1 ? result.line_number - 1
|
||||
: 0)
|
||||
.setColumnNumber(result.column_number != -1 ? result.column_number
|
||||
: 0)
|
||||
.build();
|
||||
return Response::OK();
|
||||
}
|
||||
v8::Local<v8::Message> message = tryCatch.Message();
|
||||
if (!message.IsEmpty())
|
||||
return Response::Error(toProtocolStringWithTypeCheck(message->Get()));
|
||||
else
|
||||
return Response::InternalError();
|
||||
} else {
|
||||
*stackChanged = stackChangedValue;
|
||||
*stackChanged = result.stack_changed;
|
||||
}
|
||||
std::unique_ptr<Array<CallFrame>> callFrames;
|
||||
Response response = currentCallFrames(&callFrames);
|
||||
|
@ -166,12 +166,12 @@ class ActualScript : public V8DebuggerScript {
|
||||
}
|
||||
|
||||
void setSource(const String16& newSource, bool preview,
|
||||
bool* stackChanged) override {
|
||||
v8::debug::LiveEditResult* result) override {
|
||||
DCHECK(!isModule());
|
||||
v8::HandleScope scope(m_isolate);
|
||||
v8::EscapableHandleScope scope(m_isolate);
|
||||
v8::Local<v8::String> v8Source = toV8String(m_isolate, newSource);
|
||||
if (!m_script.Get(m_isolate)->SetScriptSource(v8Source, preview,
|
||||
stackChanged)) {
|
||||
if (!m_script.Get(m_isolate)->SetScriptSource(v8Source, preview, result)) {
|
||||
result->message = scope.Escape(result->message);
|
||||
return;
|
||||
}
|
||||
if (preview) return;
|
||||
@ -290,7 +290,9 @@ class WasmVirtualScript : public V8DebuggerScript {
|
||||
bool isLiveEdit() const override { return false; }
|
||||
bool isModule() const override { return false; }
|
||||
void setSourceMappingURL(const String16&) override {}
|
||||
void setSource(const String16&, bool, bool*) override { UNREACHABLE(); }
|
||||
void setSource(const String16&, bool, v8::debug::LiveEditResult*) override {
|
||||
UNREACHABLE();
|
||||
}
|
||||
bool isSourceLoadedLazily() const override { return true; }
|
||||
const String16& source() const override {
|
||||
return m_wasmTranslation->GetSource(m_id, m_functionIndex);
|
||||
|
@ -73,7 +73,7 @@ class V8DebuggerScript {
|
||||
void setSourceURL(const String16&);
|
||||
virtual void setSourceMappingURL(const String16&) = 0;
|
||||
virtual void setSource(const String16& source, bool preview,
|
||||
bool* stackChanged) = 0;
|
||||
v8::debug::LiveEditResult* result) = 0;
|
||||
|
||||
virtual bool getPossibleBreakpoints(
|
||||
const v8::debug::Location& start, const v8::debug::Location& end,
|
||||
|
@ -898,5 +898,43 @@ RUNTIME_FUNCTION(Runtime_DebugAsyncFunctionFinished) {
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_LiveEditPatchScript) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(2, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, script_function, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, new_source, 1);
|
||||
|
||||
Handle<Script> script(Script::cast(script_function->shared()->script()),
|
||||
isolate);
|
||||
v8::debug::LiveEditResult result;
|
||||
LiveEdit::PatchScript(script, new_source, &result);
|
||||
switch (result.status) {
|
||||
case v8::debug::LiveEditResult::COMPILE_ERROR:
|
||||
return isolate->Throw(*isolate->factory()->NewStringFromAsciiChecked(
|
||||
"LiveEdit failed: COMPILE_ERROR"));
|
||||
case v8::debug::LiveEditResult::BLOCKED_BY_RUNNING_GENERATOR:
|
||||
return isolate->Throw(*isolate->factory()->NewStringFromAsciiChecked(
|
||||
"LiveEdit failed: BLOCKED_BY_RUNNING_GENERATOR"));
|
||||
case v8::debug::LiveEditResult::BLOCKED_BY_FUNCTION_ABOVE_BREAK_FRAME:
|
||||
return isolate->Throw(*isolate->factory()->NewStringFromAsciiChecked(
|
||||
"LiveEdit failed: BLOCKED_BY_FUNCTION_ABOVE_BREAK_FRAME"));
|
||||
case v8::debug::LiveEditResult::
|
||||
BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME:
|
||||
return isolate->Throw(*isolate->factory()->NewStringFromAsciiChecked(
|
||||
"LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME"));
|
||||
case v8::debug::LiveEditResult::BLOCKED_BY_ACTIVE_FUNCTION:
|
||||
return isolate->Throw(*isolate->factory()->NewStringFromAsciiChecked(
|
||||
"LiveEdit failed: BLOCKED_BY_ACTIVE_FUNCTION"));
|
||||
case v8::debug::LiveEditResult::BLOCKED_BY_NEW_TARGET_IN_RESTART_FRAME:
|
||||
return isolate->Throw(*isolate->factory()->NewStringFromAsciiChecked(
|
||||
"LiveEdit failed: BLOCKED_BY_NEW_TARGET_IN_RESTART_FRAME"));
|
||||
case v8::debug::LiveEditResult::FRAME_RESTART_IS_NOT_SUPPORTED:
|
||||
return isolate->Throw(*isolate->factory()->NewStringFromAsciiChecked(
|
||||
"LiveEdit failed: FRAME_RESTART_IS_NOT_SUPPORTED"));
|
||||
case v8::debug::LiveEditResult::OK:
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -155,7 +155,8 @@ namespace internal {
|
||||
F(ScriptLocationFromLine, 4, 1) \
|
||||
F(ScriptPositionInfo2, 3, 1) \
|
||||
F(ScriptPositionInfo, 3, 1) \
|
||||
F(SetGeneratorScopeVariableValue, 4, 1)
|
||||
F(SetGeneratorScopeVariableValue, 4, 1) \
|
||||
F(LiveEditPatchScript, 2, 1)
|
||||
|
||||
#define FOR_EACH_INTRINSIC_FORIN(F) \
|
||||
F(ForInEnumerate, 1, 1) \
|
||||
|
@ -196,5 +196,301 @@ TEST(LiveEditTranslatePosition) {
|
||||
CHECK_EQ(LiveEdit::TranslatePosition(changes, 6), 8);
|
||||
CHECK_EQ(LiveEdit::TranslatePosition(changes, 8), 8);
|
||||
}
|
||||
|
||||
namespace {
|
||||
void PatchFunctions(v8::Local<v8::Context> context, const char* source_a,
|
||||
const char* source_b,
|
||||
v8::debug::LiveEditResult* result = nullptr) {
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Script> script_a =
|
||||
v8::Script::Compile(context, v8_str(isolate, source_a)).ToLocalChecked();
|
||||
script_a->Run(context).ToLocalChecked();
|
||||
i::Handle<i::Script> i_script_a(
|
||||
i::Script::cast(v8::Utils::OpenHandle(*script_a)->shared()->script()));
|
||||
|
||||
i::Isolate* i_isolate = i_script_a->GetIsolate();
|
||||
if (result) {
|
||||
LiveEdit::PatchScript(
|
||||
i_script_a, i_isolate->factory()->NewStringFromAsciiChecked(source_b),
|
||||
result);
|
||||
} else {
|
||||
v8::debug::LiveEditResult result;
|
||||
LiveEdit::PatchScript(
|
||||
i_script_a, i_isolate->factory()->NewStringFromAsciiChecked(source_b),
|
||||
&result);
|
||||
CHECK_EQ(result.status, v8::debug::LiveEditResult::OK);
|
||||
}
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
// TODO(kozyatinskiy): enable it with new liveedit implementation.
|
||||
DISABLED_TEST(LiveEditPatchFunctions) {
|
||||
LocalContext env;
|
||||
v8::HandleScope scope(env->GetIsolate());
|
||||
v8::Local<v8::Context> context = env.local();
|
||||
// Check that function is removed from compilation cache.
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
PatchFunctions(context, "42;", "%AbortJS('')");
|
||||
PatchFunctions(context, "42;", "239;");
|
||||
i::FLAG_allow_natives_syntax = false;
|
||||
|
||||
// Basic test cases.
|
||||
PatchFunctions(context, "42;", "2;");
|
||||
PatchFunctions(context, "42;", " 42;");
|
||||
PatchFunctions(context, "42;", "42;");
|
||||
// Trivial return value change.
|
||||
PatchFunctions(context, "function foo() { return 1; }",
|
||||
"function foo() { return 42; }");
|
||||
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo()")
|
||||
->ToInt32(env->GetIsolate())
|
||||
->Value(),
|
||||
42);
|
||||
// It is expected, we do not reevaluate top level function.
|
||||
PatchFunctions(context, "var a = 1; function foo() { return a; }",
|
||||
"var a = 3; function foo() { return a; }");
|
||||
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo()")
|
||||
->ToInt32(env->GetIsolate())
|
||||
->Value(),
|
||||
1);
|
||||
// Throw exception since var b is not defined in original source.
|
||||
PatchFunctions(context, "var a = 1; function foo() { return a; }",
|
||||
"var b = 4; function foo() { return b; }");
|
||||
{
|
||||
v8::TryCatch try_catch(env->GetIsolate());
|
||||
CompileRun("foo()");
|
||||
CHECK(try_catch.HasCaught());
|
||||
}
|
||||
// But user always can add new variable to function and use it.
|
||||
PatchFunctions(context, "var a = 1; function foo() { return a; }",
|
||||
"var b = 4; function foo() { var b = 5; return b; }");
|
||||
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo()")
|
||||
->ToInt32(env->GetIsolate())
|
||||
->Value(),
|
||||
5);
|
||||
|
||||
PatchFunctions(context, "var a = 1; function foo() { return a; }",
|
||||
"var b = 4; function foo() { var a = 6; return a; }");
|
||||
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo()")
|
||||
->ToInt32(env->GetIsolate())
|
||||
->Value(),
|
||||
6);
|
||||
|
||||
PatchFunctions(context, "var a = 1; function foo() { return a; }",
|
||||
"var d = (() => ({a:2}))(); function foo() { return d; }");
|
||||
{
|
||||
v8::TryCatch try_catch(env->GetIsolate());
|
||||
CompileRun("foo()");
|
||||
CHECK(try_catch.HasCaught());
|
||||
}
|
||||
|
||||
PatchFunctions(context, "var a = 1; function foo() { return a; }",
|
||||
"var b = 1; var a = 2; function foo() { return a; }");
|
||||
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo()")
|
||||
->ToInt32(env->GetIsolate())
|
||||
->Value(),
|
||||
1);
|
||||
|
||||
PatchFunctions(context, "var a = 1; function foo() { return a; }",
|
||||
"var b = 1; var a = 2; function foo() { return b; }");
|
||||
{
|
||||
v8::TryCatch try_catch(env->GetIsolate());
|
||||
CompileRun("foo()");
|
||||
CHECK(try_catch.HasCaught());
|
||||
}
|
||||
|
||||
PatchFunctions(context, "function foo() { var a = 1; return a; }",
|
||||
"function foo() { var b = 1; return b; }");
|
||||
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo()")
|
||||
->ToInt32(env->GetIsolate())
|
||||
->Value(),
|
||||
1);
|
||||
|
||||
PatchFunctions(context, "var a = 3; function foo() { var a = 1; return a; }",
|
||||
"function foo() { var b = 1; return a; }");
|
||||
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo()")
|
||||
->ToInt32(env->GetIsolate())
|
||||
->Value(),
|
||||
3);
|
||||
|
||||
PatchFunctions(context, "var a = 3; var c = 7; function foo() { return a; }",
|
||||
"var b = 5; var a = 3; function foo() { return b; }");
|
||||
{
|
||||
v8::TryCatch try_catch(env->GetIsolate());
|
||||
CompileRun("foo()");
|
||||
CHECK(try_catch.HasCaught());
|
||||
}
|
||||
|
||||
// Add argument.
|
||||
PatchFunctions(context, "function fooArgs(a1, b1) { return a1 + b1; }",
|
||||
"function fooArgs(a2, b2, c2) { return a2 + b2 + c2; }");
|
||||
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "fooArgs(1,2,3)")
|
||||
->ToInt32(env->GetIsolate())
|
||||
->Value(),
|
||||
6);
|
||||
|
||||
PatchFunctions(context, "function fooArgs(a1, b1) { return a1 + b1; }",
|
||||
"function fooArgs(a1, b1, c1) { return a1 + b1 + c1; }");
|
||||
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "fooArgs(1,2,3)")
|
||||
->ToInt32(env->GetIsolate())
|
||||
->Value(),
|
||||
6);
|
||||
|
||||
i::FLAG_allow_natives_syntax = true;
|
||||
PatchFunctions(context,
|
||||
"function foo(a, b) { return a + b; }; "
|
||||
"%OptimizeFunctionOnNextCall(foo); foo(1,2);",
|
||||
"function foo(a, b) { return a * b; };");
|
||||
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo(5,7)")
|
||||
->ToInt32(env->GetIsolate())
|
||||
->Value(),
|
||||
35);
|
||||
i::FLAG_allow_natives_syntax = false;
|
||||
|
||||
// Check inner function.
|
||||
PatchFunctions(
|
||||
context,
|
||||
"function foo(a,b) { function op(a,b) { return a + b } return op(a,b); }",
|
||||
"function foo(a,b) { function op(a,b) { return a * b } return op(a,b); "
|
||||
"}");
|
||||
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "foo(8,9)")
|
||||
->ToInt32(env->GetIsolate())
|
||||
->Value(),
|
||||
72);
|
||||
|
||||
// Update constructor.
|
||||
PatchFunctions(context,
|
||||
"class Foo { constructor(a,b) { this.data = a + b; } };",
|
||||
"class Foo { constructor(a,b) { this.data = a * b; } };");
|
||||
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "new Foo(4,5).data")
|
||||
->ToInt32(env->GetIsolate())
|
||||
->Value(),
|
||||
20);
|
||||
|
||||
// Change inner functions.
|
||||
PatchFunctions(
|
||||
context,
|
||||
"function f(evt) { function f2() {} f2(),f3(); function f3() {} } "
|
||||
"function f4() {}",
|
||||
"function f(evt) { function f2() { return 1; } return f2() + f3(); "
|
||||
"function f3() { return 2; } } function f4() {}");
|
||||
CHECK_EQ(CompileRunChecked(env->GetIsolate(), "f()")
|
||||
->ToInt32(env->GetIsolate())
|
||||
->Value(),
|
||||
3);
|
||||
// Change usage of outer scope.
|
||||
PatchFunctions(context,
|
||||
"function ChooseAnimal(a, b) {\n "
|
||||
" if (a == 7 && b == 7) {\n"
|
||||
" return;\n"
|
||||
" }\n"
|
||||
" return function Chooser() {\n"
|
||||
" return 'Cat' + a;\n"
|
||||
" };\n"
|
||||
"}\n"
|
||||
"var old_closure = ChooseAnimal(2, 3);",
|
||||
"function ChooseAnimal(a, b) {\n "
|
||||
" if (a == 7 && b == 7) {\n"
|
||||
" return;\n"
|
||||
" }\n"
|
||||
" return function Chooser() {\n"
|
||||
" return 'Capybara' + b;\n"
|
||||
" };\n"
|
||||
"}\n");
|
||||
CompileRunChecked(env->GetIsolate(), "var new_closure = ChooseAnimal(3, 4);");
|
||||
v8::Local<v8::String> call_result =
|
||||
CompileRunChecked(env->GetIsolate(), "new_closure()").As<v8::String>();
|
||||
v8::String::Utf8Value new_result_utf8(env->GetIsolate(), call_result);
|
||||
CHECK_NOT_NULL(strstr(*new_result_utf8, "Capybara4"));
|
||||
call_result =
|
||||
CompileRunChecked(env->GetIsolate(), "old_closure()").As<v8::String>();
|
||||
v8::String::Utf8Value old_result_utf8(env->GetIsolate(), call_result);
|
||||
CHECK_NOT_NULL(strstr(*old_result_utf8, "Cat2"));
|
||||
|
||||
// Update const literals.
|
||||
PatchFunctions(context, "function foo() { return 'a' + 'b'; }",
|
||||
"function foo() { return 'c' + 'b'; }");
|
||||
{
|
||||
v8::Local<v8::String> result =
|
||||
CompileRunChecked(env->GetIsolate(), "foo()").As<v8::String>();
|
||||
v8::String::Utf8Value new_result_utf8(env->GetIsolate(), result);
|
||||
CHECK_NOT_NULL(strstr(*new_result_utf8, "cb"));
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(kozyatinskiy): enable it with new liveedit implementation.
|
||||
DISABLED_TEST(LiveEditCompileError) {
|
||||
LocalContext env;
|
||||
v8::HandleScope scope(env->GetIsolate());
|
||||
v8::Local<v8::Context> context = env.local();
|
||||
debug::LiveEditResult result;
|
||||
PatchFunctions(
|
||||
context,
|
||||
"var something1 = 25; \n"
|
||||
" function ChooseAnimal() { return 'Cat'; } \n"
|
||||
" ChooseAnimal.Helper = function() { return 'Help!'; }\n",
|
||||
"var something1 = 25; \n"
|
||||
" function ChooseAnimal() { return 'Cap' + ) + 'bara'; "
|
||||
"} \n"
|
||||
" ChooseAnimal.Helper = function() { return 'Help!'; }\n",
|
||||
&result);
|
||||
CHECK_EQ(result.status, debug::LiveEditResult::COMPILE_ERROR);
|
||||
CHECK_EQ(result.line_number, 2);
|
||||
CHECK_EQ(result.column_number, 51);
|
||||
v8::String::Utf8Value result_message(env->GetIsolate(), result.message);
|
||||
CHECK_NOT_NULL(
|
||||
strstr(*result_message, "Uncaught SyntaxError: Unexpected token )"));
|
||||
|
||||
{
|
||||
v8::Local<v8::String> result =
|
||||
CompileRunChecked(env->GetIsolate(), "ChooseAnimal()").As<v8::String>();
|
||||
v8::String::Utf8Value new_result_utf8(env->GetIsolate(), result);
|
||||
CHECK_NOT_NULL(strstr(*new_result_utf8, "Cat"));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
TEST(LiveEditFunctionExpression) {
|
||||
const char* original_source =
|
||||
"(function() {\n "
|
||||
" return 'Cat';\n"
|
||||
"})\n";
|
||||
const char* updated_source =
|
||||
"(function() {\n "
|
||||
" return 'Capy' + 'bara';\n"
|
||||
"})\n";
|
||||
LocalContext env;
|
||||
v8::HandleScope scope(env->GetIsolate());
|
||||
v8::Local<v8::Context> context = env.local();
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
v8::Local<v8::Script> script =
|
||||
v8::Script::Compile(context, v8_str(isolate, original_source))
|
||||
.ToLocalChecked();
|
||||
v8::Local<v8::Function> f =
|
||||
script->Run(context).ToLocalChecked().As<v8::Function>();
|
||||
i::Handle<i::Script> i_script(
|
||||
i::Script::cast(v8::Utils::OpenHandle(*script)->shared()->script()));
|
||||
i::Isolate* i_isolate = i_script->GetIsolate();
|
||||
debug::LiveEditResult result;
|
||||
LiveEdit::PatchScript(
|
||||
i_script, i_isolate->factory()->NewStringFromAsciiChecked(updated_source),
|
||||
&result);
|
||||
CHECK_EQ(result.status, debug::LiveEditResult::OK);
|
||||
{
|
||||
v8::Local<v8::String> result =
|
||||
f->Call(context, context->Global(), 0, nullptr)
|
||||
.ToLocalChecked()
|
||||
.As<v8::String>();
|
||||
v8::String::Utf8Value new_result_utf8(env->GetIsolate(), result);
|
||||
CHECK_NOT_NULL(strstr(*new_result_utf8, "Capybara"));
|
||||
}
|
||||
}
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -25,6 +25,7 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
Debug = debug.Debug
|
||||
|
||||
@ -34,13 +35,11 @@ eval("var something1 = 25; "
|
||||
|
||||
assertEquals("Cat", ChooseAnimal());
|
||||
|
||||
var script = Debug.findScript(ChooseAnimal);
|
||||
|
||||
var orig_animal = "Cat";
|
||||
var patch_pos = script.source.indexOf(orig_animal);
|
||||
var new_animal_patch = "Cap' + 'y' + 'bara";
|
||||
var new_source =
|
||||
Debug.scriptSource(ChooseAnimal).replace(orig_animal, new_animal_patch);
|
||||
|
||||
var change_log = new Array();
|
||||
Debug.LiveEdit.TestApi.ApplySingleChunkPatch(script, patch_pos, orig_animal.length, new_animal_patch, change_log);
|
||||
%LiveEditPatchScript(ChooseAnimal, new_source);
|
||||
|
||||
assertEquals("Capybara", ChooseAnimal());
|
||||
|
@ -25,8 +25,7 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --noalways-opt
|
||||
|
||||
// Flags: --noalways-opt --allow-natives-syntax
|
||||
|
||||
Debug = debug.Debug
|
||||
|
||||
@ -43,19 +42,12 @@ var old_closure = ChooseAnimal(19);
|
||||
|
||||
assertEquals("Cat", old_closure());
|
||||
|
||||
var script = Debug.findScript(ChooseAnimal);
|
||||
|
||||
var orig_animal = "'Cat'";
|
||||
var patch_pos = script.source.indexOf(orig_animal);
|
||||
var new_animal_patch = "'Capybara' + p";
|
||||
|
||||
// We patch innermost function "Chooser".
|
||||
// However, this does not actually patch existing "Chooser" instances,
|
||||
// because old value of parameter "p" was not saved.
|
||||
// Instead it patches ChooseAnimal.
|
||||
var change_log = new Array();
|
||||
Debug.LiveEdit.TestApi.ApplySingleChunkPatch(script, patch_pos, orig_animal.length, new_animal_patch, change_log);
|
||||
print("Change log: " + JSON.stringify(change_log) + "\n");
|
||||
%LiveEditPatchScript(
|
||||
ChooseAnimal, Debug.scriptSource(ChooseAnimal).replace("'Cat'", "'Capybara' + p"));
|
||||
|
||||
var new_closure = ChooseAnimal(19);
|
||||
// New instance of closure is patched.
|
||||
|
@ -30,6 +30,7 @@
|
||||
// hasen't been changed. However actually function became one level more nested
|
||||
// and must be recompiled because it uses variable from outer scope.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
Debug = debug.Debug
|
||||
|
||||
@ -49,15 +50,11 @@ eval(
|
||||
var z6 = Factory(6);
|
||||
assertEquals(8, z6());
|
||||
|
||||
var script = Debug.findScript(Factory);
|
||||
var new_source = Debug.scriptSource(Factory).replace(
|
||||
function_z_text,
|
||||
'function Intermediate() {\nreturn (\n' + function_z_text + ')\n;\n}\n');
|
||||
|
||||
var new_source = script.source.replace(function_z_text, "function Intermediate() {\nreturn (\n" + function_z_text + ")\n;\n}\n");
|
||||
print("new source: " + new_source);
|
||||
|
||||
var change_log = new Array();
|
||||
var result = Debug.LiveEdit.SetScriptSource(script, new_source, false, change_log);
|
||||
print("Result: " + JSON.stringify(result) + "\n");
|
||||
print("Change log: " + JSON.stringify(change_log) + "\n");
|
||||
%LiveEditPatchScript(Factory, new_source);
|
||||
|
||||
assertEquals(8, z6());
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
// hasen't been changed. However actually function became one level more nested
|
||||
// and must be recompiled because it uses variable from outer scope.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
Debug = debug.Debug
|
||||
|
||||
@ -53,14 +54,10 @@ var res = TestFunction();
|
||||
print(res);
|
||||
assertEquals('a,c', res);
|
||||
|
||||
var script = Debug.findScript(TestFunction);
|
||||
var new_source = script.source.replace("2013", "b");
|
||||
var new_source = Debug.scriptSource(TestFunction).replace('2013', 'b');
|
||||
print("new source: " + new_source);
|
||||
var change_log = new Array();
|
||||
var result = Debug.LiveEdit.SetScriptSource(script, new_source, false, change_log);
|
||||
|
||||
print("Result: " + JSON.stringify(result) + "\n");
|
||||
print("Change log: " + JSON.stringify(change_log) + "\n");
|
||||
%LiveEditPatchScript(TestFunction, new_source);
|
||||
|
||||
var res = TestFunction();
|
||||
print(res);
|
||||
|
@ -25,6 +25,7 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
Debug = debug.Debug
|
||||
|
||||
@ -33,35 +34,26 @@ unique_id = 1;
|
||||
function TestBase(name) {
|
||||
print("TestBase constructor: " + name);
|
||||
|
||||
this.ChooseAnimal = eval(
|
||||
"/* " + unique_id + "*/\n" +
|
||||
"(function ChooseAnimal(callback) {\n " +
|
||||
" callback();\n" +
|
||||
" return 'Cat';\n" +
|
||||
"})\n"
|
||||
);
|
||||
const original_source = '/* ' + unique_id + '*/\n' +
|
||||
'(function ChooseAnimal(callback) {\n ' +
|
||||
' callback();\n' +
|
||||
' return \'Cat\';\n' +
|
||||
'})\n';
|
||||
const updated_source = original_source.replace('\'Cat\'', '\'Capybara\'');
|
||||
|
||||
this.ChooseAnimal = eval(original_source);
|
||||
// Prevents eval script caching.
|
||||
unique_id++;
|
||||
|
||||
var script = Debug.findScript(this.ChooseAnimal);
|
||||
|
||||
var orig_animal = "'Cat'";
|
||||
var patch_pos = script.source.indexOf(orig_animal);
|
||||
var new_animal_patch = "'Capybara'";
|
||||
const func = this.ChooseAnimal;
|
||||
|
||||
var got_exception = false;
|
||||
var successfully_changed = false;
|
||||
|
||||
// Should be called from Debug context.
|
||||
this.ScriptChanger = function() {
|
||||
this.ScriptChanger = () => {
|
||||
assertEquals(false, successfully_changed, "applying patch second time");
|
||||
// Runs in debugger context.
|
||||
var change_log = new Array();
|
||||
try {
|
||||
Debug.LiveEdit.TestApi.ApplySingleChunkPatch(script, patch_pos, orig_animal.length, new_animal_patch, change_log);
|
||||
} finally {
|
||||
print("Change log: " + JSON.stringify(change_log) + "\n");
|
||||
}
|
||||
%LiveEditPatchScript(func, updated_source);
|
||||
successfully_changed = true;
|
||||
};
|
||||
}
|
||||
@ -74,7 +66,7 @@ function WrapInCatcher(f, holder) {
|
||||
try {
|
||||
f();
|
||||
} catch (e) {
|
||||
if (e instanceof Debug.LiveEdit.Failure) {
|
||||
if (e.startsWith('LiveEdit failed')) {
|
||||
holder[0] = e;
|
||||
} else {
|
||||
throw e;
|
||||
|
@ -25,6 +25,7 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
Debug = debug.Debug
|
||||
|
||||
@ -34,23 +35,16 @@ eval("var something1 = 25; \n"
|
||||
|
||||
assertEquals("Cat", ChooseAnimal());
|
||||
|
||||
var script = Debug.findScript(ChooseAnimal);
|
||||
var new_source =
|
||||
Debug.scriptSource(ChooseAnimal).replace('Cat', 'Cap\' + ) + \'bara');
|
||||
print('new source: ' + new_source);
|
||||
|
||||
var orig_animal = "Cat";
|
||||
var patch_pos = script.source.indexOf(orig_animal);
|
||||
var new_animal_patch = "Cap' + ) + 'bara";
|
||||
|
||||
var change_log = new Array();
|
||||
var caught_exception = null;
|
||||
try {
|
||||
Debug.LiveEdit.TestApi.ApplySingleChunkPatch(script, patch_pos,
|
||||
orig_animal.length, new_animal_patch, change_log);
|
||||
%LiveEditPatchScript(ChooseAnimal, new_source);
|
||||
} catch (e) {
|
||||
caught_exception = e;
|
||||
}
|
||||
|
||||
assertNotNull(caught_exception);
|
||||
assertEquals("Unexpected token )",
|
||||
caught_exception.details.syntaxErrorMessage);
|
||||
|
||||
assertEquals(2, caught_exception.details.position.start.line);
|
||||
assertEquals('LiveEdit failed: COMPILE_ERROR', caught_exception);
|
||||
|
@ -92,8 +92,7 @@ function TestCase(test_scenario, expected_output) {
|
||||
}
|
||||
script_text_generator.change(change_var);
|
||||
try {
|
||||
Debug.LiveEdit.SetScriptSource(script, script_text_generator.get(),
|
||||
false, []);
|
||||
%LiveEditPatchScript(test_fun, script_text_generator.get())
|
||||
} catch (e) {
|
||||
print("LiveEdit exception: " + e);
|
||||
throw e;
|
||||
|
@ -2,9 +2,9 @@
|
||||
// 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 BestEditor() {
|
||||
throw 'Emacs';
|
||||
}
|
||||
@ -37,11 +37,8 @@ function listener(event, exec_state, event_data, data) {
|
||||
};
|
||||
|
||||
function Replace(fun, original, patch) {
|
||||
var script = Debug.findScript(fun);
|
||||
if (fun.toString().indexOf(original) < 0) return;
|
||||
var patch_pos = script.source.indexOf(original);
|
||||
var change_log = [];
|
||||
Debug.LiveEdit.TestApi.ApplySingleChunkPatch(script, patch_pos, original.length, patch, change_log);
|
||||
%LiveEditPatchScript(fun, Debug.scriptSource(fun).replace(original, patch));
|
||||
}
|
||||
|
||||
Debug.setListener(listener);
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file
|
||||
|
||||
// Flags: --allow-natives-syntax --enable-inspector
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
Debug = debug.Debug
|
||||
|
||||
@ -17,14 +17,10 @@ assertEquals("Cat", foo());
|
||||
|
||||
foo();
|
||||
|
||||
var script = Debug.findScript(ChooseAnimal);
|
||||
var new_source =
|
||||
Debug.scriptSource(ChooseAnimal).replace('Cat', "Cap' + 'y' + 'bara");
|
||||
print('new source: ' + new_source);
|
||||
|
||||
var orig_animal = "Cat";
|
||||
var patch_pos = script.source.indexOf(orig_animal);
|
||||
var new_animal_patch = "Cap' + 'y' + 'bara";
|
||||
|
||||
var change_log = new Array();
|
||||
|
||||
Debug.LiveEdit.TestApi.ApplySingleChunkPatch(script, patch_pos, orig_animal.length, new_animal_patch, change_log);
|
||||
%LiveEditPatchScript(ChooseAnimal, new_source);
|
||||
|
||||
assertEquals("Capybara", foo());
|
||||
|
@ -25,6 +25,7 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
Debug = debug.Debug
|
||||
|
||||
@ -46,14 +47,9 @@ function Test(old_expression, new_expression) {
|
||||
assertEquals("Cat", ChooseAnimalArray[i]());
|
||||
}
|
||||
|
||||
var script = Debug.findScript(ChooseAnimalArray[0]);
|
||||
|
||||
var patch_pos = script.source.indexOf(old_expression);
|
||||
var new_animal_patch = new_expression;
|
||||
|
||||
var change_log = new Array();
|
||||
Debug.LiveEdit.TestApi.ApplySingleChunkPatch(script, patch_pos,
|
||||
old_expression.length, new_expression, change_log);
|
||||
var new_source =
|
||||
Debug.scriptSource(ChooseAnimalArray[0]).replace(old_expression, new_expression);
|
||||
%LiveEditPatchScript(ChooseAnimalArray[0], new_source);
|
||||
|
||||
for (var i = 0; i < ChooseAnimalArray.length; i++) {
|
||||
assertEquals("Capybara", ChooseAnimalArray[i]());
|
||||
|
@ -25,6 +25,7 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
Debug = debug.Debug
|
||||
|
||||
@ -47,9 +48,7 @@ eval("var something1 = 25; \n"
|
||||
assertEquals("Cat", ChooseAnimal());
|
||||
assertEquals(25, something1);
|
||||
|
||||
var script = Debug.findScript(ChooseAnimal);
|
||||
|
||||
var new_source = script.source.replace("Cat", "Cap' + 'yb' + 'ara");
|
||||
var new_source = Debug.scriptSource(ChooseAnimal).replace("Cat", "Cap' + 'yb' + 'ara");
|
||||
var new_source = new_source.replace("25", "26");
|
||||
var new_source = new_source.replace("Help", "Hello");
|
||||
var new_source = new_source.replace("17", "18");
|
||||
@ -62,10 +61,7 @@ var new_source = new_source.replace("17", "18");
|
||||
var new_source = new_source.replace("// Array", "Array");
|
||||
print("new source: " + new_source);
|
||||
|
||||
var change_log = new Array();
|
||||
var result = Debug.LiveEdit.SetScriptSource(script, new_source, false, change_log);
|
||||
print("Result: " + JSON.stringify(result) + "\n");
|
||||
print("Change log: " + JSON.stringify(change_log) + "\n");
|
||||
%LiveEditPatchScript(ChooseAnimal, new_source);
|
||||
|
||||
assertEquals("Capybara", ChooseAnimal());
|
||||
// Global variable do not get changed (without restarting script).
|
||||
|
@ -34,6 +34,7 @@
|
||||
// change they are 114 characters away from each other. New instance of Code is
|
||||
// required when those numbers cross the border value of 64 (in any direction).
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
Debug = debug.Debug
|
||||
|
||||
eval(
|
||||
@ -46,19 +47,13 @@ eval(
|
||||
"}"
|
||||
);
|
||||
|
||||
var script = Debug.findScript(BeingReplaced);
|
||||
|
||||
var orig_body = "{}";
|
||||
var patch_pos = script.source.indexOf(orig_body);
|
||||
// Line long enough to change rinfo encoding.
|
||||
var new_body_patch = "{return 'Capybara';" +
|
||||
" " +
|
||||
"}";
|
||||
|
||||
var change_log = new Array();
|
||||
function Changer() {
|
||||
Debug.LiveEdit.TestApi.ApplySingleChunkPatch(script, patch_pos, orig_body.length, new_body_patch, change_log);
|
||||
print("Change log: " + JSON.stringify(change_log) + "\n");
|
||||
// Line long enough to change rinfo encoding.
|
||||
var new_source =
|
||||
Debug.scriptSource(BeingReplaced).replace("{}", "{return 'Capybara';" +
|
||||
" " +
|
||||
"}");
|
||||
%LiveEditPatchScript(BeingReplaced, new_source);
|
||||
}
|
||||
|
||||
function NoOp() {
|
||||
|
@ -2,25 +2,23 @@
|
||||
// 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
|
||||
var counter = 0;
|
||||
var exception = null;
|
||||
|
||||
function f() {
|
||||
if (++counter > 5) return;
|
||||
debugger;
|
||||
return counter;
|
||||
}
|
||||
};
|
||||
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
if (event != Debug.DebugEvent.Break) return;
|
||||
try {
|
||||
var script = Debug.findScript(f);
|
||||
var original = 'debugger;';
|
||||
var patch = 'debugger;\n';
|
||||
var position = script.source.indexOf(original);
|
||||
Debug.LiveEdit.TestApi.ApplySingleChunkPatch(
|
||||
script, position, original.length, patch, []);
|
||||
%LiveEditPatchScript(f, Debug.scriptSource(f).replace(original, patch));
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
|
@ -25,22 +25,20 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
Debug = debug.Debug;
|
||||
Debug.setListener(listener);
|
||||
|
||||
SlimFunction = eval(
|
||||
"(function() {\n " +
|
||||
" return 'Cat';\n" +
|
||||
"})\n"
|
||||
);
|
||||
'(function f() {\n ' +
|
||||
' return \'Cat\';\n' +
|
||||
'})\n');
|
||||
|
||||
var script = Debug.findScript(SlimFunction);
|
||||
|
||||
Debug.setScriptBreakPointById(script.id, 1, 0);
|
||||
|
||||
var orig_animal = "'Cat'";
|
||||
var patch_pos = script.source.indexOf(orig_animal);
|
||||
var new_animal_patch = "'Capybara'";
|
||||
|
||||
debugger_handler = (function() {
|
||||
@ -50,14 +48,8 @@ debugger_handler = (function() {
|
||||
return;
|
||||
}
|
||||
already_called = true;
|
||||
|
||||
var change_log = new Array();
|
||||
try {
|
||||
Debug.LiveEdit.TestApi.ApplySingleChunkPatch(script, patch_pos,
|
||||
orig_animal.length, new_animal_patch, change_log);
|
||||
} finally {
|
||||
print("Change log: " + JSON.stringify(change_log) + "\n");
|
||||
}
|
||||
%LiveEditPatchScript(
|
||||
SlimFunction, script.source.replace(orig_animal, new_animal_patch));
|
||||
};
|
||||
})();
|
||||
|
||||
|
@ -2,9 +2,9 @@
|
||||
// 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 BestEditor() {
|
||||
return 'Emacs';
|
||||
}
|
||||
@ -39,11 +39,8 @@ function listener(event, exec_state, event_data, data) {
|
||||
};
|
||||
|
||||
function Replace(fun, original, patch) {
|
||||
var script = Debug.findScript(fun);
|
||||
if (fun.toString().indexOf(original) < 0) return;
|
||||
var patch_pos = script.source.indexOf(original);
|
||||
var change_log = [];
|
||||
Debug.LiveEdit.TestApi.ApplySingleChunkPatch(script, patch_pos, original.length, patch, change_log);
|
||||
%LiveEditPatchScript(fun, Debug.scriptSource(fun).replace(original, patch));
|
||||
}
|
||||
|
||||
Debug.setListener(listener);
|
||||
@ -56,7 +53,7 @@ Debug.setListener(null);
|
||||
|
||||
assertNull(exception);
|
||||
assertEquals(["Emacs", "Eclipse", "Vim"], results);
|
||||
print(JSON.stringify(log, 1));
|
||||
// TODO(kozyatinskiy): uncomment lines with new liveedit implementation.
|
||||
assertEquals([
|
||||
"debugger;",
|
||||
"results.push(BestEditor());",
|
||||
@ -65,12 +62,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);"
|
||||
|
@ -2,8 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
||||
// Test that live-editing a frame that uses new.target fails.
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
Debug = debug.Debug
|
||||
var calls = 0;
|
||||
@ -43,16 +43,11 @@ function ExecuteInDebugContext(f) {
|
||||
|
||||
function Replace(fun, original, patch) {
|
||||
ExecuteInDebugContext(function() {
|
||||
var change_log = [];
|
||||
try {
|
||||
var script = Debug.findScript(fun);
|
||||
var patch_pos = script.source.indexOf(original);
|
||||
Debug.LiveEdit.TestApi.ApplySingleChunkPatch(
|
||||
script, patch_pos, original.length, patch, change_log);
|
||||
%LiveEditPatchScript(fun, Debug.scriptSource(fun).replace(original, patch));
|
||||
} catch (e) {
|
||||
assertEquals("BLOCKED_NO_NEW_TARGET_ON_RESTART",
|
||||
change_log[0].functions_on_stack[0].replace_problem);
|
||||
assertInstanceof(e, Debug.LiveEdit.Failure);
|
||||
// TODO(kozyatinskiy): message should be BLOCKED_BY_NEW_TARGET_IN_RESTART_FRAME.
|
||||
assertEquals(e, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
|
||||
exceptions++;
|
||||
}
|
||||
});
|
||||
|
@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
// Test that live-editing a frame to introduce new.target fails.
|
||||
|
||||
Debug = debug.Debug
|
||||
@ -40,16 +40,11 @@ function ExecuteInDebugContext(f) {
|
||||
|
||||
function Replace(fun, original, patch) {
|
||||
ExecuteInDebugContext(function() {
|
||||
var change_log = [];
|
||||
try {
|
||||
var script = Debug.findScript(fun);
|
||||
var patch_pos = script.source.indexOf(original);
|
||||
Debug.LiveEdit.TestApi.ApplySingleChunkPatch(
|
||||
script, patch_pos, original.length, patch, change_log);
|
||||
%LiveEditPatchScript(fun, Debug.scriptSource(fun).replace(original, patch));
|
||||
} catch (e) {
|
||||
assertEquals("BLOCKED_NO_NEW_TARGET_ON_RESTART",
|
||||
change_log[0].functions_on_stack[0].replace_problem);
|
||||
assertInstanceof(e, Debug.LiveEdit.Failure);
|
||||
// TODO(kozyatinskiy): message should be BLOCKED_BY_NEW_TARGET_IN_RESTART_FRAME.
|
||||
assertEquals(e, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
|
||||
exceptions++;
|
||||
}
|
||||
});
|
||||
|
@ -2,10 +2,10 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
||||
// Test that live-editing a frame above one that uses new.target succeeds.
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
Debug = debug.Debug
|
||||
Debug = debug.Debug;
|
||||
var wrapper_calls = 0;
|
||||
var construct_calls = 0;
|
||||
var exceptions = 0;
|
||||
@ -47,10 +47,7 @@ function Replace(fun, original, patch) {
|
||||
ExecuteInDebugContext(function() {
|
||||
var change_log = [];
|
||||
try {
|
||||
var script = Debug.findScript(fun);
|
||||
var patch_pos = script.source.indexOf(original);
|
||||
Debug.LiveEdit.TestApi.ApplySingleChunkPatch(
|
||||
script, patch_pos, original.length, patch, change_log);
|
||||
%LiveEditPatchScript(fun, Debug.scriptSource(fun).replace(original, patch));
|
||||
} catch (e) {
|
||||
exceptions++;
|
||||
}
|
||||
|
@ -2,9 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
var Debug = debug.Debug;
|
||||
var LiveEdit = Debug.LiveEdit;
|
||||
|
||||
unique_id = 0;
|
||||
|
||||
@ -62,15 +61,8 @@ function ExecuteInDebugContext(f) {
|
||||
|
||||
function patch(fun, from, to) {
|
||||
function debug() {
|
||||
var log = new Array();
|
||||
var script = Debug.findScript(fun);
|
||||
var pos = script.source.indexOf(from);
|
||||
try {
|
||||
LiveEdit.TestApi.ApplySingleChunkPatch(script, pos, from.length, to,
|
||||
log);
|
||||
} finally {
|
||||
print("Change log: " + JSON.stringify(log) + "\n");
|
||||
}
|
||||
%LiveEditPatchScript(fun, script.source.replace(from, to));
|
||||
}
|
||||
ExecuteInDebugContext(debug);
|
||||
}
|
||||
@ -84,8 +76,9 @@ function patch(fun, from, to) {
|
||||
function attempt_gen_patch() {
|
||||
assertFalse(gen_patch_attempted);
|
||||
gen_patch_attempted = true;
|
||||
assertThrows(function() { patch(generator, "'Cat'", "'Capybara'") },
|
||||
LiveEdit.Failure);
|
||||
assertThrowsEquals(function() {
|
||||
patch(generator, '\'Cat\'', '\'Capybara\'')
|
||||
}, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
|
||||
};
|
||||
var iter = generator(attempt_gen_patch);
|
||||
assertIteratorResult(undefined, false, iter.next());
|
||||
@ -104,8 +97,10 @@ function patch(fun, from, to) {
|
||||
// Patching will fail however when a live iterator is suspended.
|
||||
iter = generator(function(){});
|
||||
assertIteratorResult(undefined, false, iter.next());
|
||||
assertThrows(function() { patch(generator, "'Capybara'", "'Tapir'") },
|
||||
LiveEdit.Failure);
|
||||
// TODO(kozyatinskiy): message should be BLOCKED_BY_RUNNING_GENERATOR.
|
||||
assertThrowsEquals(function() {
|
||||
patch(generator, '\'Capybara\'', '\'Tapir\'')
|
||||
}, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
|
||||
assertIteratorResult("Capybara", true, iter.next());
|
||||
|
||||
// Try to patch functions with activations inside and outside generator
|
||||
@ -123,8 +118,9 @@ function patch(fun, from, to) {
|
||||
}
|
||||
fun_patch_attempted = true;
|
||||
// Patching outside a generator activation must fail.
|
||||
assertThrows(function() { patch(fun_outside, "'Cat'", "'Cobra'") },
|
||||
LiveEdit.Failure);
|
||||
assertThrowsEquals(function() {
|
||||
patch(fun_outside, '\'Cat\'', '\'Cobra\'')
|
||||
}, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
|
||||
// Patching inside a generator activation may succeed.
|
||||
patch(fun_inside, "'Cat'", "'Koala'");
|
||||
}
|
||||
|
@ -2,8 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
var Debug = debug.Debug;
|
||||
var LiveEdit = Debug.LiveEdit;
|
||||
|
||||
unique_id = 0;
|
||||
|
||||
@ -69,16 +70,8 @@ function ExecuteInDebugContext(f) {
|
||||
|
||||
function patch(fun, from, to) {
|
||||
function debug() {
|
||||
var log = new Array();
|
||||
var script = Debug.findScript(fun);
|
||||
var pos = script.source.indexOf(from);
|
||||
print(`pos ${pos}`);
|
||||
try {
|
||||
LiveEdit.TestApi.ApplySingleChunkPatch(script, pos, from.length, to,
|
||||
log);
|
||||
} finally {
|
||||
print("Change log: " + JSON.stringify(log) + "\n");
|
||||
}
|
||||
%LiveEditPatchScript(fun, script.source.replace(from, to));
|
||||
}
|
||||
ExecuteInDebugContext(debug);
|
||||
}
|
||||
@ -92,8 +85,9 @@ function patch(fun, from, to) {
|
||||
function attempt_patch() {
|
||||
assertFalse(patch_attempted);
|
||||
patch_attempted = true;
|
||||
assertThrows(function() { patch(asyncfn, "'Cat'", "'Capybara'") },
|
||||
LiveEdit.Failure);
|
||||
assertThrowsEquals(function() {
|
||||
patch(asyncfn, '\'Cat\'', '\'Capybara\'')
|
||||
}, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
|
||||
};
|
||||
var promise = asyncfn(attempt_patch);
|
||||
// Patch should not succeed because there is a live async function activation
|
||||
@ -112,15 +106,17 @@ 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})});
|
||||
assertThrows(function() { patch(asyncfn, "'Capybara'", "'Tapir'") },
|
||||
LiveEdit.Failure);
|
||||
// TODO(kozyatinskiy): message should be BLOCKED_BY_RUNNING_GENERATOR.
|
||||
assertThrowsEquals(function() {
|
||||
patch(asyncfn, '\'Capybara\'', '\'Tapir\'')
|
||||
}, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
|
||||
resolve();
|
||||
assertPromiseValue("Capybara", promise);
|
||||
|
||||
// Try to patch functions with activations inside and outside async
|
||||
// function activations. We should succeed in the former case, but not in the
|
||||
// latter.
|
||||
var fun_outside = MakeFunction();
|
||||
var fun_outside = eval('((callback) => { callback(); return \'Cat\';})');
|
||||
var fun_inside = MakeFunction();
|
||||
var fun_patch_attempted = false;
|
||||
var fun_patch_restarted = false;
|
||||
@ -132,15 +128,18 @@ function patch(fun, from, to) {
|
||||
}
|
||||
fun_patch_attempted = true;
|
||||
// Patching outside an async function activation must fail.
|
||||
assertThrows(function() { patch(fun_outside, "'Cat'", "'Cobra'") },
|
||||
LiveEdit.Failure);
|
||||
assertThrowsEquals(function() {
|
||||
patch(fun_outside, '\'Cat\'', '\'Cobra\'')
|
||||
}, 'LiveEdit failed: BLOCKED_BY_FUNCTION_BELOW_NON_DROPPABLE_FRAME');
|
||||
// Patching inside an async function activation may succeed.
|
||||
patch(fun_inside, "'Cat'", "'Koala'");
|
||||
}
|
||||
promise = asyncfn(function() { return fun_inside(attempt_fun_patches) });
|
||||
result = fun_outside(() => asyncfn(function() {
|
||||
return fun_inside(attempt_fun_patches);
|
||||
}));
|
||||
assertEquals('Cat',
|
||||
fun_outside(function () {
|
||||
assertPromiseValue('Capybara', promise);
|
||||
assertEquals(result, 'Cat');
|
||||
assertTrue(fun_patch_restarted);
|
||||
assertTrue(fun_inside.toString().includes("'Koala'"));
|
||||
}));
|
||||
|
@ -311,11 +311,6 @@ class DebugWrapper {
|
||||
return scopes;
|
||||
}
|
||||
|
||||
get LiveEdit() {
|
||||
const debugContext = %GetDebugContext();
|
||||
return debugContext.Debug.LiveEdit;
|
||||
}
|
||||
|
||||
// --- Internal methods. -----------------------------------------------------
|
||||
|
||||
getNextMessageId() {
|
||||
|
@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --no-always-opt
|
||||
|
||||
let {session, contextGroup, Protocol} =
|
||||
InspectorTest.start('Tests Debugger.setScriptSource');
|
||||
|
||||
|
@ -11,9 +11,13 @@ Running test: testIncorrectScriptId
|
||||
|
||||
Running test: testSourceWithSyntaxError
|
||||
{
|
||||
error : {
|
||||
code : -32000
|
||||
message : Uncaught [object Object]
|
||||
}
|
||||
id : <messageId>
|
||||
result : {
|
||||
exceptionDetails : {
|
||||
columnNumber : 0
|
||||
exceptionId : <exceptionId>
|
||||
lineNumber : 0
|
||||
text : Invalid or unexpected token
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,9 @@ TestExpression(2,4) === 8
|
||||
Update current script source 'a * b' -> 'a # b'..
|
||||
{
|
||||
exceptionDetails : {
|
||||
columnNumber : 13
|
||||
columnNumber : 0
|
||||
exceptionId : <exceptionId>
|
||||
lineNumber : 1
|
||||
lineNumber : 0
|
||||
text : Invalid or unexpected token
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user