Skip to content

Commit 6b5758d

Browse files
committed
Expose ModifyCodeGenerationFromStrings callback
1 parent 58a7a86 commit 6b5758d

File tree

4 files changed

+90
-0
lines changed

4 files changed

+90
-0
lines changed

src/binding.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,13 @@ void v8__Isolate__SetUseCounterCallback(
288288
isolate->SetUseCounterCallback(callback);
289289
}
290290

291+
void v8__Isolate__SetModifyCodeGenerationFromStringsCallback(
292+
v8::Isolate* isolate,
293+
v8::ModifyCodeGenerationFromStringsCallback2 callback
294+
) {
295+
isolate->SetModifyCodeGenerationFromStringsCallback(callback);
296+
}
297+
291298
bool v8__Isolate__AddMessageListener(v8::Isolate* isolate,
292299
v8::MessageCallback callback) {
293300
return isolate->AddMessageListener(callback);

src/isolate.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,22 @@ pub type OomErrorCallback =
559559
#[derive(Debug)]
560560
pub struct HeapStatistics([usize; 16]);
561561

562+
#[repr(C)]
563+
pub struct ModifyCodeGenerationFromStringsResult<'s> {
564+
pub codegen_allowed: bool,
565+
pub modified_source: Option<Local<'s, String>>,
566+
}
567+
568+
// We use Option<NonNull<T>> which _is_ FFI-safe.
569+
// See https://doc.rust-lang.org/nomicon/other-reprs.html
570+
#[allow(improper_ctypes_definitions)]
571+
pub type ModifyCodeGenerationFromStringsCallback<'s> =
572+
extern "C" fn(
573+
context: Local<'s, Context>,
574+
source: Local<'s, Value>,
575+
is_code_like: bool,
576+
) -> ModifyCodeGenerationFromStringsResult<'s>;
577+
562578
// Windows x64 ABI: MaybeLocal<Value> returned on the stack.
563579
#[cfg(target_os = "windows")]
564580
pub type PrepareStackTraceCallback<'s> =
@@ -694,6 +710,13 @@ unsafe extern "C" {
694710
isolate: *mut Isolate,
695711
callback: UseCounterCallback,
696712
);
713+
// We use Option<NonNull<T>> which _is_ FFI-safe.
714+
// See https://doc.rust-lang.org/nomicon/other-reprs.html
715+
#[allow(improper_ctypes)]
716+
fn v8__Isolate__SetModifyCodeGenerationFromStringsCallback(
717+
isolate: *mut Isolate,
718+
callback: ModifyCodeGenerationFromStringsCallback,
719+
);
697720
fn v8__Isolate__RequestInterrupt(
698721
isolate: *const Isolate,
699722
callback: InterruptCallback,
@@ -1380,6 +1403,20 @@ impl Isolate {
13801403
unsafe { v8__Isolate__RemoveGCPrologueCallback(self, callback, data) }
13811404
}
13821405

1406+
/// This specifies the callback called by v8 when JS is trying to dynamically execute
1407+
/// code using `eval` or the `Function` constructor.
1408+
///
1409+
/// The callback can decide whether to allow code generation and, if so, modify
1410+
/// the source code beforehand.
1411+
pub fn set_modify_code_generation_from_strings_callback(
1412+
&mut self,
1413+
callback: ModifyCodeGenerationFromStringsCallback,
1414+
) {
1415+
unsafe {
1416+
v8__Isolate__SetModifyCodeGenerationFromStringsCallback(self, callback)
1417+
}
1418+
}
1419+
13831420
/// Add a callback to invoke in case the heap size is close to the heap limit.
13841421
/// If multiple callbacks are added, only the most recently added callback is
13851422
/// invoked.

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ pub use isolate::MemoryPressureLevel;
115115
pub use isolate::MessageCallback;
116116
pub use isolate::MessageErrorLevel;
117117
pub use isolate::MicrotasksPolicy;
118+
pub use isolate::ModifyCodeGenerationFromStringsCallback;
119+
pub use isolate::ModifyCodeGenerationFromStringsResult;
118120
pub use isolate::ModuleImportPhase;
119121
pub use isolate::NearHeapLimitCallback;
120122
pub use isolate::OomDetails;

tests/test_api.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12102,6 +12102,50 @@ fn use_counter_callback() {
1210212102
assert_eq!(COUNT.load(Ordering::Relaxed), 1);
1210312103
}
1210412104

12105+
#[test]
12106+
fn code_generation_from_strings_callback() {
12107+
static CODEGEN_ALLOWED: AtomicBool = AtomicBool::new(false);
12108+
static CALLED: AtomicBool = AtomicBool::new(false);
12109+
12110+
#[allow(improper_ctypes_definitions)]
12111+
extern "C" fn callback<'s>(
12112+
_context: v8::Local<'s, v8::Context>,
12113+
_source: v8::Local<'s, v8::Value>,
12114+
_is_code_like: bool,
12115+
) -> v8::ModifyCodeGenerationFromStringsResult<'s> {
12116+
CALLED.store(true, Ordering::Relaxed);
12117+
v8::ModifyCodeGenerationFromStringsResult {
12118+
codegen_allowed: CODEGEN_ALLOWED.load(Ordering::Relaxed),
12119+
modified_source: None,
12120+
}
12121+
}
12122+
12123+
let _setup_guard = setup::parallel_test();
12124+
let mut isolate = v8::Isolate::new(Default::default());
12125+
isolate.set_modify_code_generation_from_strings_callback(callback);
12126+
let mut scope = v8::HandleScope::new(&mut isolate);
12127+
let context = v8::Context::new(&mut scope, Default::default());
12128+
// Must be set to false, otherwise code generation is unconditionally allowed and the callback is never used
12129+
context.set_allow_generation_from_strings(false);
12130+
12131+
let scope = &mut v8::ContextScope::new(&mut scope, context);
12132+
12133+
// Code generation should be disallowed
12134+
{
12135+
let tc = &mut v8::TryCatch::new(scope);
12136+
eval(tc, "eval('1 + 1')");
12137+
assert_eq!(
12138+
tc.message().unwrap().get(tc).to_rust_string_lossy(tc),
12139+
"Uncaught EvalError: Code generation from strings disallowed for this context"
12140+
);
12141+
}
12142+
12143+
// Enable code generation
12144+
CODEGEN_ALLOWED.store(true, Ordering::Relaxed);
12145+
let result: Option<v8::Local<'_, v8::Value>> = eval(scope, "eval('1 + 1')");
12146+
assert_eq!(result.unwrap().number_value(scope).unwrap(), 2.0);
12147+
}
12148+
1210512149
#[test]
1210612150
fn test_eternals() {
1210712151
let _setup_guard = setup::parallel_test();

0 commit comments

Comments
 (0)