diff --git a/js/libdeno.ts b/js/libdeno.ts index e67a021c77f9ab..cbd9a60f053dc2 100644 --- a/js/libdeno.ts +++ b/js/libdeno.ts @@ -18,6 +18,8 @@ interface Libdeno { shared: ArrayBuffer; + builtinModules: { [s: string]: object }; + setGlobalErrorHandler: ( handler: ( message: string, diff --git a/libdeno/binding.cc b/libdeno/binding.cc index f05d20757c9178..da3ce198d098ea 100644 --- a/libdeno/binding.cc +++ b/libdeno/binding.cc @@ -323,6 +323,23 @@ void Send(const v8::FunctionCallbackInfo& args) { } } +v8::Local DenoIsolate::GetBuiltinModules() { + v8::EscapableHandleScope handle_scope(isolate_); + if (builtin_modules_.IsEmpty()) { + builtin_modules_.Reset(isolate_, v8::Object::New(isolate_)); + } + return handle_scope.Escape(builtin_modules_.Get(isolate_)); +} + +void BuiltinModules(v8::Local property, + const v8::PropertyCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + DenoIsolate* d = FromIsolate(isolate); + DCHECK_EQ(d->isolate_, isolate); + v8::Locker locker(d->isolate_); + info.GetReturnValue().Set(d->GetBuiltinModules()); +} + void Shared(v8::Local property, const v8::PropertyCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); @@ -408,6 +425,44 @@ v8::MaybeLocal ResolveCallback(v8::Local context, v8::EscapableHandleScope handle_scope(isolate); v8::Context::Scope context_scope(context); + v8::String::Utf8Value specifier_utf8val(isolate, specifier); + const char* specifier_cstr = ToCString(specifier_utf8val); + + auto builtin_modules = d->GetBuiltinModules(); + bool has_builtin = builtin_modules->Has(context, specifier).ToChecked(); + if (has_builtin) { + auto val = builtin_modules->Get(context, specifier).ToLocalChecked(); + CHECK(val->IsObject()); + auto obj = val->ToObject(isolate); + + // In order to export obj as a module, we must iterate over its properties + // and export them each individually. + // TODO Find a better way to do this. + std::string src = "let globalEval = eval\nlet g = globalEval('this');\n"; + auto names = obj->GetOwnPropertyNames(context).ToLocalChecked(); + for (uint32_t i = 0; i < names->Length(); i++) { + auto name = names->Get(context, i).ToLocalChecked(); + v8::String::Utf8Value name_utf8val(isolate, name); + const char* name_cstr = ToCString(name_utf8val); + // TODO use format string. + src.append("export const "); + src.append(name_cstr); + src.append(" = g.libdeno.builtinModules."); + src.append(specifier_cstr); + src.append("."); + src.append(name_cstr); + src.append(";\n"); + } + auto export_str = v8_str(src.c_str(), true); + + auto module = + CompileModule(context, specifier_cstr, export_str).ToLocalChecked(); + auto maybe_ok = module->InstantiateModule(context, ResolveCallback); + CHECK(!maybe_ok.IsNothing()); + + return handle_scope.Escape(module); + } + int ref_id = referrer->GetIdentityHash(); std::string referrer_filename = d->module_filename_map_[ref_id]; @@ -439,7 +494,7 @@ void DenoIsolate::ResolveOk(const char* filename, const char* source) { v8::HandleScope handle_scope(isolate_); auto context = context_.Get(isolate_); v8::TryCatch try_catch(isolate_); - auto maybe_module = CompileModule(context, filename, v8_str(source)); + auto maybe_module = CompileModule(context, filename, v8_str(source, true)); if (maybe_module.IsEmpty()) { DCHECK(try_catch.HasCaught()); HandleException(context, try_catch.Exception()); @@ -545,6 +600,11 @@ void InitializeContext(v8::Isolate* isolate, v8::Local context) { CHECK(deno_val->SetAccessor(context, deno::v8_str("shared"), Shared) .FromJust()); + + CHECK( + deno_val + ->SetAccessor(context, deno::v8_str("builtinModules"), BuiltinModules) + .FromJust()); } void DenoIsolate::AddIsolate(v8::Isolate* isolate) { diff --git a/libdeno/internal.h b/libdeno/internal.h index d8316ce6b488f4..454b3e2e49b5f7 100644 --- a/libdeno/internal.h +++ b/libdeno/internal.h @@ -45,6 +45,8 @@ class DenoIsolate { void ResolveOk(const char* filename, const char* source); void ClearModules(); + v8::Local GetBuiltinModules(); + v8::Isolate* isolate_; v8::ArrayBuffer::Allocator* array_buffer_allocator_; deno_buf shared_; @@ -63,6 +65,8 @@ class DenoIsolate { // Set by deno_resolve_ok v8::Persistent resolve_module_; + v8::Persistent builtin_modules_; + v8::Persistent context_; std::map> async_data_map_; std::map> pending_promise_map_; @@ -108,9 +112,15 @@ void Recv(const v8::FunctionCallbackInfo& args); void Send(const v8::FunctionCallbackInfo& args); void Shared(v8::Local property, const v8::PropertyCallbackInfo& info); +void BuiltinModules(v8::Local property, + const v8::PropertyCallbackInfo& info); static intptr_t external_references[] = { - reinterpret_cast(Print), reinterpret_cast(Recv), - reinterpret_cast(Send), reinterpret_cast(Shared), 0}; + reinterpret_cast(Print), + reinterpret_cast(Recv), + reinterpret_cast(Send), + reinterpret_cast(Shared), + reinterpret_cast(BuiltinModules), + 0}; static const deno_buf empty_buf = {nullptr, 0, nullptr, 0}; diff --git a/libdeno/libdeno_test.cc b/libdeno/libdeno_test.cc index 8b95c1831439eb..6ee8979eec28e9 100644 --- a/libdeno/libdeno_test.cc +++ b/libdeno/libdeno_test.cc @@ -306,3 +306,28 @@ TEST(LibDenoTest, ModuleSnapshot) { delete[] test_snapshot.data_ptr; } + +TEST(LibDenoTest, BuiltinModules) { + static int count = 0; + auto resolve_cb = [](void* user_data, const char* specifier, + const char* referrer) { + EXPECT_STREQ(specifier, "b.js"); + EXPECT_STREQ(referrer, "c.js"); + count++; + auto d = reinterpret_cast(user_data); + deno_resolve_ok(d, "b.js", mod_b); + }; + Deno* d = deno_new(deno_config{0, empty, empty, nullptr, resolve_cb}); + EXPECT_TRUE(deno_execute( + d, d, "setup.js", "libdeno.builtinModules['deno'] = { foo: 'bar' }; \n")); + EXPECT_EQ(count, 0); + EXPECT_TRUE( + deno_execute_mod(d, d, "c.js", + "import { retb } from 'b.js'\n" + "import * as deno from 'deno'\n" + "if (retb() != 'b') throw Error('retb');\n" + // " libdeno.print('deno ' + JSON.stringify(deno));\n" + "if (deno.foo != 'bar') throw Error('foo');\n")); + EXPECT_EQ(count, 1); + deno_delete(d); +}