Skip to content

Commit

Permalink
Implement codegen for require.cache (#136)
Browse files Browse the repository at this point in the history
Closes #135

Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
  • Loading branch information
alexkirsz and sokra committed Jul 25, 2022
1 parent b6aae7a commit 8060ba8
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 10 deletions.
46 changes: 46 additions & 0 deletions crates/turbopack-ecmascript/src/analyzer/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ pub enum Effect {
ast_path: Vec<AstParentKind>,
span: Span,
},
Member {
obj: JsValue,
prop: JsValue,
ast_path: Vec<AstParentKind>,
span: Span,
},
ImportedBinding {
request: String,
export: Option<String>,
Expand Down Expand Up @@ -62,6 +68,15 @@ impl Effect {
arg.normalize();
}
}
Effect::Member {
obj,
prop,
ast_path: _,
span: _,
} => {
obj.normalize();
prop.normalize();
}
Effect::ImportedBinding {
request: _,
export: _,
Expand Down Expand Up @@ -682,6 +697,28 @@ impl Analyzer<'_> {
}
}

fn check_member_expr_for_effects<'ast: 'r, 'r>(
&mut self,
member_expr: &'ast MemberExpr,
ast_path: &AstNodePath<AstParentNodeRef<'r>>,
) {
let obj_value = self.eval_context.eval(&member_expr.obj);
let prop_value = match &member_expr.prop {
// TODO avoid clone
MemberProp::Ident(i) => i.sym.clone().into(),
MemberProp::PrivateName(_) => {
return;
}
MemberProp::Computed(ComputedPropName { expr, .. }) => self.eval_context.eval(expr),
};
self.data.effects.push(Effect::Member {
obj: obj_value,
prop: prop_value,
ast_path: as_parent_path(ast_path),
span: member_expr.span(),
});
}

fn take_return_values(&mut self) -> Box<JsValue> {
let values = self.cur_fn_return_values.take().unwrap();

Expand Down Expand Up @@ -754,6 +791,15 @@ impl VisitAstPath for Analyzer<'_> {
}
}

fn visit_member_expr<'ast: 'r, 'r>(
&mut self,
member_expr: &'ast MemberExpr,
ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
) {
self.check_member_expr_for_effects(member_expr, ast_path);
member_expr.visit_children_with_path(self, ast_path);
}

fn visit_expr<'ast: 'r, 'r>(
&mut self,
n: &'ast Expr,
Expand Down
23 changes: 14 additions & 9 deletions crates/turbopack-ecmascript/src/analyzer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -812,17 +812,21 @@ impl JsValue {
"The Node.js process module: https://nodejs.org/api/process.html",
),
WellKnownObjectKind::NodePreGyp => (
"@mapbox/node-pre-gyp",
"The Node.js @mapbox/node-pre-gyp module: https://github.com/mapbox/node-pre-gyp",
"@mapbox/node-pre-gyp",
"The Node.js @mapbox/node-pre-gyp module: https://github.com/mapbox/node-pre-gyp",
),
WellKnownObjectKind::NodeExpressApp => (
"express",
"express",
"The Node.js express package: https://github.com/expressjs/express"
),
WellKnownObjectKind::NodeProtobufLoader => (
"@grpc/proto-loader",
"@grpc/proto-loader",
"The Node.js @grpc/proto-loader package: https://github.com/grpc/grpc-node"
),
WellKnownObjectKind::RequireCache => (
"require.cache",
"The CommonJS require.cache object: https://nodejs.org/api/modules.html#requirecache"
),
};
if depth > 0 {
let i = hints.len();
Expand Down Expand Up @@ -1406,7 +1410,6 @@ impl JsValue {
FreeVarKind::Object
| FreeVarKind::Require
| FreeVarKind::Import
| FreeVarKind::RequireResolve
| FreeVarKind::NodeProcess,
) => false,
JsValue::FreeVar(FreeVarKind::Other(_)) => false,
Expand All @@ -1417,7 +1420,11 @@ impl JsValue {

JsValue::Variable(_) | JsValue::Unknown(..) | JsValue::Argument(..) => false,

JsValue::Call(_, box JsValue::FreeVar(FreeVarKind::RequireResolve), _) => true,
JsValue::Call(
_,
box JsValue::WellKnownFunction(WellKnownFunctionKind::RequireResolve),
_,
) => true,
JsValue::Call(..) | JsValue::MemberCall(..) | JsValue::Member(..) => false,
JsValue::WellKnownObject(_) | JsValue::WellKnownFunction(_) => false,
}
Expand Down Expand Up @@ -1801,9 +1808,6 @@ pub enum FreeVarKind {
/// A reference to `import`
Import,

/// A reference to global `require.resolve`
RequireResolve,

/// Node.js process
NodeProcess,

Expand All @@ -1823,6 +1827,7 @@ pub enum WellKnownObjectKind {
NodePreGyp,
NodeExpressApp,
NodeProtobufLoader,
RequireCache,
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
Expand Down
3 changes: 3 additions & 0 deletions crates/turbopack-ecmascript/src/analyzer/well_known.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,9 @@ pub fn well_known_function_member(kind: WellKnownFunctionKind, prop: JsValue) ->
(WellKnownFunctionKind::Require, Some("resolve")) => {
JsValue::WellKnownFunction(WellKnownFunctionKind::RequireResolve)
}
(WellKnownFunctionKind::Require, Some("cache")) => {
JsValue::WellKnownObject(WellKnownObjectKind::RequireCache)
}
(WellKnownFunctionKind::NodeStrongGlobalize, Some("SetRootDir")) => {
JsValue::WellKnownFunction(WellKnownFunctionKind::NodeStrongGlobalizeSetRootDir)
}
Expand Down
1 change: 1 addition & 0 deletions crates/turbopack-ecmascript/src/chunk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ async fn module_factory(content: EcmascriptChunkItemContentVc) -> Result<StringV
"i: __turbopack_import__",
"s: __turbopack_esm__",
"v: __turbopack_export_value__",
"c: __turbopack_cache__",
"p: process",
];
if content.options.module {
Expand Down
29 changes: 29 additions & 0 deletions crates/turbopack-ecmascript/src/references/cjs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,3 +289,32 @@ impl CodeGenerateable for CjsRequireResolveAssetReference {
Ok(CodeGeneration { visitors }.into())
}
}

#[turbo_tasks::value(shared, CodeGenerateable)]
#[derive(Hash, Debug)]
pub struct CjsRequireCacheAccess {
pub path: AstPathVc,
}

#[turbo_tasks::value_impl]
impl CodeGenerateable for CjsRequireCacheAccess {
#[turbo_tasks::function]
async fn code_generation(
&self,
_chunk_context: EcmascriptChunkContextVc,
_context: ChunkingContextVc,
) -> Result<CodeGenerationVc> {
let mut visitors = Vec::new();

let path = &self.path.await?;
visitors.push(create_visitor!(path, visit_mut_expr(expr: &mut Expr) {
if let Expr::Member(_) = expr {
*expr = Expr::Ident(Ident::new("__turbopack_cache__".into(), DUMMY_SP));
} else {
unreachable!("`CjsRequireCacheAccess` is only created from `MemberExpr`");
}
}));

Ok(CodeGeneration { visitors }.into())
}
}
39 changes: 38 additions & 1 deletion crates/turbopack-ecmascript/src/references/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ use crate::{
code_gen::{CodeGenerateableVc, CodeGenerateablesVc},
magic_identifier,
references::{
cjs::{CjsRequireAssetReferenceVc, CjsRequireResolveAssetReferenceVc},
cjs::{
CjsRequireAssetReferenceVc, CjsRequireCacheAccess, CjsRequireResolveAssetReferenceVc,
},
esm::{EsmBinding, EsmExportsVc},
},
};
Expand Down Expand Up @@ -999,6 +1001,30 @@ pub(crate) async fn analyze_ecmascript_module(
Ok(())
}

async fn handle_member(
ast_path: &Vec<AstParentKind>,
obj: JsValue,
prop: JsValue,
code_gen: &mut Vec<CodeGenerateableVc>,
) -> Result<()> {
match (obj, prop) {
(
JsValue::WellKnownFunction(WellKnownFunctionKind::Require),
JsValue::Constant(ConstantValue::Str(s)),
) if &*s == "cache" => {
code_gen.push(
CjsRequireCacheAccess {
path: AstPathVc::cell(ast_path.clone()),
}
.into(),
);
}
_ => {}
}

Ok(())
}

let cache = Mutex::new(LinkCache::new());
let linker =
|value| value_visitor(source, context, value, target, node_native_bindings);
Expand Down Expand Up @@ -1069,6 +1095,17 @@ pub(crate) async fn analyze_ecmascript_module(
)
.await?;
}
Effect::Member {
obj,
prop,
ast_path,
span: _,
} => {
let obj = link_value(obj).await?;
let prop = link_value(prop).await?;

handle_member(&ast_path, obj, prop, &mut code_gen).await?;
}
Effect::ImportedBinding {
request,
export,
Expand Down

0 comments on commit 8060ba8

Please sign in to comment.