-
-
Notifications
You must be signed in to change notification settings - Fork 33.7k
vm: add bindings for v8::CompileFunctionInContext #21571
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
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>(); | ||
|
||
|
|
||
| // Argument 4: column offset | ||
| CHECK(args[3]->IsNumber()); | ||
| Local<Integer> column_offset = args[3].As<Integer>(); | ||
|
||
|
|
||
| // Argument 5: cached data (optional) | ||
| Local<Uint8Array> cached_data_buf; | ||
| if (!args[4]->IsUndefined()) { | ||
| CHECK(args[4]->IsUint8Array()); | ||
|
||
| 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()); | ||
|
||
| } | ||
|
|
||
| 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( | ||
|
||
| 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) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
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
forEachuse?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
forEachwithin 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,
forEachhas 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.