Skip to content

Commit

Permalink
Add libdeno.builtinModules (denoland#1463)
Browse files Browse the repository at this point in the history
This is needed to support builtin modules like

    import { open } from "deno"
  • Loading branch information
ry committed Jan 6, 2019
1 parent f37d67e commit 1b7938e
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 3 deletions.
2 changes: 2 additions & 0 deletions js/libdeno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ interface Libdeno {

shared: ArrayBuffer;

builtinModules: { [s: string]: object };

setGlobalErrorHandler: (
handler: (
message: string,
Expand Down
62 changes: 61 additions & 1 deletion libdeno/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,23 @@ void Send(const v8::FunctionCallbackInfo<v8::Value>& args) {
}
}

v8::Local<v8::Object> 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<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Value>& 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<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
Expand Down Expand Up @@ -408,6 +425,44 @@ v8::MaybeLocal<v8::Module> ResolveCallback(v8::Local<v8::Context> 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];

Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -545,6 +600,11 @@ void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> 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) {
Expand Down
14 changes: 12 additions & 2 deletions libdeno/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class DenoIsolate {
void ResolveOk(const char* filename, const char* source);
void ClearModules();

v8::Local<v8::Object> GetBuiltinModules();

v8::Isolate* isolate_;
v8::ArrayBuffer::Allocator* array_buffer_allocator_;
deno_buf shared_;
Expand All @@ -63,6 +65,8 @@ class DenoIsolate {
// Set by deno_resolve_ok
v8::Persistent<v8::Module> resolve_module_;

v8::Persistent<v8::Object> builtin_modules_;

v8::Persistent<v8::Context> context_;
std::map<int32_t, v8::Persistent<v8::Value>> async_data_map_;
std::map<int, v8::Persistent<v8::Value>> pending_promise_map_;
Expand Down Expand Up @@ -108,9 +112,15 @@ void Recv(const v8::FunctionCallbackInfo<v8::Value>& args);
void Send(const v8::FunctionCallbackInfo<v8::Value>& args);
void Shared(v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Value>& info);
void BuiltinModules(v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Value>& info);
static intptr_t external_references[] = {
reinterpret_cast<intptr_t>(Print), reinterpret_cast<intptr_t>(Recv),
reinterpret_cast<intptr_t>(Send), reinterpret_cast<intptr_t>(Shared), 0};
reinterpret_cast<intptr_t>(Print),
reinterpret_cast<intptr_t>(Recv),
reinterpret_cast<intptr_t>(Send),
reinterpret_cast<intptr_t>(Shared),
reinterpret_cast<intptr_t>(BuiltinModules),
0};

static const deno_buf empty_buf = {nullptr, 0, nullptr, 0};

Expand Down
25 changes: 25 additions & 0 deletions libdeno/libdeno_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<Deno*>(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);
}

0 comments on commit 1b7938e

Please sign in to comment.