[inspector] added test infrastructure and test for es6 modules

Test just checks that all basic features are working correctly with modules.

BUG=v8:1569
R=dgozman@chromium.org,alph@chromium.org,adamk@chromium.org

Review-Url: https://codereview.chromium.org/2663743002
Cr-Commit-Position: refs/heads/master@{#42796}
This commit is contained in:
kozyatinskiy 2017-01-30 16:19:41 -08:00 committed by Commit bot
parent 3901e247b3
commit d6db11fd18
9 changed files with 406 additions and 44 deletions

View File

@ -1883,6 +1883,11 @@ void Scope::ResolveVariablesRecursively(ParseInfo* info) {
VariableProxy* Scope::FetchFreeVariables(DeclarationScope* max_outer_scope,
ParseInfo* info,
VariableProxy* stack) {
// Module variables must be allocated before variable resolution
// to ensure that AccessNeedsHoleCheck() can detect import variables.
if (info != nullptr && is_module_scope()) {
AsModuleScope()->AllocateModuleVariables();
}
// Lazy parsed declaration scopes are already partially analyzed. If there are
// unresolved references remaining, they just need to be resolved in outer
// scopes.

View File

@ -9,4 +9,4 @@ Protocol.Debugger.onceScriptParsed().then(message => message.params.scriptId)
.then(InspectorTest.logMessage)
.then(InspectorTest.completeTest);
compileAndRunWithOrigin("() => []", "", 0, 0);
InspectorTest.addScript("() => []");

View File

@ -163,7 +163,7 @@ function foo6() { Promise.resolve().then(() => 42) }`;
function compileScript(source, origin) {
var promise = Protocol.Debugger.onceScriptParsed().then(message => message.params.scriptId);
if (!origin) origin = { name: "", line_offset: 0, column_offset: 0 };
compileAndRunWithOrigin(source, origin.name, origin.line_offset, origin.column_offset);
compileAndRunWithOrigin(source, origin.name, origin.line_offset, origin.column_offset, false);
return promise;
}

View File

@ -198,17 +198,18 @@ class UtilsExtension : public v8::Extension {
static void CompileAndRunWithOrigin(
const v8::FunctionCallbackInfo<v8::Value>& args) {
if (args.Length() != 4 || !args[0]->IsString() || !args[1]->IsString() ||
!args[2]->IsInt32() || !args[3]->IsInt32()) {
if (args.Length() != 5 || !args[0]->IsString() || !args[1]->IsString() ||
!args[2]->IsInt32() || !args[3]->IsInt32() || !args[4]->IsBoolean()) {
fprintf(stderr,
"Internal error: compileAndRunWithOrigin(source, name, line, "
"column).");
"column, is_module).");
Exit();
}
backend_runner_->Append(new ExecuteStringTask(
ToVector(args[0].As<v8::String>()), args[1].As<v8::String>(),
args[2].As<v8::Int32>(), args[3].As<v8::Int32>(), nullptr, nullptr));
args[2].As<v8::Int32>(), args[3].As<v8::Int32>(),
args[4].As<v8::Boolean>(), nullptr, nullptr));
}
static void SetCurrentTimeMSForTest(
@ -313,7 +314,7 @@ class SetTimeoutExtension : public v8::Extension {
task.reset(new ExecuteStringTask(
ToVector(args[0].As<v8::String>()), v8::String::Empty(isolate),
v8::Integer::New(isolate, 0), v8::Integer::New(isolate, 0),
"setTimeout", inspector));
v8::Boolean::New(isolate, false), "setTimeout", inspector));
}
TaskRunner::FromContext(context)->Append(task.release());
}
@ -455,10 +456,10 @@ class FrontendChannelImpl : public InspectorClientImpl::FrontendChannel {
v8::Local<v8::String> result = v8::String::Concat(prefix, message_string);
result = v8::String::Concat(result, suffix);
frontend_task_runner_->Append(
new ExecuteStringTask(ToVector(result), v8::String::Empty(isolate),
v8::Integer::New(isolate, 0),
v8::Integer::New(isolate, 0), nullptr, nullptr));
frontend_task_runner_->Append(new ExecuteStringTask(
ToVector(result), v8::String::Empty(isolate),
v8::Integer::New(isolate, 0), v8::Integer::New(isolate, 0),
v8::Boolean::New(isolate, false), nullptr, nullptr));
}
private:

View File

@ -135,13 +135,17 @@ InspectorTest.completeTest = function()
InspectorTest.completeTestAfterPendingTimeouts = function()
{
Protocol.Runtime.evaluate({
expression: "new Promise(resolve => setTimeout(resolve, 0))",
awaitPromise: true }).then(InspectorTest.completeTest);
InspectorTest.waitPendingTasks().then(InspectorTest.completeTest);
}
InspectorTest.addScript = (string, lineOffset, columnOffset) => compileAndRunWithOrigin(string, "", lineOffset || 0, columnOffset || 0);
InspectorTest.addScriptWithUrl = (string, url) => compileAndRunWithOrigin(string, url, 0, 0);
InspectorTest.waitPendingTasks = function()
{
return Protocol.Runtime.evaluate({ expression: "new Promise(r => setTimeout(r, 0))//# sourceURL=wait-pending-tasks.js", awaitPromise: true });
}
InspectorTest.addScript = (string, lineOffset, columnOffset) => compileAndRunWithOrigin(string, "", lineOffset || 0, columnOffset || 0, false);
InspectorTest.addScriptWithUrl = (string, url) => compileAndRunWithOrigin(string, url, 0, 0, false);
InspectorTest.addModule = (string, url, lineOffset, columnOffset) => compileAndRunWithOrigin(string, url, lineOffset || 0, columnOffset || 0, true);
InspectorTest.startDumpingProtocolMessages = function()
{

View File

@ -0,0 +1,236 @@
Checks basic ES6 modules support.
{
method : Debugger.scriptParsed
params : {
endColumn : 17
endLine : 5
executionContextId : <executionContextId>
hasSourceURL : false
hash : 9C014F7249BAFA12B91017817AD15091D01A9155
isLiveEdit : false
scriptId : <scriptId>
sourceMapURL :
startColumn : 0
startLine : 0
url : module1
}
}
{
method : Debugger.scriptParsed
params : {
endColumn : 17
endLine : 5
executionContextId : <executionContextId>
hasSourceURL : false
hash : 443A2FA24A6112E6B9101781E6A19B56BDC396D4
isLiveEdit : false
scriptId : <scriptId>
sourceMapURL :
startColumn : 0
startLine : 0
url : module2
}
}
{
method : Debugger.scriptParsed
params : {
endColumn : 0
endLine : 9
executionContextId : <executionContextId>
hasSourceURL : false
hash : 54D834614FBF9B389082DAE06CD3EFC499BEBF13
isLiveEdit : false
scriptId : <scriptId>
sourceMapURL :
startColumn : 0
startLine : 0
url : module3
}
}
console.log(module1)
foo (module1:2:10)
(anonymous) (module3:3:12)
console.log(42)
(anonymous) (module3:3:8)
console.log(module2)
foo (module2:2:10)
(anonymous) (module3:4:12)
console.log(239)
(anonymous) (module3:4:8)
{
method : Debugger.paused
params : {
callFrames : [
[0] : {
callFrameId : <callFrameId>
functionLocation : {
columnNumber : 0
lineNumber : 0
scriptId : <scriptId>
}
functionName :
location : {
columnNumber : 0
lineNumber : 7
scriptId : <scriptId>
}
scopeChain : [
[0] : {
endLocation : {
columnNumber : 0
lineNumber : 8
scriptId : <scriptId>
}
object : {
className : Object
description : Object
objectId : <objectId>
type : object
}
startLocation : {
columnNumber : 0
lineNumber : 0
scriptId : <scriptId>
}
type : module
}
[1] : {
object : {
className : global
description : global
objectId : <objectId>
type : object
}
type : global
}
]
this : {
type : undefined
}
}
]
hitBreakpoints : [
]
reason : other
}
}
{
id : <messageId>
result : {
result : [
[0] : {
configurable : true
enumerable : true
isOwn : true
name : foo1
value : {
className : Function
description : function foo() { console.log('module1'); return 42; }
objectId : <objectId>
type : function
}
writable : true
}
[1] : {
configurable : true
enumerable : true
isOwn : true
name : foo2
value : {
className : Function
description : function foo() { console.log('module2'); return 239; }
objectId : <objectId>
type : function
}
writable : true
}
[2] : {
configurable : true
enumerable : true
isOwn : true
name : a1
value : {
description : 1
type : number
value : 1
}
writable : true
}
[3] : {
configurable : true
enumerable : true
isOwn : true
name : a2
value : {
description : 2
type : number
value : 2
}
writable : true
}
]
}
}
{
method : Debugger.scriptFailedToParse
params : {
endColumn : 1
endLine : 0
executionContextId : <executionContextId>
hasSourceURL : false
hash : FF746120E4E4F1BA4CB5762843D429DC872EBA18
scriptId : <scriptId>
sourceMapURL :
startColumn : 0
startLine : 0
url : module4
}
}
{
method : Runtime.exceptionThrown
params : {
exceptionDetails : {
columnNumber : 0
exception : {
className : SyntaxError
description : SyntaxError: Unexpected token }
objectId : <objectId>
preview : {
description : SyntaxError: Unexpected token }
overflow : false
properties : [
[0] : {
name : stack
type : string
value : SyntaxError: Unexpected token }
}
[1] : {
name : message
type : string
value : Unexpected token }
}
]
subtype : error
type : object
}
subtype : error
type : object
}
exceptionId : <exceptionId>
executionContextId : <executionContextId>
lineNumber : 0
scriptId : <scriptId>
stackTrace : {
callFrames : [
]
}
text : Uncaught SyntaxError: Unexpected token }
url : module4
}
timestamp : <timestamp>
}
}

View File

@ -0,0 +1,64 @@
// 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.
print('Checks basic ES6 modules support.');
var module1 = `
export function foo() {
console.log('module1');
return 42;
}
export let a1 = 1`;
var module2 = `
export function foo() {
console.log('module2');
return 239;
}
export let a2 = 2`;
var module3 = `
import { foo as foo1 } from 'module1';
import { foo as foo2 } from 'module2';
console.log(foo1());
console.log(foo2());
import { a1 } from 'module1';
import { a2 } from 'module2';
debugger;
`;
var module4 = '}';
InspectorTest.setupScriptMap();
// We get scriptParsed events for modules ..
Protocol.Debugger.onScriptParsed(message => {
if (message.params.url === 'wait-pending-tasks.js') return;
InspectorTest.logMessage(message);
});
// .. scriptFailed to parse for modules with syntax error ..
Protocol.Debugger.onScriptFailedToParse(InspectorTest.logMessage);
// .. API messages from modules contain correct stack trace ..
Protocol.Runtime.onConsoleAPICalled(message => {
InspectorTest.log(`console.log(${message.params.args[0].value})`);
InspectorTest.logCallFrames(message.params.stackTrace.callFrames);
InspectorTest.log('');
});
// .. we could break inside module and scope contains correct list of variables ..
Protocol.Debugger.onPaused(message => {
InspectorTest.logMessage(message);
Protocol.Runtime.getProperties({ objectId: message.params.callFrames[0].scopeChain[0].object.objectId})
.then(InspectorTest.logMessage)
.then(() => Protocol.Debugger.resume());
});
// .. we process uncaught errors from modules correctly.
Protocol.Runtime.onExceptionThrown(InspectorTest.logMessage);
Protocol.Runtime.enable();
Protocol.Debugger.enable()
.then(() => InspectorTest.addModule(module1, "module1"))
.then(() => InspectorTest.addModule(module2, "module2"))
.then(() => InspectorTest.addModule(module3, "module3"))
.then(() => InspectorTest.addModule(module4, "module4"))
.then(() => InspectorTest.waitPendingTasks())
.then(InspectorTest.completeTest);

View File

@ -22,6 +22,13 @@ void ReportUncaughtException(v8::Isolate* isolate,
fprintf(stderr, "Unhandle exception: %s\n", message.data());
}
v8::internal::Vector<uint16_t> ToVector(v8::Local<v8::String> str) {
v8::internal::Vector<uint16_t> buffer =
v8::internal::Vector<uint16_t>::New(str->Length());
str->Write(buffer.start(), 0, str->Length());
return buffer;
}
} // namespace
TaskRunner::TaskRunner(v8::ExtensionConfiguration* extensions,
@ -101,6 +108,19 @@ void TaskRunner::Terminate() {
process_queue_semaphore_.Signal();
}
void TaskRunner::RegisterModule(v8::internal::Vector<uint16_t> name,
v8::Local<v8::Module> module) {
modules_[name] = v8::Global<v8::Module>(isolate_, module);
}
v8::MaybeLocal<v8::Module> TaskRunner::ModuleResolveCallback(
v8::Local<v8::Context> context, v8::Local<v8::String> specifier,
v8::Local<v8::Module> referrer) {
std::string str = *v8::String::Utf8Value(specifier);
TaskRunner* runner = TaskRunner::FromContext(context);
return runner->modules_[ToVector(specifier)].Get(runner->isolate_);
}
TaskRunner::Task* TaskRunner::GetNext(bool only_protocol) {
for (;;) {
if (is_terminated_.Value()) return nullptr;
@ -125,17 +145,6 @@ TaskRunner* TaskRunner::FromContext(v8::Local<v8::Context> context) {
context->GetAlignedPointerFromEmbedderData(kTaskRunnerIndex));
}
namespace {
v8::internal::Vector<uint16_t> ToVector(v8::Local<v8::String> str) {
v8::internal::Vector<uint16_t> buffer =
v8::internal::Vector<uint16_t>::New(str->Length());
str->Write(buffer.start(), 0, str->Length());
return buffer;
}
} // namespace
AsyncTask::AsyncTask(const char* task_name,
v8_inspector::V8Inspector* inspector)
: inspector_(task_name ? inspector : nullptr) {
@ -157,20 +166,18 @@ void AsyncTask::Run(v8::Isolate* isolate,
ExecuteStringTask::ExecuteStringTask(
const v8::internal::Vector<uint16_t>& expression,
v8::Local<v8::String> name, v8::Local<v8::Integer> line_offset,
v8::Local<v8::Integer> column_offset, const char* task_name,
v8_inspector::V8Inspector* inspector)
v8::Local<v8::Integer> column_offset, v8::Local<v8::Boolean> is_module,
const char* task_name, v8_inspector::V8Inspector* inspector)
: AsyncTask(task_name, inspector),
expression_(expression),
name_(ToVector(name)),
line_offset_(line_offset.As<v8::Int32>()->Value()),
column_offset_(column_offset.As<v8::Int32>()->Value()) {}
column_offset_(column_offset.As<v8::Int32>()->Value()),
is_module_(is_module->Value()) {}
ExecuteStringTask::ExecuteStringTask(
const v8::internal::Vector<const char>& expression)
: AsyncTask(nullptr, nullptr),
expression_utf8_(expression),
line_offset_(0),
column_offset_(0) {}
: AsyncTask(nullptr, nullptr), expression_utf8_(expression) {}
void ExecuteStringTask::AsyncRun(v8::Isolate* isolate,
const v8::Global<v8::Context>& context) {
@ -188,7 +195,14 @@ void ExecuteStringTask::AsyncRun(v8::Isolate* isolate,
v8::Local<v8::Integer> column_offset =
v8::Integer::New(isolate, column_offset_);
v8::ScriptOrigin origin(name, line_offset, column_offset);
v8::ScriptOrigin origin(
name, line_offset, column_offset,
/* resource_is_shared_cross_origin */ v8::Local<v8::Boolean>(),
/* script_id */ v8::Local<v8::Integer>(),
/* source_map_url */ v8::Local<v8::Value>(),
/* resource_is_opaque */ v8::Local<v8::Boolean>(),
/* is_wasm */ v8::Local<v8::Boolean>(),
v8::Boolean::New(isolate, is_module_));
v8::Local<v8::String> source;
if (expression_.length()) {
source = v8::String::NewFromTwoByte(isolate, expression_.start(),
@ -203,10 +217,24 @@ void ExecuteStringTask::AsyncRun(v8::Isolate* isolate,
}
v8::ScriptCompiler::Source scriptSource(source, origin);
v8::Local<v8::Script> script;
if (!v8::ScriptCompiler::Compile(local_context, &scriptSource)
.ToLocal(&script))
return;
v8::MaybeLocal<v8::Value> result;
result = script->Run(local_context);
if (!is_module_) {
v8::Local<v8::Script> script;
if (!v8::ScriptCompiler::Compile(local_context, &scriptSource)
.ToLocal(&script))
return;
v8::MaybeLocal<v8::Value> result;
result = script->Run(local_context);
} else {
v8::Local<v8::Module> module;
if (!v8::ScriptCompiler::CompileModule(isolate, &scriptSource)
.ToLocal(&module)) {
return;
}
if (!module->Instantiate(local_context, &TaskRunner::ModuleResolveCallback))
return;
v8::Local<v8::Value> result;
if (!module->Evaluate(local_context).ToLocal(&result)) return;
TaskRunner* runner = TaskRunner::FromContext(local_context);
runner->RegisterModule(name_, module);
}
}

View File

@ -5,6 +5,8 @@
#ifndef V8_TEST_INSPECTOR_PROTOCOL_TASK_RUNNER_H_
#define V8_TEST_INSPECTOR_PROTOCOL_TASK_RUNNER_H_
#include <map>
#include "include/v8-inspector.h"
#include "include/v8-platform.h"
#include "include/v8.h"
@ -14,6 +16,16 @@
#include "src/locked-queue-inl.h"
#include "src/vector.h"
struct VectorCompare {
bool operator()(const v8::internal::Vector<uint16_t>& lhs,
const v8::internal::Vector<uint16_t>& rhs) const {
for (int i = 0; i < lhs.length() && i < rhs.length(); ++i) {
if (lhs[i] != rhs[i]) return lhs[i] < rhs[i];
}
return false;
}
};
class TaskRunner : public v8::base::Thread {
public:
class Task {
@ -42,6 +54,12 @@ class TaskRunner : public v8::base::Thread {
void Terminate();
void RegisterModule(v8::internal::Vector<uint16_t> name,
v8::Local<v8::Module> module);
static v8::MaybeLocal<v8::Module> ModuleResolveCallback(
v8::Local<v8::Context> context, v8::Local<v8::String> specifier,
v8::Local<v8::Module> referrer);
private:
void InitializeContext();
Task* GetNext(bool only_protocol);
@ -60,6 +78,10 @@ class TaskRunner : public v8::base::Thread {
v8::internal::LockedQueue<Task*> deffered_queue_;
v8::base::Semaphore process_queue_semaphore_;
std::map<v8::internal::Vector<uint16_t>, v8::Global<v8::Module>,
VectorCompare>
modules_;
int nested_loop_count_;
v8::base::AtomicNumber<int> is_terminated_;
@ -86,7 +108,8 @@ class ExecuteStringTask : public AsyncTask {
ExecuteStringTask(const v8::internal::Vector<uint16_t>& expression,
v8::Local<v8::String> name,
v8::Local<v8::Integer> line_offset,
v8::Local<v8::Integer> column_offset, const char* task_name,
v8::Local<v8::Integer> column_offset,
v8::Local<v8::Boolean> is_module, const char* task_name,
v8_inspector::V8Inspector* inspector);
explicit ExecuteStringTask(
const v8::internal::Vector<const char>& expression);
@ -99,8 +122,9 @@ class ExecuteStringTask : public AsyncTask {
v8::internal::Vector<uint16_t> expression_;
v8::internal::Vector<const char> expression_utf8_;
v8::internal::Vector<uint16_t> name_;
int32_t line_offset_;
int32_t column_offset_;
int32_t line_offset_ = 0;
int32_t column_offset_ = 0;
bool is_module_ = false;
DISALLOW_COPY_AND_ASSIGN(ExecuteStringTask);
};