summaryrefslogtreecommitdiff
path: root/libdeno/binding.cc
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2018-11-29 15:40:04 -0500
committerRyan Dahl <ry@tinyclouds.org>2019-01-03 09:45:40 -0500
commitbba0ed3185f30218e015ccd32cc0c8ae14ebba01 (patch)
treeaa00e844bf262e6a81dda890d0755dcca4ac7f58 /libdeno/binding.cc
parentcbb18a596a0696b65fcb116744ee86492421ec2b (diff)
Add ESM support to libdeno
Introduces deno_execute_mod() for executing ES modules.
Diffstat (limited to 'libdeno/binding.cc')
-rw-r--r--libdeno/binding.cc162
1 files changed, 149 insertions, 13 deletions
diff --git a/libdeno/binding.cc b/libdeno/binding.cc
index 9e93bfca8..f05d20757 100644
--- a/libdeno/binding.cc
+++ b/libdeno/binding.cc
@@ -285,7 +285,7 @@ void Send(const v8::FunctionCallbackInfo<v8::Value>& args) {
DCHECK_EQ(d->isolate_, isolate);
v8::Locker locker(d->isolate_);
- v8::EscapableHandleScope handle_scope(isolate);
+ v8::HandleScope handle_scope(isolate);
CHECK_EQ(d->current_args_, nullptr); // libdeno.send re-entry forbidden.
int32_t req_id = d->next_req_id_++;
@@ -343,34 +343,146 @@ void Shared(v8::Local<v8::Name> property,
info.GetReturnValue().Set(ab);
}
-bool ExecuteV8StringSource(v8::Local<v8::Context> context,
- const char* js_filename,
- v8::Local<v8::String> source) {
+v8::ScriptOrigin ModuleOrigin(v8::Local<v8::Value> resource_name,
+ v8::Isolate* isolate) {
+ return v8::ScriptOrigin(resource_name, v8::Local<v8::Integer>(),
+ v8::Local<v8::Integer>(), v8::Local<v8::Boolean>(),
+ v8::Local<v8::Integer>(), v8::Local<v8::Value>(),
+ v8::Local<v8::Boolean>(), v8::Local<v8::Boolean>(),
+ v8::True(isolate));
+}
+
+void DenoIsolate::ClearModules() {
+ for (auto it = module_map_.begin(); it != module_map_.end(); it++) {
+ it->second.Reset();
+ }
+ module_map_.clear();
+ module_filename_map_.clear();
+}
+
+void DenoIsolate::RegisterModule(const char* filename,
+ v8::Local<v8::Module> module) {
+ int id = module->GetIdentityHash();
+
+ // v8.h says that identity hash is not necessarily unique. It seems it's quite
+ // unique enough for the purposes of O(1000) modules, so we use it as a
+ // hashmap key here. The following check is to detect collisions.
+ CHECK_EQ(0, module_filename_map_.count(id));
+
+ module_filename_map_[id] = filename;
+ module_map_.emplace(std::piecewise_construct, std::make_tuple(filename),
+ std::make_tuple(isolate_, module));
+}
+
+v8::MaybeLocal<v8::Module> CompileModule(v8::Local<v8::Context> context,
+ const char* js_filename,
+ v8::Local<v8::String> source_text) {
auto* isolate = context->GetIsolate();
+
v8::Isolate::Scope isolate_scope(isolate);
- v8::HandleScope handle_scope(isolate);
+ v8::EscapableHandleScope handle_scope(isolate);
+ v8::Context::Scope context_scope(context);
+ auto origin = ModuleOrigin(v8_str(js_filename, true), isolate);
+ v8::ScriptCompiler::Source source(source_text, origin);
+
+ auto maybe_module = v8::ScriptCompiler::CompileModule(isolate, &source);
+
+ if (!maybe_module.IsEmpty()) {
+ auto module = maybe_module.ToLocalChecked();
+ CHECK_EQ(v8::Module::kUninstantiated, module->GetStatus());
+ DenoIsolate* d = FromIsolate(isolate);
+ d->RegisterModule(js_filename, module);
+ }
+
+ return handle_scope.EscapeMaybe(maybe_module);
+}
+
+v8::MaybeLocal<v8::Module> ResolveCallback(v8::Local<v8::Context> context,
+ v8::Local<v8::String> specifier,
+ v8::Local<v8::Module> referrer) {
+ auto* isolate = context->GetIsolate();
+ DenoIsolate* d = FromIsolate(isolate);
+
+ v8::Isolate::Scope isolate_scope(isolate);
+ v8::EscapableHandleScope handle_scope(isolate);
v8::Context::Scope context_scope(context);
- v8::TryCatch try_catch(isolate);
+ int ref_id = referrer->GetIdentityHash();
+ std::string referrer_filename = d->module_filename_map_[ref_id];
- auto name = v8_str(js_filename, true);
+ v8::String::Utf8Value specifier_(isolate, specifier);
+ const char* specifier_c = ToCString(specifier_);
- v8::ScriptOrigin origin(name);
+ CHECK_NE(d->resolve_cb_, nullptr);
+ d->resolve_cb_(d->user_data_, specifier_c, referrer_filename.c_str());
- auto script = v8::Script::Compile(context, source, &origin);
+ if (d->resolve_module_.IsEmpty()) {
+ // Resolution Error.
+ isolate->ThrowException(v8_str("module resolution error"));
+ return v8::MaybeLocal<v8::Module>();
+ } else {
+ auto module = d->resolve_module_.Get(isolate);
+ d->resolve_module_.Reset();
+ return handle_scope.Escape(module);
+ }
+}
- if (script.IsEmpty()) {
+void DenoIsolate::ResolveOk(const char* filename, const char* source) {
+ CHECK(resolve_module_.IsEmpty());
+ auto count = module_map_.count(filename);
+ if (count == 1) {
+ auto module = module_map_[filename].Get(isolate_);
+ resolve_module_.Reset(isolate_, module);
+ } else {
+ CHECK_EQ(count, 0);
+ v8::HandleScope handle_scope(isolate_);
+ auto context = context_.Get(isolate_);
+ v8::TryCatch try_catch(isolate_);
+ auto maybe_module = CompileModule(context, filename, v8_str(source));
+ if (maybe_module.IsEmpty()) {
+ DCHECK(try_catch.HasCaught());
+ HandleException(context, try_catch.Exception());
+ } else {
+ auto module = maybe_module.ToLocalChecked();
+ resolve_module_.Reset(isolate_, module);
+ }
+ }
+}
+
+bool ExecuteMod(v8::Local<v8::Context> context, const char* js_filename,
+ const char* js_source) {
+ auto* isolate = context->GetIsolate();
+ v8::Isolate::Scope isolate_scope(isolate);
+ v8::HandleScope handle_scope(isolate);
+ v8::Context::Scope context_scope(context);
+
+ auto source = v8_str(js_source, true);
+
+ v8::TryCatch try_catch(isolate);
+
+ auto maybe_module = CompileModule(context, js_filename, source);
+
+ if (maybe_module.IsEmpty()) {
DCHECK(try_catch.HasCaught());
HandleException(context, try_catch.Exception());
return false;
}
+ DCHECK(!try_catch.HasCaught());
- auto result = script.ToLocalChecked()->Run(context);
+ auto module = maybe_module.ToLocalChecked();
+ auto maybe_ok = module->InstantiateModule(context, ResolveCallback);
+ if (maybe_ok.IsNothing()) {
+ return false;
+ }
+
+ CHECK_EQ(v8::Module::kInstantiated, module->GetStatus());
+ auto result = module->Evaluate(context);
if (result.IsEmpty()) {
DCHECK(try_catch.HasCaught());
- HandleException(context, try_catch.Exception());
+ CHECK_EQ(v8::Module::kErrored, module->GetStatus());
+ HandleException(context, module->GetException());
return false;
}
@@ -382,8 +494,32 @@ bool Execute(v8::Local<v8::Context> context, const char* js_filename,
auto* isolate = context->GetIsolate();
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
+ v8::Context::Scope context_scope(context);
+
auto source = v8_str(js_source, true);
- return ExecuteV8StringSource(context, js_filename, source);
+ auto name = v8_str(js_filename, true);
+
+ v8::TryCatch try_catch(isolate);
+
+ v8::ScriptOrigin origin(name);
+
+ auto script = v8::Script::Compile(context, source, &origin);
+
+ if (script.IsEmpty()) {
+ DCHECK(try_catch.HasCaught());
+ HandleException(context, try_catch.Exception());
+ return false;
+ }
+
+ auto result = script.ToLocalChecked()->Run(context);
+
+ if (result.IsEmpty()) {
+ DCHECK(try_catch.HasCaught());
+ HandleException(context, try_catch.Exception());
+ return false;
+ }
+
+ return true;
}
void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context) {