Commit aa6db9d9 authored by adamk's avatar adamk Committed by Commit bot

[modules] Introduce v8::Module to the API and return it from CompileModule

R=neis@chromium.org
BUG=v8:1569

Review-Url: https://codereview.chromium.org/2347933002
Cr-Commit-Position: refs/heads/master@{#39481}
parent 61a6b6f2
......@@ -1070,6 +1070,26 @@ class V8_EXPORT UnboundScript {
static const int kNoScriptId = 0;
};
/**
* This is an unfinished experimental feature, and is only exposed
* here for internal testing purposes. DO NOT USE.
*
* A compiled JavaScript module.
*/
class V8_EXPORT Module {
public:
/**
* ModuleDeclarationInstantiation
*
* Returns false if an exception occurred during instantiation.
*/
V8_WARN_UNUSED_RESULT bool Instantiate(Local<Context> context);
/**
* ModuleEvaluation
*/
V8_WARN_UNUSED_RESULT MaybeLocal<Value> Evaluate(Local<Context> context);
};
/**
* A compiled JavaScript script, tied to a Context which was active when the
......@@ -1373,18 +1393,17 @@ class V8_EXPORT ScriptCompiler {
static uint32_t CachedDataVersionTag();
/**
* Compile an ES6 module.
*
* This is an unfinished experimental feature, and is only exposed
* here for internal testing purposes.
* Only parsing works at the moment. Do not use.
* here for internal testing purposes. DO NOT USE.
*
* TODO(adamk): Script is likely the wrong return value for this;
* should return some new Module type.
* Compile an ES module, returning a Module that encapsulates
* the compiled code.
*
* Corresponds to the ParseModule abstract operation in the
* ECMAScript specification.
*/
static V8_WARN_UNUSED_RESULT MaybeLocal<Script> CompileModule(
Local<Context> context, Source* source,
CompileOptions options = kNoCompileOptions);
static V8_WARN_UNUSED_RESULT MaybeLocal<Module> CompileModule(
Isolate* isolate, Source* source);
/**
* Compile a function for a given context. This is equivalent to running
......
......@@ -1858,20 +1858,10 @@ MaybeLocal<Value> Script::Run(Local<Context> context) {
i::TimerEventScope<i::TimerEventExecute> timer_scope(isolate);
auto fun = i::Handle<i::JSFunction>::cast(Utils::OpenHandle(this));
i::Handle<i::Object> receiver;
i::Handle<i::Object> receiver = isolate->global_proxy();
Local<Value> result;
if (fun->shared()->scope_info()->scope_type() == i::MODULE_SCOPE) {
receiver = isolate->factory()->undefined_value();
i::Handle<i::Object> argv[] = {
handle(isolate->native_context()->current_module())};
has_pending_exception = !ToLocal<Value>(
i::Execution::Call(isolate, fun, receiver, 1, argv), &result);
} else {
receiver = isolate->global_proxy();
has_pending_exception = !ToLocal<Value>(
i::Execution::Call(isolate, fun, receiver, 0, nullptr), &result);
}
has_pending_exception = !ToLocal<Value>(
i::Execution::Call(isolate, fun, receiver, 0, nullptr), &result);
RETURN_ON_FAILED_EXECUTION(Value);
RETURN_ESCAPED(result);
......@@ -1894,6 +1884,51 @@ Local<UnboundScript> Script::GetUnboundScript() {
i::Handle<i::SharedFunctionInfo>(i::JSFunction::cast(*obj)->shared()));
}
bool Module::Instantiate(Local<Context> v8_context) {
i::Handle<i::Module> self = Utils::OpenHandle(this);
i::Isolate* isolate = self->GetIsolate();
// Already instantiated.
if (self->code()->IsJSFunction()) return true;
i::Handle<i::SharedFunctionInfo> shared(
i::SharedFunctionInfo::cast(self->code()), isolate);
i::Handle<i::Context> context = Utils::OpenHandle(*v8_context);
i::Handle<i::JSFunction> function =
isolate->factory()->NewFunctionFromSharedFunctionInfo(
shared, handle(context->native_context(), isolate));
self->set_code(*function);
// TODO(adamk): This could fail in the future when Instantiate
// does linking.
return true;
}
MaybeLocal<Value> Module::Evaluate(Local<Context> context) {
PREPARE_FOR_EXECUTION_WITH_CONTEXT_IN_RUNTIME_CALL_STATS_SCOPE(
"v8", "V8.Execute", context, Script, Run, MaybeLocal<Value>(),
InternalEscapableScope, true);
i::HistogramTimerScope execute_timer(isolate->counters()->execute(), true);
i::AggregatingHistogramTimerScope timer(isolate->counters()->compile_lazy());
i::TimerEventScope<i::TimerEventExecute> timer_scope(isolate);
i::Handle<i::Module> self = Utils::OpenHandle(this);
// It's an API error to call Evaluate before Instantiate.
CHECK(self->code()->IsJSFunction());
i::Handle<i::JSFunction> function(i::JSFunction::cast(self->code()), isolate);
DCHECK_EQ(i::MODULE_SCOPE, function->shared()->scope_info()->scope_type());
i::Handle<i::Object> receiver = isolate->factory()->undefined_value();
Local<Value> result;
i::Handle<i::Object> argv[] = {self};
has_pending_exception = !ToLocal<Value>(
i::Execution::Call(isolate, function, receiver, arraysize(argv), argv),
&result);
RETURN_ON_FAILED_EXECUTION(Value);
RETURN_ESCAPED(result);
}
MaybeLocal<UnboundScript> ScriptCompiler::CompileUnboundInternal(
Isolate* v8_isolate, Source* source, CompileOptions options,
......@@ -2004,32 +2039,22 @@ Local<Script> ScriptCompiler::Compile(
RETURN_TO_LOCAL_UNCHECKED(Compile(context, source, options), Script);
}
MaybeLocal<Script> ScriptCompiler::CompileModule(Local<Context> context,
Source* source,
CompileOptions options) {
auto isolate = context->GetIsolate();
MaybeLocal<Module> ScriptCompiler::CompileModule(Isolate* isolate,
Source* source) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
auto maybe = CompileUnboundInternal(isolate, source, options, true);
Local<UnboundScript> generic;
if (!maybe.ToLocal(&generic)) return MaybeLocal<Script>();
v8::Context::Scope scope(context);
auto result = generic->BindToCurrentContext();
auto maybe = CompileUnboundInternal(isolate, source, kNoCompileOptions, true);
Local<UnboundScript> unbound;
if (!maybe.ToLocal(&unbound)) return MaybeLocal<Module>();
i::Handle<i::SharedFunctionInfo> shared = Utils::OpenHandle(*generic);
i::Handle<i::SharedFunctionInfo> shared = Utils::OpenHandle(*unbound);
i::Handle<i::FixedArray> regular_exports =
i::handle(shared->scope_info()->ModuleDescriptorInfo()->regular_exports(),
i_isolate);
int regular_exports_length = regular_exports->length();
i::Handle<i::Module> module =
i_isolate->factory()->NewModule(regular_exports_length);
// TODO(neis): Storing the module into the native context is a temporary hack
// to pass it to the Script::Run function. This will be removed once we
// support modules in the API.
i_isolate->native_context()->set_current_module(*module);
i_isolate->factory()->NewModule(shared, regular_exports_length);
// TODO(neis): This will create multiple cells for the same local variable if
// exported under multiple names, which is wrong but cannot be observed at the
......@@ -2044,7 +2069,7 @@ MaybeLocal<Script> ScriptCompiler::CompileModule(Local<Context> context,
i::Module::CreateExport(module, export_name);
}
return result;
return ToApiHandle<Module>(module);
}
......
......@@ -69,7 +69,6 @@ class RegisteredExtension {
static RegisteredExtension* first_extension_;
};
#define OPEN_HANDLE_LIST(V) \
V(Template, TemplateInfo) \
V(FunctionTemplate, FunctionTemplateInfo) \
......@@ -101,6 +100,7 @@ class RegisteredExtension {
V(Symbol, Symbol) \
V(Script, JSFunction) \
V(UnboundScript, SharedFunctionInfo) \
V(Module, Module) \
V(Function, JSReceiver) \
V(Message, JSMessageObject) \
V(Context, Context) \
......
......@@ -460,18 +460,12 @@ ScriptCompiler::CachedData* CompileForCachedData(
// Compile a string within the current v8 context.
MaybeLocal<Script> Shell::CompileString(
Isolate* isolate, Local<String> source, Local<Value> name,
ScriptCompiler::CompileOptions compile_options, SourceType source_type) {
ScriptCompiler::CompileOptions compile_options) {
Local<Context> context(isolate->GetCurrentContext());
ScriptOrigin origin(name);
// TODO(adamk): Make use of compile options for Modules.
if (compile_options == ScriptCompiler::kNoCompileOptions ||
source_type == MODULE) {
if (compile_options == ScriptCompiler::kNoCompileOptions) {
ScriptCompiler::Source script_source(source, origin);
return source_type == SCRIPT
? ScriptCompiler::Compile(context, &script_source,
compile_options)
: ScriptCompiler::CompileModule(context, &script_source,
compile_options);
return ScriptCompiler::Compile(context, &script_source, compile_options);
}
ScriptCompiler::CachedData* data =
......@@ -485,7 +479,6 @@ MaybeLocal<Script> Shell::CompileString(
DCHECK(false); // A new compile option?
}
if (data == NULL) compile_options = ScriptCompiler::kNoCompileOptions;
DCHECK_EQ(SCRIPT, source_type);
MaybeLocal<Script> result =
ScriptCompiler::Compile(context, &cached_source, compile_options);
CHECK(data == NULL || !data->rejected);
......@@ -507,14 +500,31 @@ bool Shell::ExecuteString(Isolate* isolate, Local<String> source,
Local<Context> realm =
Local<Context>::New(isolate, data->realms_[data->realm_current_]);
Context::Scope context_scope(realm);
Local<Script> script;
if (!Shell::CompileString(isolate, source, name, options.compile_options,
source_type).ToLocal(&script)) {
// Print errors that happened during compilation.
if (report_exceptions) ReportException(isolate, &try_catch);
return false;
if (source_type == SCRIPT) {
Local<Script> script;
if (!Shell::CompileString(isolate, source, name, options.compile_options)
.ToLocal(&script)) {
// Print errors that happened during compilation.
if (report_exceptions) ReportException(isolate, &try_catch);
return false;
}
maybe_result = script->Run(realm);
} else {
DCHECK_EQ(MODULE, source_type);
Local<Module> module;
ScriptOrigin origin(name);
ScriptCompiler::Source script_source(source, origin);
// TODO(adamk): Make use of compile options for Modules.
if (!ScriptCompiler::CompileModule(isolate, &script_source)
.ToLocal(&module)) {
// Print errors that happened during compilation.
if (report_exceptions) ReportException(isolate, &try_catch);
return false;
}
// This can't fail until we support linking.
CHECK(module->Instantiate(realm));
maybe_result = module->Evaluate(realm);
}
maybe_result = script->Run(realm);
EmptyMessageQueues(isolate);
data->realm_current_ = data->realm_switch_;
}
......
......@@ -335,8 +335,7 @@ class Shell : public i::AllStatic {
static MaybeLocal<Script> CompileString(
Isolate* isolate, Local<String> source, Local<Value> name,
v8::ScriptCompiler::CompileOptions compile_options,
SourceType source_type);
v8::ScriptCompiler::CompileOptions compile_options);
static bool ExecuteString(Isolate* isolate, Local<String> source,
Local<Value> name, bool print_result,
bool report_exceptions,
......
......@@ -1705,9 +1705,11 @@ Handle<JSGeneratorObject> Factory::NewJSGeneratorObject(
JSGeneratorObject);
}
Handle<Module> Factory::NewModule(int min_size) {
Handle<Module> module = Handle<Module>::cast(NewStruct(MODULE_TYPE));
Handle<Module> Factory::NewModule(Handle<SharedFunctionInfo> code,
int min_size) {
Handle<ObjectHashTable> exports = ObjectHashTable::New(isolate(), min_size);
Handle<Module> module = Handle<Module>::cast(NewStruct(MODULE_TYPE));
module->set_code(*code);
module->set_exports(*exports);
return module;
}
......
......@@ -477,7 +477,7 @@ class Factory final {
Handle<JSGeneratorObject> NewJSGeneratorObject(Handle<JSFunction> function);
Handle<Module> NewModule(int min_size);
Handle<Module> NewModule(Handle<SharedFunctionInfo> code, int min_size);
Handle<JSArrayBuffer> NewJSArrayBuffer(
SharedFlag shared = SharedFlag::kNotShared,
......
......@@ -901,6 +901,8 @@ void Box::BoxVerify() {
void Module::ModuleVerify() {
CHECK(IsModule());
CHECK(code()->IsSharedFunctionInfo() || code()->IsJSFunction());
code()->ObjectVerify();
exports()->ObjectVerify();
// TODO(neis): Check more.
}
......
......@@ -5701,6 +5701,7 @@ BOOL_ACCESSORS(PrototypeInfo, bit_field, should_be_fast_map, kShouldBeFastBit)
ACCESSORS(ContextExtension, scope_info, ScopeInfo, kScopeInfoOffset)
ACCESSORS(ContextExtension, extension, Object, kExtensionOffset)
ACCESSORS(Module, code, Object, kCodeOffset)
ACCESSORS(Module, exports, ObjectHashTable, kExportsOffset)
ACCESSORS(AccessorPair, getter, Object, kGetterOffset)
......
......@@ -1147,6 +1147,7 @@ void Box::BoxPrint(std::ostream& os) { // NOLINT
void Module::ModulePrint(std::ostream& os) { // NOLINT
HeapObject::PrintHeader(os, "Module");
os << "\n - code: " << Brief(code());
os << "\n - exports: " << Brief(exports());
os << "\n";
}
......
......@@ -7871,6 +7871,11 @@ class Module : public Struct {
DECLARE_VERIFIER(Module)
DECLARE_PRINTER(Module)
// The code representing this Module, either a
// SharedFunctionInfo or a JSFunction depending
// on whether it's been instantiated.
DECL_ACCESSORS(code, Object)
DECL_ACCESSORS(exports, ObjectHashTable)
static void CreateExport(Handle<Module> module, Handle<String> name);
......@@ -7878,7 +7883,8 @@ class Module : public Struct {
Handle<Object> value);
static Handle<Object> LoadExport(Handle<Module> module, Handle<String> name);
static const int kExportsOffset = HeapObject::kHeaderSize;
static const int kCodeOffset = HeapObject::kHeaderSize;
static const int kExportsOffset = kCodeOffset + kPointerSize;
static const int kSize = kExportsOffset + kPointerSize;
private:
......
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