Skip to content

Commit 68f2a5c

Browse files
committed
Implement exceptions
1 parent 0a12213 commit 68f2a5c

File tree

4 files changed

+66
-12
lines changed

4 files changed

+66
-12
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ The extension includes builds of libv8, via the [v8 crate](https://docs.rs/v8/la
3333
- [x] `setModuleLoader`
3434
- [ ] `setModuleNormaliser`
3535
- [ ] Subclassing V8Js
36-
- [ ] Custom exceptions for `V8JsScriptException`, `V8JsMemoryLimitException` and `V8JsTimeLimitException`
36+
- [x] Custom exceptions for `V8JsScriptException`, `V8JsMemoryLimitException` and `V8JsTimeLimitException`
37+
- [ ] Support for `V8JsScriptException::getJsLineNumber` etc.
3738
- [ ] Support for `FLAG_PROPAGATE_PHP_EXCEPTIONS`, `V8Js::FLAG_FORCE_ARRAY`
3839
- [ ] Throw correct exception subclasses
3940
- [ ] PHP INI settings `v8js.flags`

src/lib.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ mod runtime;
1414
pub use crate::runtime::JSRuntime;
1515
pub use crate::runtime::Error as RuntimeError;
1616

17-
static mut V8JSTIMELIMITEXCEPTION_EXCEPTION: Option<&'static ClassEntry> = None;
18-
static mut V8JSMEMORYLIMITEXCEPTION_EXCEPTION: Option<&'static ClassEntry> = None;
17+
static mut V8JS_TIME_LIMIT_EXCEPTION: Option<&'static ClassEntry> = None;
18+
static mut V8JS_MEMORY_LIMIT_EXCEPTION: Option<&'static ClassEntry> = None;
19+
static mut V8JS_SCRIPT_EXCEPTION: Option<&'static ClassEntry> = None;
1920

2021
pub fn zval_from_jsvalue(result: v8::Local<v8::Value>, scope: &mut v8::HandleScope) -> Zval {
2122
if result.is_string() {
@@ -214,12 +215,15 @@ impl V8Js {
214215
Err(e) => {
215216
match e {
216217
RuntimeError::ExecutionTimeout => {
217-
Err(PhpException::new("".into(), 0, unsafe{ V8JSTIMELIMITEXCEPTION_EXCEPTION.unwrap() } ))
218+
Err(PhpException::new("".into(), 0, unsafe{ V8JS_TIME_LIMIT_EXCEPTION.unwrap() } ))
218219
},
219220
RuntimeError::MemoryLimitExceeded => {
220-
Err(PhpException::new("".into(), 0, unsafe{ V8JSMEMORYLIMITEXCEPTION_EXCEPTION.unwrap() } ))
221+
Err(PhpException::new("".into(), 0, unsafe{ V8JS_MEMORY_LIMIT_EXCEPTION.unwrap() } ))
221222
},
222-
_ => Err(PhpException::default(String::from("Exception")))
223+
RuntimeError::ScriptExecutionError(error) => {
224+
Err(PhpException::new(error.message.into(), 0, unsafe{ V8JS_SCRIPT_EXCEPTION.unwrap() } ))
225+
}
226+
_ => Err(PhpException::default(String::from("Unknown error.")))
223227
}
224228
}
225229
}
@@ -468,13 +472,19 @@ pub fn startup() {
468472
.extends(ce::exception())
469473
.build()
470474
.expect("Failed to build V8JsTimeLimitException");
471-
unsafe { V8JSTIMELIMITEXCEPTION_EXCEPTION.replace(ce) };
475+
unsafe { V8JS_TIME_LIMIT_EXCEPTION.replace(ce) };
472476

473477
let ce = ClassBuilder::new("V8JsMemoryLimitException")
474478
.extends(ce::exception())
475479
.build()
476480
.expect("Failed to build V8JsMemoryLimitException");
477-
unsafe { V8JSMEMORYLIMITEXCEPTION_EXCEPTION.replace(ce) };
481+
unsafe { V8JS_MEMORY_LIMIT_EXCEPTION.replace(ce) };
482+
483+
let ce = ClassBuilder::new("V8JsScriptException")
484+
.extends(ce::exception())
485+
.build()
486+
.expect("Failed to build V8JsScriptException");
487+
unsafe { V8JS_SCRIPT_EXCEPTION.replace(ce) };
478488
}
479489

480490
#[php_module]

src/runtime.rs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,24 @@ pub struct JsRuntimeState {
1414
pub commonjs_modules: HashMap<String, v8::Global<v8::Value>>,
1515
}
1616

17+
#[derive(Debug)]
18+
pub struct ScriptExecutionErrorData {
19+
pub file_name: String,
20+
pub line_number: u64,
21+
pub start_column: u64,
22+
pub end_column: u64,
23+
pub source_line: String,
24+
pub trace: String,
25+
pub message: String,
26+
}
27+
1728
#[derive(Debug)]
1829
pub enum Error {
1930
JSRuntimeError,
2031
ExecutionTimeout,
2132
MemoryLimitExceeded,
2233
V8Error,
34+
ScriptExecutionError(ScriptExecutionErrorData),
2335
}
2436

2537
fn init_v8() {
@@ -159,8 +171,11 @@ impl JSRuntime {
159171
false,
160172
false,
161173
);
174+
175+
let try_catch = &mut v8::TryCatch::new(scope);
176+
162177
let script =
163-
v8::Script::compile(scope, code, Some(&script_origin)).ok_or(Error::JSRuntimeError)?;
178+
v8::Script::compile(try_catch, code, Some(&script_origin)).ok_or(Error::JSRuntimeError)?;
164179
let stop_flag = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
165180
let time_limit_hit = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
166181
let memory_limit_hit = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
@@ -217,7 +232,7 @@ impl JSRuntime {
217232
});
218233
}
219234

220-
let result = script.run(scope);
235+
let result = script.run(try_catch);
221236
stop_flag.store(true, std::sync::atomic::Ordering::SeqCst);
222237
let time_limit_hit = time_limit_hit.load(std::sync::atomic::Ordering::SeqCst);
223238
let memory_limit_hit = memory_limit_hit.load(std::sync::atomic::Ordering::SeqCst);
@@ -237,8 +252,24 @@ impl JSRuntime {
237252

238253
stop_flag.store(true, std::sync::atomic::Ordering::SeqCst);
239254
let result = match result {
240-
Some(result) => Ok(Some(v8::Global::new(scope, result))),
241-
None => Ok(None),
255+
Some(result) => Ok(Some(v8::Global::new(try_catch, result))),
256+
None => {
257+
let exception = try_catch.exception().unwrap();
258+
let exception_string = exception
259+
.to_string(try_catch)
260+
.unwrap()
261+
.to_rust_string_lossy(try_catch);
262+
let message = try_catch.message().unwrap();
263+
Err(Error::ScriptExecutionError(ScriptExecutionErrorData {
264+
file_name: message.get_script_resource_name(try_catch).unwrap().to_rust_string_lossy(try_catch),
265+
line_number: u64::try_from(message.get_line_number(try_catch).unwrap()).unwrap(),
266+
start_column: u64::try_from(message.get_start_column()).unwrap(),
267+
end_column: u64::try_from(message.get_end_column()).unwrap(),
268+
trace: "".into(), // todo,
269+
message: exception_string,
270+
source_line: message.get_source_line(try_catch).unwrap().to_rust_string_lossy(try_catch),
271+
}))
272+
}
242273
};
243274
result
244275
}

tests/compile_error.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
$v8js = new V8Js;
4+
5+
try {
6+
$result = $v8js->executeString( 'my_func();' );
7+
} catch ( V8JsScriptException $e ) {
8+
assert( $e->getJsFileName() );
9+
}
10+
11+
12+

0 commit comments

Comments
 (0)