Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 5 additions & 21 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,17 @@ use std::fmt::{self, Display, Formatter};

#[derive(Debug)]
pub enum OrchestrionError {
IoError(std::io::Error),
StrError(String),
InjectionMatchFailure,
}

impl From<std::io::Error> for OrchestrionError {
fn from(e: std::io::Error) -> Self {
OrchestrionError::IoError(e)
}
}

impl From<String> for OrchestrionError {
fn from(s: String) -> Self {
OrchestrionError::StrError(s)
}
}

impl From<&str> for OrchestrionError {
fn from(s: &str) -> Self {
OrchestrionError::StrError(s.to_string())
}
}
impl std::error::Error for OrchestrionError {}

impl Display for OrchestrionError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
OrchestrionError::IoError(e) => write!(f, "IO error: {e}"),
OrchestrionError::StrError(s) => write!(f, "String error: {s}"),
OrchestrionError::InjectionMatchFailure => {
write!(f, "Injection match failed")
}
}
}
}
15 changes: 15 additions & 0 deletions src/instrumentation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub struct Instrumentation {
config: InstrumentationConfig,
count: usize,
is_correct_class: bool,
has_injected: bool,
}

impl Instrumentation {
Expand All @@ -41,14 +42,24 @@ impl Instrumentation {
config,
count: 0,
is_correct_class: false,
has_injected: false,
}
}

#[must_use]
pub fn has_injected(&self) -> bool {
self.has_injected
}

pub(crate) fn reset(&mut self) {
self.count = 0;
self.is_correct_class = false;
}

pub(crate) fn reset_has_injected(&mut self) {
self.has_injected = false;
}

fn new_fn(&self, body: BlockStmt) -> ArrowExpr {
ArrowExpr {
params: vec![],
Expand Down Expand Up @@ -114,6 +125,8 @@ impl Instrumentation {
trace = trace_ident
),
];

self.has_injected = true;
}

fn insert_constructor_tracing(&mut self, body: &mut BlockStmt) {
Expand Down Expand Up @@ -160,6 +173,8 @@ impl Instrumentation {
quote!("const $ctx = { arguments };" as Stmt, ctx = ctx_ident,),
try_catch,
];

self.has_injected = true;
}

fn trace_expr_or_count(&mut self, func_expr: &mut FnExpr, name: &Atom) -> bool {
Expand Down
29 changes: 27 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ pub use instrumentation::*;
mod function_query;
pub use function_query::*;

use crate::error::OrchestrionError;

#[cfg(feature = "wasm")]
pub mod wasm;

Expand Down Expand Up @@ -112,6 +114,19 @@ impl InstrumentationVisitor {
!self.instrumentations.is_empty()
}

#[must_use]
pub fn has_injected(&self) -> bool {
self.instrumentations
.iter()
.any(Instrumentation::has_injected)
}

pub fn reset_has_injected(&mut self) {
for instr in &mut self.instrumentations {
instr.reset_has_injected();
}
}

/// Transform the given JavaScript code.
/// # Errors
/// Returns an error if the transformation fails.
Expand All @@ -125,7 +140,7 @@ impl InstrumentationVisitor {
)));

#[allow(clippy::redundant_closure_for_method_calls)]
Ok(try_with_handler(
let result = try_with_handler(
compiler.cm.clone(),
HandlerOpts {
color: ColorConfig::Never,
Expand Down Expand Up @@ -165,10 +180,20 @@ impl InstrumentationVisitor {
..Default::default()
},
)?;

Ok(result.code)
},
)
.map_err(|e| e.to_pretty_error())?)
.map_err(|e| e.to_pretty_error())?;

let has_injected = self.has_injected();
self.reset_has_injected();

if has_injected {
Ok(result)
} else {
Err(Box::new(OrchestrionError::InjectionMatchFailure))
}
}
}

Expand Down
10 changes: 6 additions & 4 deletions src/wasm.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::*;
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
use crate::{Config, InstrumentationConfig, InstrumentationVisitor, Instrumentor};
use std::path::PathBuf;
use swc::config::IsModule;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub struct InstrumentationMatcher(Instrumentor);
Expand Down Expand Up @@ -31,11 +33,11 @@ pub struct Transformer(InstrumentationVisitor);
#[wasm_bindgen]
impl Transformer {
#[wasm_bindgen]
pub fn transform(&mut self, contents: &str, is_module: bool) -> Result<String, JsValue> {
pub fn transform(&mut self, contents: &str, is_module: bool) -> Result<String, JsError> {
let is_module = IsModule::Bool(is_module);
self.0
.transform(contents, is_module)
.map_err(|e| JsValue::from_str(&e.to_string()))
.map_err(|e| JsError::new(&e.to_string()))
}
}

Expand Down
12 changes: 5 additions & 7 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,11 @@ pub fn transpile_and_test(test_file: &str, mjs: bool, config: Config) {
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();

let result = instrumentations
.transform(&contents, IsModule::Bool(mjs))
.unwrap();

let instrumented_file = test_dir.join(format!("instrumented.{}", extension));
let mut file = std::fs::File::create(&instrumented_file).unwrap();
file.write_all(result.as_bytes()).unwrap();
if let Ok(result) = instrumentations.transform(&contents, IsModule::Bool(mjs)) {
let instrumented_file = test_dir.join(format!("instrumented.{}", extension));
let mut file = std::fs::File::create(&instrumented_file).unwrap();
file.write_all(result.as_bytes()).unwrap();
}

let test_file = format!("test.{}", extension);
Command::new("node")
Expand Down
9 changes: 9 additions & 0 deletions tests/injection_failure/mod.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License.
* This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2025 Datadog, Inc.
**/
const fetch = async function (url) {
return 42;
}

export { fetch };
15 changes: 15 additions & 0 deletions tests/injection_failure/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use crate::common::*;
use orchestrion_js::*;

#[test]
fn injection_failure() {
transpile_and_test(
file!(),
true,
Config::new_single(InstrumentationConfig::new(
"some_expr",
test_module_matcher(),
FunctionQuery::function_expression("some", FunctionKind::Async),
)),
);
}
9 changes: 9 additions & 0 deletions tests/injection_failure/test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License.
* This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2025 Datadog, Inc.
**/
import { existsSync } from 'node:fs'
import { assert } from '../common/preamble.js';

const instrumented = existsSync('./instrumented.js');
assert.strictEqual(instrumented, false, 'instrumented.js should not exist');
1 change: 1 addition & 0 deletions tests/instrumentor_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod decl_mjs_mismatched_type;
mod expr_cjs;
mod expr_mjs;
mod index_cjs;
mod injection_failure;
mod multiple_class_method_cjs;
mod multiple_load_cjs;
mod object_method_cjs;
Expand Down
9 changes: 9 additions & 0 deletions tests/wasm/testdata/no-match.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class Down {
constructor() {
console.log('constructor')
}

fetch() {
console.log('fetch')
}
}
6 changes: 6 additions & 0 deletions tests/wasm/tests.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,9 @@ const outputCjs = matchedTransforms.transform(originalCjs.toString('utf8'), fals

const expectedCjs = await fs.readFile(path.join(import.meta.dirname, './testdata/expected-cjs.js'))
assert.strictEqual(outputCjs, expectedCjs.toString('utf8'));

const noMatch = await fs.readFile(path.join(import.meta.dirname, './testdata/no-match.mjs'));

assert.throws(() => {
matchedTransforms.transform(noMatch.toString('utf8'), true);
}, { message: "Injection match failed" });