Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --prefetch flag for deps prefetch without running #1475

Merged
merged 9 commits into from
Jan 15, 2019

Conversation

kevinkassimo
Copy link
Contributor

@kevinkassimo kevinkassimo commented Jan 7, 2019

Aiming to close #1386. This is built on top of #1460

The only difference from running is that the modules are compiled but NOT evaluated

if (is_prefetch) {
return true;
}

auto result = module->Evaluate(context);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only modified part of this PR: module->Evaluate is not run and short circuited by is_prefetch

@ry
Copy link
Member

ry commented Jan 7, 2019

Great - very glad you took this on! I will review after #1460 has landed

@kevinkassimo
Copy link
Contributor Author

kevinkassimo commented Jan 7, 2019

@ry Found a mysterious issue that I am not quite sure about during debugging:
Notice that the current diff test has surprising output (with --prefetch and --reload flags):

Compiling [WILDCARD]tests/prefetch.ts
Downloading http:https://localhost:4545/tests/subdir/subdir3/mod1.ts... << These 3 lines...
Downloading http:https://localhost:4545/tests/subdir/subdir3/mod2.ts... << These 3 lines...
Downloading http:https://localhost:4545/tests/subdir/subdir3/mod3.ts... << These 3 lines...
Downloading http:https://localhost:4545/tests/subdir/subdir3/mod1.ts...
Compiling http:https://localhost:4545/tests/subdir/subdir3/mod1.ts
Downloading http:https://localhost:4545/tests/subdir/subdir3/mod2.ts...
Compiling http:https://localhost:4545/tests/subdir/subdir3/mod2.ts
Downloading http:https://localhost:4545/tests/subdir/subdir3/mod3.ts...
Compiling http:https://localhost:4545/tests/subdir/subdir3/mod3.ts

The 3 lines marked are completely unexpected (since they download the files but not compiling them, and is duplicate with later downloads).
When I trace down where these downloads happens, it is exactly from here:

const output = service.getEmitOutput(fileName);

which surprised me since this is a TypeScript compiler builtin. Notice that resolveModule is the only part where we have the codeFetch logic injected and is used in resolveModuleNames

When I renamed resolveModuleNames to _resolveModuleNames, the unexpected downloads goes away...

I would assume that we unexpectedly overwrote a TypeScript API? It seems that getEmitOutput is internally, in somewhere, invoking resolveModuleNames...

cc @kitsonk since I don't really know things about the Language Service API...

(It feels like both TypeScript compiler and v8 ES modules are trying to gather all the dependencies, or at least recursively evaluating and resolving files for the import statements... ideally only one of them should happen

@kitsonk
Copy link
Contributor

kitsonk commented Jan 7, 2019

resolveModuleNames is the core language service API that TypeScript uses to understand the full type dependencies. TypeScript is going to build a dependency graph before it emits the first file, because it needs to have a holistic understanding. Once that is done, then I assume the ES Module logic is also building a dependency graph, as each module is actually getting emitted. When the Compiler and the Runner were both in user land, they would cache that, so that the Runner wouldn't ask again to fetch modules that were needed. It seems like #1460 needs to do the same thing and cache modules being fetched to be able to offer them up to TypeScript multiple times.

@kevinkassimo I would run the whole thing with the debug flag to validate that this is actually what is happening.

@ry
Copy link
Member

ry commented Jan 9, 2019

@kevinkassimo could you please rebase?

@kevinkassimo
Copy link
Contributor Author

@ry Rebased.

@kitsonk I'm pretty sure it is the problem.

Also days ago TimothyGu (former Node TSC who also worked on WHATWG standards) mentioned that browsers resolve modules when doing parsing and before actual module instantiation (before auto maybe_ok = module->InstantiateModule(context, ResolveCallback);), which I think is similar to what TypeScript compiler is actually doing here. Ideally only 1 dependency graph should be built...

@@ -530,7 +530,7 @@ void DenoIsolate::ResolveOk(const char* filename, const char* source) {
}

bool ExecuteMod(v8::Local<v8::Context> context, const char* js_filename,
const char* js_source) {
const char* js_source, int is_prefetch) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the name of this argument should be changed at this level level of abstraction, because in libdeno we don't know about "prefetching" ...

What do you think about "resolve_only" or "without_execute"... something like that?

Also you may use a bool here as this is C++.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adopting resolve_only (execute_mod that without_execute sounds weird...)

libdeno/deno.h Outdated
@@ -76,7 +76,7 @@ int deno_execute(Deno* d, void* user_data, const char* js_filename,
// Return value: 0 = fail, 1 = success
// Get error text with deno_last_exception().
int deno_execute_mod(Deno* d, void* user_data, const char* js_filename,
const char* js_source);
const char* js_source, int is_prefetch);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above regarding the name of this argument

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am making them all bools now instead (C++, extern C and Rust)

@@ -294,7 +294,7 @@ TEST(LibDenoTest, ModuleResolutionFail) {
// Do not call deno_resolve_ok();
};
Deno* d = deno_new(deno_config{0, empty, empty, nullptr, resolve_cb});
EXPECT_FALSE(deno_execute_mod(d, d, "a.js", mod_a));
EXPECT_FALSE(deno_execute_mod(d, d, "a.js", mod_a, 0));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to have a libdeno test exercising the is_prefetch option (or whatever you end up naming it)

Suggest something like this (please change naming)

TEST(LibDenoTest, ModuleResolutionPrefetch) {
  static int count = 0;
  auto resolve_cb = [](void* user_data, const char* specifier,
                       const char* referrer) {
    EXPECT_STREQ(specifier, "b.js");
    EXPECT_STREQ(referrer, "a.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_mod(d, d, "a.js",
    "import { retb } from 'b.js'\n"
    "throw Error('unreachable');" , 1));
  EXPECT_EQ(count, 1);
  deno_delete(d);
}

Copy link
Member

@ry ry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I'm happy to see how simply done this was.

The one problem that I have is that it doesn't have a test for the actual behavior - it seems it would need some custom bits of python.

@kitsonk
Copy link
Contributor

kitsonk commented Jan 10, 2019

@kevinkassimo I think it is a problem too and yes, browser do a two pass instantiation, it was something that we dealt with in the runner... In chatting with Ryan I am going to take a look at the whole thing and see if we can get things to behave a bit cleaner.

@kevinkassimo
Copy link
Contributor Author

@ry Hmm the diff test currently at least proves that the files are downloaded (due to logs). Probably we should double check by walking into the deps/ folder to see if they are actually cached at the correct location?

@kevinkassimo
Copy link
Contributor Author

kevinkassimo commented Jan 10, 2019

@kitsonk Cool... Also, since we are having 2 independent dependency graphs without any communications at this moment, we might need to add on Rust/C++ side another function/map that resolves referrer and specifier to filenames (and possibly some metadata) and store the mappings, to make recompile/reload function correctly again...

libdeno/deno.h Outdated
@@ -76,7 +76,7 @@ int deno_execute(Deno* d, void* user_data, const char* js_filename,
// Return value: 0 = fail, 1 = success
// Get error text with deno_last_exception().
int deno_execute_mod(Deno* d, void* user_data, const char* js_filename,
const char* js_source);
const char* js_source, bool resolve_only);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use bool here. deno.h is supposed to be ANSI C to support linking to Rust. Even if this isn't a problem currently, it is in Go, and we might be conservative.

Add a line of documentation regarding this parameter, please.

@ry
Copy link
Member

ry commented Jan 10, 2019

@kevinkassimo I've added tools/prefetch_test.py please take a look

@kevinkassimo
Copy link
Contributor Author

@ry The test looks good (although I'm investigating why it fails on Appveyor...)

(I actually just noticed that I used to have an integration test for --prefetch, but for some reason it was lost during rebasing... I'm adding them back)

Downloading http:https://localhost:4545/tests/subdir/subdir3/mod1.ts...
Downloading http:https://localhost:4545/tests/subdir/subdir3/mod2.ts...
Downloading http:https://localhost:4545/tests/subdir/subdir3/mod3.ts...
Downloading http:https://localhost:4545/tests/subdir/subdir3/mod1.ts...
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm. I guess this is #1496

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They should have the same underlying problem...

@ry ry force-pushed the esmodule/prefetch branch 2 times, most recently from 3a4eeb1 to 1516135 Compare January 14, 2019 16:23
@ry
Copy link
Member

ry commented Jan 15, 2019

FYI we are looking into the windows failures here.

@kevinkassimo
Copy link
Contributor Author

@ry Great... (annoying since I have no way of reproducing it on a non-Windows machine...)

@piscisaureus
Copy link
Member

@ry, @kevinkassimo I added a commit that fixes windows.

Copy link
Member

@ry ry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - thanks @kevinkassimo and @piscisaureus

@ry ry merged commit c870cf4 into denoland:master Jan 15, 2019
@kevinkassimo kevinkassimo deleted the esmodule/prefetch branch December 27, 2019 07:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

deno script.ts --prefetch
4 participants