-
Notifications
You must be signed in to change notification settings - Fork 30.1k
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
vm: add bindings for v8::CompileFunctionInContext #21571
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -209,6 +209,7 @@ void ContextifyContext::Init(Environment* env, Local<Object> target) { | |
|
||
env->SetMethod(target, "makeContext", MakeContext); | ||
env->SetMethod(target, "isContext", IsContext); | ||
env->SetMethod(target, "compileFunction", CompileFunction); | ||
} | ||
|
||
|
||
|
@@ -985,6 +986,144 @@ class ContextifyScript : public BaseObject { | |
}; | ||
|
||
|
||
void ContextifyContext::CompileFunction( | ||
const FunctionCallbackInfo<Value>& args) { | ||
Environment* env = Environment::GetCurrent(args); | ||
Isolate* isolate = env->isolate(); | ||
Local<Context> context = env->context(); | ||
|
||
// Argument 1: source code | ||
CHECK(args[0]->IsString()); | ||
Local<String> code = args[0].As<String>(); | ||
|
||
// Argument 2: filename | ||
CHECK(args[1]->IsString()); | ||
Local<String> filename = args[1].As<String>(); | ||
|
||
// Argument 3: line offset | ||
CHECK(args[2]->IsNumber()); | ||
Local<Integer> line_offset = args[2].As<Integer>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. have it been tested these offsets actually do anything for compiling functions? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No idea, my guess is that we set them to 0 for like... 99% cases, but I put them in just for parity with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. well in ContextifyScript the entire source can be offset for dealing with source positions in html bodies and such. if they don't do anything here it would be best to remove them. (easy test is offset by a bunch and make the body have a syntax error, then read the column offset in the stack) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. did you ever verify this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this could be useful. One way to test this is to throw an exception ans check whether the exception position is affected by the offsets. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It indeed works. Will add tests. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Number type is indeed checked in JavaScript, but not that the number is an integer. That should be added. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agreed, will throw in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. BTW you should use |
||
|
||
// Argument 4: column offset | ||
CHECK(args[3]->IsNumber()); | ||
Local<Integer> column_offset = args[3].As<Integer>(); | ||
This comment was marked as resolved.
Sorry, something went wrong. |
||
|
||
// Argument 5: cached data (optional) | ||
Local<Uint8Array> cached_data_buf; | ||
if (!args[4]->IsUndefined()) { | ||
CHECK(args[4]->IsUint8Array()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should accept all ArrayBuffer view types, and ArrayBuffer itself, consistent with other newer APIs. Hint: use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I’d stick with consistency with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds great to me. @TimothyGu wdyt? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure. |
||
cached_data_buf = args[4].As<Uint8Array>(); | ||
} | ||
|
||
// Argument 6: produce cache data | ||
CHECK(args[5]->IsBoolean()); | ||
bool produce_cached_data = args[5]->IsTrue(); | ||
|
||
// Argument 7: parsing context (optional) | ||
Local<Context> parsing_context; | ||
if (!args[6]->IsUndefined()) { | ||
CHECK(args[6]->IsObject()); | ||
ContextifyContext* sandbox = | ||
ContextifyContext::ContextFromContextifiedSandbox( | ||
env, args[6].As<Object>()); | ||
CHECK_NOT_NULL(sandbox); | ||
parsing_context = sandbox->context(); | ||
} else { | ||
parsing_context = context; | ||
} | ||
|
||
// Argument 8: context extensions (optional) | ||
Local<Array> context_extensions_buf; | ||
if (!args[7]->IsUndefined()) { | ||
CHECK(args[7]->IsArray()); | ||
context_extensions_buf = args[7].As<Array>(); | ||
} | ||
|
||
// Argument 9: params for the function (optional) | ||
Local<Array> params_buf; | ||
if (!args[8]->IsUndefined()) { | ||
CHECK(args[8]->IsArray()); | ||
params_buf = args[8].As<Array>(); | ||
} | ||
|
||
// Read cache from cached data buffer | ||
ScriptCompiler::CachedData* cached_data = nullptr; | ||
if (!cached_data_buf.IsEmpty()) { | ||
ArrayBuffer::Contents contents = cached_data_buf->Buffer()->GetContents(); | ||
uint8_t* data = static_cast<uint8_t*>(contents.Data()); | ||
cached_data = new ScriptCompiler::CachedData( | ||
data + cached_data_buf->ByteOffset(), cached_data_buf->ByteLength()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This hasn't been fixed yet. Note, you don't need to support all types of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I realized that, and had been planning to change these in both Or would you still prefer that I make the switch in this one and just change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nah, do as you planned :) |
||
} | ||
|
||
ScriptOrigin origin(filename, line_offset, column_offset); | ||
ScriptCompiler::Source source(code, origin, cached_data); | ||
ScriptCompiler::CompileOptions options; | ||
if (source.GetCachedData() == nullptr) { | ||
options = ScriptCompiler::kNoCompileOptions; | ||
} else { | ||
options = ScriptCompiler::kConsumeCodeCache; | ||
} | ||
|
||
TryCatch try_catch(isolate); | ||
Context::Scope scope(parsing_context); | ||
|
||
// Read context extensions from buffer | ||
std::vector<Local<Object>> context_extensions; | ||
if (!context_extensions_buf.IsEmpty()) { | ||
for (uint32_t n = 0; n < context_extensions_buf->Length(); n++) { | ||
Local<Value> val; | ||
if (!context_extensions_buf->Get(context, n).ToLocal(&val)) return; | ||
CHECK(val->IsObject()); | ||
context_extensions.push_back(val.As<Object>()); | ||
} | ||
} | ||
|
||
// Read params from params buffer | ||
std::vector<Local<String>> params; | ||
if (!params_buf.IsEmpty()) { | ||
for (uint32_t n = 0; n < params_buf->Length(); n++) { | ||
Local<Value> val; | ||
if (!params_buf->Get(context, n).ToLocal(&val)) return; | ||
CHECK(val->IsString()); | ||
params.push_back(val.As<String>()); | ||
} | ||
} | ||
|
||
MaybeLocal<Function> maybe_fun = ScriptCompiler::CompileFunctionInContext( | ||
context, &source, params.size(), params.data(), | ||
context_extensions.size(), context_extensions.data(), options); | ||
|
||
Local<Function> fun; | ||
if (maybe_fun.IsEmpty() || !maybe_fun.ToLocal(&fun)) { | ||
ContextifyScript::DecorateErrorStack(env, try_catch); | ||
try_catch.ReThrow(); | ||
return; | ||
} | ||
|
||
if (produce_cached_data) { | ||
const std::unique_ptr<ScriptCompiler::CachedData> | ||
cached_data(ScriptCompiler::CreateCodeCacheForFunction(fun, code)); | ||
bool cached_data_produced = cached_data != nullptr; | ||
if (cached_data_produced) { | ||
MaybeLocal<Object> buf = Buffer::Copy( | ||
env, | ||
reinterpret_cast<const char*>(cached_data->data), | ||
cached_data->length); | ||
if (fun->Set( | ||
parsing_context, | ||
env->cached_data_string(), | ||
buf.ToLocalChecked()).IsNothing()) return; | ||
} | ||
if (fun->Set( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what if the return value was something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That could be rad, but I'm supposing that most people would just be expecting the function or would disable the cache and then we'll be returning There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Like mentioned previously, let's not have this at all, and expose |
||
parsing_context, | ||
env->cached_data_produced_string(), | ||
Boolean::New(isolate, cached_data_produced)).IsNothing()) return; | ||
} | ||
|
||
args.GetReturnValue().Set(fun); | ||
} | ||
|
||
|
||
void Initialize(Local<Object> target, | ||
Local<Value> unused, | ||
Local<Context> context) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👆 I know many internals try to avoid prototypal methods directly (opting to create generic forms). Should this be done for
forEach
use?Update:
Same with
isArray
(e.g.const { isArray } = Array
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've generally tried to avoid using
forEach
within core functions because it's an order of magnitude slower than a regular for-loop. Even if this is not expected to be a hot function, I think I'd rather see this as a regular for-loop.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we're taking the performance approach,
forEach
has actually become faster in the common case than a for loop (in V8 anyway).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👆 \cc @bmeurer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whatever the outcome of this discussion, please let’s not hold up the PR over this – this isn’t hot code (or at least the cost of actual parsing + compilation is likely going to overshadow any choice of this kind), so I think it’s fine to go with whatever the PR author considers most readable.