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

Support rewrite on methods without callee #84

Merged
merged 11 commits into from
Jul 18, 2024
Merged
20 changes: 10 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface CsiMethod {
src: string
dst?: string
operator?: boolean
allowedWithoutCallee?: boolean
}
export interface RewriterConfig {
chainSourceMap?: boolean
Expand Down
3 changes: 2 additions & 1 deletion scripts/crawler.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ const CSI_METHODS = [
{ src: 'toLocaleLowerCase' },
{ src: 'toLocaleUpperCase' },
{ src: 'toLowerCase' },
{ src: 'toUpperCase' }
{ src: 'toUpperCase' },
{ src: 'eval', allowedWithoutCallee: true }
]

const GLOBAL_METHODS_TEMPLATE = `;(function(globals){
Expand Down
2 changes: 2 additions & 0 deletions src/lib_napi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub struct CsiMethod {
pub src: String,
pub dst: Option<String>,
pub operator: Option<bool>,
pub allowed_without_callee: Option<bool>,
}

#[napi(object)]
Expand All @@ -45,6 +46,7 @@ impl RewriterConfig {
m.src.clone(),
m.dst.clone(),
m.operator.unwrap_or(false),
m.allowed_without_callee.unwrap_or(false),
)
})
.collect::<Vec<visitor::csi_methods::CsiMethod>>(),
Expand Down
2 changes: 2 additions & 0 deletions src/lib_wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub struct CsiMethod {
pub src: String,
pub dst: Option<String>,
pub operator: Option<bool>,
pub allowed_without_callee: Option<bool>,
}

#[derive(Deserialize)]
Expand Down Expand Up @@ -80,6 +81,7 @@ impl RewriterConfig {
m.src.clone(),
m.dst.clone(),
m.operator.unwrap_or(false),
m.allowed_without_callee.unwrap_or(false),
)
})
.collect::<Vec<visitor::csi_methods::CsiMethod>>(),
Expand Down
4 changes: 2 additions & 2 deletions src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,13 @@ fn csi_from_str(src: &str, dst: Option<&str>) -> CsiMethod {
Some(str) => Some(String::from(str)),
None => None,
};
CsiMethod::new(String::from(src), dst_string, false)
CsiMethod::new(String::from(src), dst_string, false, false)
}

fn csi_op_from_str(src: &str, dst: Option<&str>) -> CsiMethod {
let dst_string = match dst {
Some(str) => Some(String::from(str)),
None => None,
};
CsiMethod::new(String::from(src), dst_string, true)
CsiMethod::new(String::from(src), dst_string, true, false)
}
16 changes: 4 additions & 12 deletions src/tracer_logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,18 @@ extern "C" {

pub struct TracerLogger<'a> {
pub logger: LoggerFn<'a>,
pub level_filter: LevelFilter,
}

impl<'a> TracerLogger<'a> {
pub fn new(level: &str, logger: LoggerFn<'a>) -> Self {
TracerLogger {
logger,
level_filter: LevelFilter::from_str(level).unwrap_or(LevelFilter::Off),
}
pub fn new(logger: LoggerFn<'a>) -> Self {
TracerLogger { logger }
}

pub fn with_level(level: &str) -> Self {
Self::new(level, &|level, msg| {
pub fn default() -> Self {
Self::new(&|level, msg| {
log(&level.into(), &msg.into()).ok();
})
}

pub fn default() -> Self {
Self::with_level("ERROR")
}
}

impl Log for TracerLogger<'_> {
Expand Down
59 changes: 59 additions & 0 deletions src/transform/call_expr_transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ impl CallExprTransform {
}
_ => None,
},

Expr::Ident(obj) => replace_call_expr_if_csi_method_without_callee(
&obj,
call,
csi_methods,
ident_provider,
),
_ => None,
},
_ => None,
Expand Down Expand Up @@ -171,6 +178,58 @@ fn replace_call_expr_if_csi_method(
)
}

fn replace_call_expr_if_csi_method_without_callee(
ident: &Ident,
call: &mut CallExpr,
csi_methods: &CsiMethods,
ident_provider: &mut dyn IdentProvider,
) -> Option<ResultExpr> {
let method_name = &ident.sym.to_string();

if let Some(csi_method) = csi_methods.get(method_name) {
if csi_method.allowed_without_callee {
let mut assignations = Vec::new();
let mut arguments = Vec::new();
let span = call.span;

// let __datadog_test_0;
// (__datadog_test_0 = arg0, _ddiast.aloneMethod
// (aloneMethod(__datadog_test_0), aloneMethod, undefined, __datadog_test_0));
arguments.push(Expr::Ident(ident.clone()));
let global = Ident {
span,
sym: JsWord::from("undefined"),
optional: false,
};
arguments.push(Expr::Ident(global));

let mut call_replacement = call.clone();
call_replacement.args.iter_mut().for_each(|expr_or_spread| {
DefaultOperandHandler::replace_expressions_in_operand(
&mut expr_or_spread.expr,
IdentMode::Replace,
&mut assignations,
&mut arguments,
&span,
ident_provider,
)
});

return Some(ResultExpr {
tag: method_name.clone(),
expr: get_dd_paren_expr(
&Expr::Call(call_replacement),
&arguments,
&mut assignations,
csi_method.dst.as_str(),
&span,
),
});
}
}
None
}

fn replace_call_expr_if_csi_method_with_member(
expr: &Expr,
ident: &Ident,
Expand Down
15 changes: 13 additions & 2 deletions src/visitor/csi_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,23 @@ pub struct CsiMethod {
pub src: String,
pub dst: String,
pub operator: bool,
pub allowed_without_callee: bool,
}

impl CsiMethod {
pub fn new(src: String, dst: Option<String>, operator: bool) -> Self {
pub fn new(
src: String,
dst: Option<String>,
operator: bool,
allowed_without_callee: bool,
) -> Self {
let dst = dst.unwrap_or_else(|| src.clone());
CsiMethod { src, dst, operator }
CsiMethod {
src,
dst,
operator,
allowed_without_callee,
}
}
}

Expand Down
55 changes: 55 additions & 0 deletions test/method_without_callee.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict'

const { rewriteAndExpect } = require('./util')

describe('Method without callee', () => {
it('should rewrite aloneMethod', () => {
const js = 'aloneMethod(arg0)'
rewriteAndExpect(
js,
`{
let __datadog_test_0;
(__datadog_test_0 = arg0, _ddiast.aloneMethod\
(aloneMethod(__datadog_test_0), aloneMethod, undefined, __datadog_test_0));
}`
)
})

it('should rewrite aloneMethod with 2 args', () => {
const js = 'aloneMethod(arg0, obj.arg1)'
rewriteAndExpect(
js,
`{
let __datadog_test_0, __datadog_test_1;
(__datadog_test_0 = arg0, __datadog_test_1 = obj.arg1, _ddiast.aloneMethod\
(aloneMethod(__datadog_test_0, __datadog_test_1), aloneMethod, undefined, __datadog_test_0, __datadog_test_1));
}`
)
})
it('should rewrite aloneMethod without args', () => {
const js = 'aloneMethod()'
rewriteAndExpect(
js,
`{
_ddiast.aloneMethod(aloneMethod(), aloneMethod, undefined);
}`
)
})

it('should rewrite aloneMethod when it is called with callee', () => {
const js = 'obj.aloneMethod(arg0)'
rewriteAndExpect(
js,
`{
let __datadog_test_0, __datadog_test_1, __datadog_test_2;
(__datadog_test_0 = obj, __datadog_test_1 = __datadog_test_0.aloneMethod, __datadog_test_2 = arg0, _ddiast.aloneMethod\
(__datadog_test_1.call(__datadog_test_0, __datadog_test_2), __datadog_test_1, __datadog_test_0, __datadog_test_2));
}`
)
})

it('should not rewrite method not configured as alone when it is used alone', () => {
const js = 'cantAloneMethod(arg0)'
rewriteAndExpect(js, '{cantAloneMethod(arg0)}')
})
})
4 changes: 3 additions & 1 deletion test/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ const csiMethods = [
{ src: 'replace' },
{ src: 'replaceAll' },
{ src: 'slice' },
{ src: 'concat' }
{ src: 'concat' },
{ src: 'aloneMethod', allowedWithoutCallee: true },
{ src: 'cantAloneMethod' }
]

const rewriteWithOpts = (code, opts) => {
Expand Down
Loading