1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-21 04:52:26 -05:00

optimize callign to js

This commit is contained in:
Bartek Iwańczuk 2024-12-04 04:30:32 +01:00
parent 3588e75c6c
commit 026a5d952a
No known key found for this signature in database
GPG key ID: 0C6BCDDC3B3AD750
3 changed files with 71 additions and 200 deletions

View file

@ -51,8 +51,25 @@ export class Context {
}
}
export function installPlugins(plugins) {
state.plugins = plugins;
// TODO(bartlomieju): remove
export function runPluginRule() {}
export function installPlugin(plugin) {
console.log("plugin", plugin);
if (typeof plugin !== "object") {
throw new Error("Linter plugin must be an object");
}
if (typeof plugin.name !== "string") {
throw new Error("Linter plugin name must be a string");
}
if (typeof plugin.rules !== "object") {
throw new Error("Linter plugin rules must be an object");
}
if (typeof state.plugins[plugin.name] !== "undefined") {
throw new Error(`Linter plugin ${plugin.name} has already been registered`);
}
state.plugins[plugin.name] = plugin.rules;
console.log("Installed plugin", plugin.name, plugin.rules);
}
export function runPluginsForFile(fileName, serializedAst) {
@ -63,12 +80,12 @@ export function runPluginsForFile(fileName, serializedAst) {
return value;
});
for (const plugin of state.plugins) {
runRulesFromPlugin(plugin, fileName, ast);
for (const pluginName of Object.keys(state.plugins)) {
runRulesFromPlugin(pluginName, state.plugins[pluginName], fileName, ast);
}
}
function runRulesFromPlugin(plugin, fileName, ast) {
function runRulesFromPlugin(pluginName, plugin, fileName, ast) {
for (const ruleName of Object.keys(plugin)) {
const rule = plugin[ruleName];
@ -77,7 +94,7 @@ function runRulesFromPlugin(plugin, fileName, ast) {
}
// TODO(bartlomieju): can context be created less often, maybe once per plugin or even once per `runRulesForFile` invocation?
const id = `${plugin.name}/${ruleName};`;
const id = `${pluginName}/${ruleName}`;
const ctx = new Context(id, fileName);
const visitor = rule.create(ctx);
traverse(ast, visitor);
@ -88,24 +105,6 @@ function runRulesFromPlugin(plugin, fileName, ast) {
}
}
export function runPluginRule(fileName, pluginName, ruleName, serializedAst) {
const id = `${pluginName}/${ruleName}`;
const ctx = new Context(id, fileName);
const rule = op_lint_get_rule(pluginName, ruleName);
const visitor = rule(ctx);
const ast = JSON.parse(serializedAst, (key, value) => {
if (key === "ctxt") {
return undefined;
}
return value;
});
// console.log("ast", ast);
traverse(ast, visitor);
}
function traverse(ast, visitor, parent = null) {
if (!ast || typeof ast !== "object") {
return;

View file

@ -5,7 +5,6 @@ use deno_ast::ParsedSource;
use deno_ast::SourceRange;
use deno_ast::SourceTextInfo;
use deno_ast::SourceTextProvider;
use deno_core::anyhow::Context;
use deno_core::error::custom_error;
use deno_core::error::AnyError;
use deno_core::futures::FutureExt;
@ -68,7 +67,7 @@ pub struct PluginRunnerProxy {
pub struct PluginRunner {
worker: MainWorker,
run_plugin_rule_fn: v8::Global<v8::Function>,
install_plugins_fn: v8::Global<v8::Function>,
install_plugin_fn: v8::Global<v8::Function>,
run_plugins_for_file_fn: v8::Global<v8::Function>,
tx: Sender<PluginRunnerResponse>,
rx: Receiver<PluginRunnerRequest>,
@ -81,7 +80,8 @@ impl PluginRunner {
log::info!("spawning thread");
let join_handle = std::thread::spawn(move || {
log::info!("thread spawned");
log::info!("PluginRunner thread spawned");
let start = std::time::Instant::now();
let fut = async move {
let mut flags = Flags::default();
flags.subcommand = DenoSubcommand::Lint(LintFlags::default());
@ -129,7 +129,8 @@ impl PluginRunner {
}
};
let (install_plugins_fn, run_plugins_for_file_fn, run_plugin_rule_fn) = {
log::info!("After plugin loaded, capturing exports");
let (run_plugin_rule_fn, install_plugin_fn, run_plugins_for_file_fn) = {
let scope = &mut runtime.handle_scope();
let module_exports: v8::Local<v8::Object> =
v8::Local::new(scope, obj).try_into().unwrap();
@ -144,17 +145,17 @@ impl PluginRunner {
run_plugin_rule_fn_val.try_into().unwrap();
// TODO(bartlomieju): use v8::OneByteConst and `v8_static_strings!` macro from `deno_core`.
let install_plugins_fn_name =
v8::String::new(scope, "installPlugins").unwrap();
let install_plugins_fn_val = module_exports
.get(scope, install_plugins_fn_name.into())
let install_plugin_fn_name =
v8::String::new(scope, "installPlugin").unwrap();
let install_plugin_fn_val = module_exports
.get(scope, install_plugin_fn_name.into())
.unwrap();
let install_plugins_fn: v8::Local<v8::Function> =
install_plugins_fn_val.try_into().unwrap();
let install_plugin_fn: v8::Local<v8::Function> =
install_plugin_fn_val.try_into().unwrap();
// TODO(bartlomieju): use v8::OneByteConst and `v8_static_strings!` macro from `deno_core`.
let run_plugins_for_file_fn_name =
v8::String::new(scope, "runPluginRule").unwrap();
v8::String::new(scope, "runPluginsForFile").unwrap();
let run_plugins_for_file_fn_val = module_exports
.get(scope, run_plugins_for_file_fn_name.into())
.unwrap();
@ -163,7 +164,7 @@ impl PluginRunner {
(
v8::Global::new(scope, run_plugin_rule_fn),
v8::Global::new(scope, install_plugins_fn),
v8::Global::new(scope, install_plugin_fn),
v8::Global::new(scope, run_plugins_for_file_fn),
)
};
@ -171,7 +172,7 @@ impl PluginRunner {
let runner = Self {
worker,
run_plugin_rule_fn,
install_plugins_fn,
install_plugin_fn,
run_plugins_for_file_fn,
tx: tx_res,
rx: rx_req,
@ -179,6 +180,10 @@ impl PluginRunner {
// TODO(bartlomieju): send "host ready" message to the proxy
log::info!("running host loop");
runner.run_loop().await?;
log::info!(
"PluginRunner thread finished, took {:?}",
std::time::Instant::now() - start
);
Ok(())
}
.boxed_local();
@ -209,24 +214,9 @@ impl PluginRunner {
specifier,
source_text_info,
) => {
let rules_to_run = self.get_rules_to_run();
log::info!("Loaded plugins:");
for (plugin_name, rules) in rules_to_run.iter() {
log::info!(" - {}", plugin_name);
for rule in rules {
log::info!(" - {}", rule);
}
}
let start = std::time::Instant::now();
let r = match self
.run_rules(
&specifier,
rules_to_run,
serialized_ast,
source_text_info,
)
.run_plugins(&specifier, serialized_ast, source_text_info)
.await
{
Ok(()) => Ok(self.take_diagnostics()),
@ -251,28 +241,9 @@ impl PluginRunner {
std::mem::take(&mut container.diagnostics)
}
fn get_rules_to_run(&mut self) -> IndexMap<String, Vec<String>> {
let op_state = self.worker.js_runtime.op_state();
let state = op_state.borrow();
let container = state.borrow::<LintPluginContainer>();
let mut to_run = IndexMap::with_capacity(container.plugins.len());
for (plugin_name, plugin) in container.plugins.iter() {
let rules = plugin
.rules
.keys()
.map(String::from)
.collect::<Vec<String>>();
to_run.insert(plugin_name.to_string(), rules);
}
to_run
}
async fn run_rules(
async fn run_plugins(
&mut self,
specifier: &PathBuf,
rules_to_run: IndexMap<String, Vec<String>>,
ast_string: String,
source_text_info: SourceTextInfo,
) -> Result<(), AnyError> {
@ -297,43 +268,21 @@ impl PluginRunner {
)
};
// TODO(bartlomieju): this could be optimized by having JS side keep track of all plugins and rules and only
// forwarding file name and ast string.
for (plugin_name, rules) in rules_to_run {
for rule_name in rules {
let (plugin_name_v8, rule_name_v8) = {
let scope = &mut self.worker.js_runtime.handle_scope();
let plugin_name_v8: v8::Local<v8::Value> =
v8::String::new(scope, &plugin_name).unwrap().into();
let rule_name_v8: v8::Local<v8::Value> =
v8::String::new(scope, &rule_name).unwrap().into();
(
v8::Global::new(scope, plugin_name_v8),
v8::Global::new(scope, rule_name_v8),
)
};
let call = self.worker.js_runtime.call_with_args(
&self.run_plugin_rule_fn,
&[
file_name_v8.clone(),
plugin_name_v8,
rule_name_v8,
ast_string_v8.clone(),
],
);
let result = self
.worker
.js_runtime
.with_event_loop_promise(call, PollEventLoopOptions::default())
.await;
match result {
Ok(_r) => {
log::info!("plugin finished")
}
Err(error) => {
log::info!("error running plugin {}", error);
}
}
let call = self.worker.js_runtime.call_with_args(
&self.run_plugins_for_file_fn,
&[file_name_v8, ast_string_v8],
);
let result = self
.worker
.js_runtime
.with_event_loop_promise(call, PollEventLoopOptions::default())
.await;
match result {
Ok(_r) => {
log::info!("plugins finished")
}
Err(error) => {
log::info!("error running plugins {}", error);
}
}
@ -362,65 +311,29 @@ impl PluginRunner {
.run_event_loop(PollEventLoopOptions::default())
.await?;
let state = self.worker.js_runtime.op_state();
for (fut, mod_id) in load_futures {
fut.await?;
let module = self.worker.js_runtime.get_module_namespace(mod_id).unwrap();
let scope = &mut self.worker.js_runtime.handle_scope();
let module_local = v8::Local::new(scope, module);
let default_export_str = v8::String::new(scope, "default").unwrap();
log::info!(
"has default export {:?}",
module_local.has_own_property(scope, default_export_str.into())
);
let default_export =
module_local.get(scope, default_export_str.into()).unwrap();
let name_str = v8::String::new(scope, "name").unwrap();
let name_val: v8::Local<v8::Object> = default_export.try_into().unwrap();
let name_val = name_val.get(scope, name_str.into()).unwrap();
log::info!(
"default export name {:?}",
name_val.to_rust_string_lossy(scope)
);
log::info!("deserializing plugin");
let def: PluginDefinition = serde_v8::from_v8(scope, default_export)
.context("Failed to deserialize plugin")?;
log::info!("deserialized plugin {} {:?}", def.name, def.rules.keys());
let mut state = state.borrow_mut();
let container = state.borrow_mut::<LintPluginContainer>();
let mut plugin_desc = LintPluginDesc {
rules: IndexMap::with_capacity(def.rules.len()),
};
for (name, rule_def) in def.rules.into_iter() {
let local_val = v8::Local::new(scope, rule_def.create.v8_value);
let local_fn: v8::Local<v8::Function> = local_val.try_into().unwrap();
let global_fn = v8::Global::new(scope, local_fn);
plugin_desc.rules.insert(name, global_fn);
}
let _ = container.register(def.name, plugin_desc);
// TODO(bartlomieju): put `install_plugin_fn` behind na `Rc``
let install_plugins_local =
v8::Local::new(scope, self.install_plugin_fn.clone());
let undefined = v8::undefined(scope);
let args = &[default_export];
log::info!("Installing plugin...");
// TODO(bartlomieju): do it in a try/catch scope
install_plugins_local.call(scope, undefined.into(), args);
log::info!("Plugin installed");
}
Ok(())
}
}
#[derive(Deserialize)]
struct RuleDefinition {
create: serde_v8::GlobalValue,
}
#[derive(Deserialize)]
struct PluginDefinition {
name: String,
rules: IndexMap<String, RuleDefinition>,
}
impl PluginRunnerProxy {
pub async fn load_plugins(
&self,
@ -496,34 +409,13 @@ pub fn serialize_ast(parsed_source: ParsedSource) -> Result<String, AnyError> {
Ok(r)
}
struct LintPluginDesc {
rules: IndexMap<String, v8::Global<v8::Function>>,
}
#[derive(Default)]
struct LintPluginContainer {
plugins: IndexMap<String, LintPluginDesc>,
diagnostics: Vec<LintDiagnostic>,
source_text_info: Option<SourceTextInfo>,
}
impl LintPluginContainer {
fn register(
&mut self,
name: String,
desc: LintPluginDesc,
) -> Result<(), AnyError> {
if self.plugins.contains_key(&name) {
return Err(custom_error(
"AlreadyExists",
format!("{} plugin already exists", name),
));
}
self.plugins.insert(name, desc);
Ok(())
}
fn report(
&mut self,
id: String,
@ -560,35 +452,12 @@ impl LintPluginContainer {
deno_core::extension!(
deno_lint_ext,
ops = [op_lint_get_rule, op_lint_report, op_lint_get_source],
ops = [op_lint_report, op_lint_get_source],
state = |state| {
state.put(LintPluginContainer::default());
},
);
#[op2]
#[global]
fn op_lint_get_rule(
state: &mut OpState,
#[string] plugin_name: &str,
#[string] rule_name: &str,
) -> Result<v8::Global<v8::Function>, AnyError> {
let container = state.borrow::<LintPluginContainer>();
let Some(plugin) = container.plugins.get(plugin_name) else {
return Err(custom_error(
"NotFound",
format!("{} plugin is not registered", plugin_name),
));
};
let Some(rule) = plugin.rules.get(rule_name) else {
return Err(custom_error(
"NotFound",
format!("Plugin {}, does not have {} rule", plugin_name, rule_name),
));
};
Ok(rule.clone())
}
#[op2(fast)]
fn op_lint_report(
state: &mut OpState,

View file

@ -92,6 +92,9 @@ import { bootstrap as bootstrapOtel } from "ext:deno_telemetry/telemetry.ts";
if (Symbol.metadata) {
throw "V8 supports Symbol.metadata now, no need to shim it";
}
/// REMOVE
ObjectDefineProperties(Symbol, {
dispose: {
__proto__: null,