Skip to content
This repository was archived by the owner on Mar 29, 2024. It is now read-only.

Commit dde27ce

Browse files
committed
Add external exception wiring option to V8\Isolate::ThrowException()
1 parent c21ac35 commit dde27ce

9 files changed

+216
-31
lines changed

src/php_v8_isolate.cc

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "php_v8_context.h"
2222
#include "php_v8_exceptions.h"
2323
#include "php_v8_stack_trace.h"
24+
#include "php_v8_object.h"
2425
#include "php_v8_value.h"
2526
#include "php_v8_a.h"
2627
#include "php_v8.h"
@@ -346,8 +347,9 @@ static PHP_METHOD(V8Isolate, GetEnteredContext) {
346347
static PHP_METHOD(V8Isolate, ThrowException) {
347348
zval *php_v8_context_zv;
348349
zval *php_v8_value_zv;
350+
zval *exception_zv = NULL;
349351

350-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "oo", &php_v8_context_zv, &php_v8_value_zv) == FAILURE) {
352+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "oo|o", &php_v8_context_zv, &php_v8_value_zv, &exception_zv) == FAILURE) {
351353
return;
352354
}
353355

@@ -364,6 +366,22 @@ static PHP_METHOD(V8Isolate, ThrowException) {
364366

365367
v8::Local<v8::Value> local_value = php_v8_value_get_local(php_v8_value);
366368

369+
if (NULL != exception_zv) {
370+
if (!local_value->IsObject()) {
371+
PHP_V8_THROW_VALUE_EXCEPTION("Unable to associate external exception with non-object value");
372+
return;
373+
}
374+
375+
php_v8_value_t *php_v8_value = php_v8_object_get_self_ptr(php_v8_isolate, local_value.As<v8::Object>());
376+
377+
if (!Z_ISUNDEF(php_v8_value->exception)) {
378+
PHP_V8_THROW_VALUE_EXCEPTION("Another external exception is already associated with a given value");
379+
return;
380+
}
381+
382+
ZVAL_COPY(&php_v8_value->exception, exception_zv);
383+
}
384+
367385
isolate->ThrowException(local_value);
368386
}
369387

@@ -505,6 +523,7 @@ ZEND_END_ARG_INFO()
505523
ZEND_BEGIN_ARG_INFO_EX(arginfo_v8_isolate_ThrowException, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 2)
506524
ZEND_ARG_OBJ_INFO(0, context, V8\\Context, 0)
507525
ZEND_ARG_OBJ_INFO(0, value, V8\\Value, 0)
526+
ZEND_ARG_OBJ_INFO(0, e, Throwable, 0)
508527
ZEND_END_ARG_INFO()
509528

510529
PHP_V8_ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_v8_isolate_IdleNotificationDeadline, ZEND_RETURN_VALUE, 1, _IS_BOOL, 0)

src/php_v8_try_catch.cc

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,15 @@ void php_v8_try_catch_create_from_try_catch(zval *return_value, php_v8_isolate_t
4343

4444
if (try_catch && !try_catch->Exception().IsEmpty()) {
4545
zval exception_zv;
46-
php_v8_get_or_create_value(&exception_zv, try_catch->Exception(), php_v8_isolate);
46+
php_v8_value_t * php_v8_value = php_v8_get_or_create_value(&exception_zv, try_catch->Exception(), php_v8_isolate);
4747
zend_update_property(this_ce, return_value, ZEND_STRL("exception"), &exception_zv);
48+
49+
if (!Z_ISUNDEF(php_v8_value->exception)) {
50+
zend_update_property(this_ce, return_value, ZEND_STRL("external_exception"), &php_v8_value->exception);
51+
zval_ptr_dtor(&php_v8_value->exception);
52+
ZVAL_UNDEF(&php_v8_value->exception);
53+
}
54+
4855
zval_ptr_dtor(&exception_zv);
4956
}
5057

@@ -71,18 +78,20 @@ static PHP_METHOD(V8TryCatch, __construct) {
7178
zval *php_v8_exception_zv = NULL;
7279
zval *php_v8_stack_trace_zv = NULL;
7380
zval *php_v8_message_zv = NULL;
81+
zval *external_exception_zv = NULL;
7482

7583
zend_bool can_continue = '\0';
7684
zend_bool has_terminated = '\0';
7785

78-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "oo|o!o!o!bb",
86+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "oo|o!o!o!bbo!",
7987
&php_v8_isolate_zv,
8088
&php_v8_context_zv,
8189
&php_v8_exception_zv,
8290
&php_v8_stack_trace_zv,
8391
&php_v8_message_zv,
8492
&can_continue,
85-
&has_terminated) == FAILURE) {
93+
&has_terminated,
94+
&external_exception_zv) == FAILURE) {
8695
return;
8796
}
8897

@@ -114,6 +123,10 @@ static PHP_METHOD(V8TryCatch, __construct) {
114123

115124
zend_update_property_bool(this_ce, getThis(), ZEND_STRL("can_continue"), can_continue);
116125
zend_update_property_bool(this_ce, getThis(), ZEND_STRL("has_terminated"), has_terminated);
126+
127+
if (external_exception_zv != NULL) {
128+
zend_update_property(this_ce, getThis(), ZEND_STRL("external_exception"), external_exception_zv);
129+
}
117130
}
118131

119132
static PHP_METHOD(V8TryCatch, GetIsolate)
@@ -203,6 +216,18 @@ static PHP_METHOD(V8TryCatch, HasTerminated)
203216
}
204217

205218

219+
static PHP_METHOD(V8TryCatch, getExternalException)
220+
{
221+
zval rv;
222+
223+
if (zend_parse_parameters_none() == FAILURE) {
224+
return;
225+
}
226+
227+
RETVAL_ZVAL(zend_read_property(this_ce, getThis(), ZEND_STRL("external_exception"), 0, &rv), 1, 0);
228+
}
229+
230+
206231
ZEND_BEGIN_ARG_INFO_EX(arginfo_v8_try_catch___construct, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 2)
207232
ZEND_ARG_OBJ_INFO(0, isolate, V8\\Isolate, 0)
208233
ZEND_ARG_OBJ_INFO(0, context, V8\\Context, 0)
@@ -211,6 +236,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_v8_try_catch___construct, ZEND_SEND_BY_VAL, ZEND_
211236
ZEND_ARG_OBJ_INFO(0, message, V8\\Message, 1)
212237
ZEND_ARG_TYPE_INFO(0, can_continue, _IS_BOOL, 0)
213238
ZEND_ARG_TYPE_INFO(0, has_terminated, _IS_BOOL, 0)
239+
ZEND_ARG_OBJ_INFO(0, external_exception, Throwable, 1)
214240
ZEND_END_ARG_INFO()
215241

216242
PHP_V8_ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_v8_try_catch_GetIsolate, ZEND_RETURN_VALUE, 0, V8\\Isolate, 0)
@@ -234,6 +260,9 @@ ZEND_END_ARG_INFO()
234260
PHP_V8_ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_v8_try_catch_HasTerminated, ZEND_RETURN_VALUE, 0, _IS_BOOL, 0)
235261
ZEND_END_ARG_INFO()
236262

263+
PHP_V8_ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_v8_try_catch_getExternalException, ZEND_RETURN_VALUE, 0, Throwable, 1)
264+
ZEND_END_ARG_INFO()
265+
237266

238267
static const zend_function_entry php_v8_try_catch_methods[] = {
239268
PHP_ME(V8TryCatch, __construct, arginfo_v8_try_catch___construct, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
@@ -248,6 +277,8 @@ static const zend_function_entry php_v8_try_catch_methods[] = {
248277
PHP_ME(V8TryCatch, CanContinue, arginfo_v8_try_catch_CanContinue, ZEND_ACC_PUBLIC)
249278
PHP_ME(V8TryCatch, HasTerminated, arginfo_v8_try_catch_HasTerminated, ZEND_ACC_PUBLIC)
250279

280+
PHP_ME(V8TryCatch, getExternalException, arginfo_v8_try_catch_getExternalException, ZEND_ACC_PUBLIC)
281+
251282
PHP_FE_END
252283
};
253284

@@ -266,5 +297,7 @@ PHP_MINIT_FUNCTION (php_v8_try_catch) {
266297
zend_declare_property_null(this_ce, ZEND_STRL("can_continue"), ZEND_ACC_PRIVATE);
267298
zend_declare_property_null(this_ce, ZEND_STRL("has_terminated"), ZEND_ACC_PRIVATE);
268299

300+
zend_declare_property_null(this_ce, ZEND_STRL("external_exception"), ZEND_ACC_PRIVATE);
301+
269302
return SUCCESS;
270303
}

src/php_v8_value.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,16 @@ static HashTable * php_v8_value_gc(zval *object, zval **table, int *n) {
9191

9292
php_v8_callbacks_gc(php_v8_value->persistent_data, &php_v8_value->gc_data, &php_v8_value->gc_data_count, table, n);
9393

94+
if(!Z_ISUNDEF(php_v8_value->exception)) {
95+
*n = *n + 1;
96+
97+
if (php_v8_value->gc_data_count < *n) {
98+
php_v8_value->gc_data = (zval *)safe_erealloc(php_v8_value->gc_data, *n, sizeof(zval), 0);
99+
}
100+
101+
ZVAL_COPY_VALUE(&php_v8_value->gc_data[*n-1], &php_v8_value->exception);
102+
}
103+
94104
return zend_std_get_properties(object);
95105
}
96106

@@ -114,6 +124,11 @@ static void php_v8_value_free(zend_object *object) {
114124
}
115125
}
116126

127+
if (!Z_ISUNDEF(php_v8_value->exception)) {
128+
zval_ptr_dtor(&php_v8_value->exception);
129+
ZVAL_UNDEF(&php_v8_value->exception);
130+
}
131+
117132
if (php_v8_value->gc_data) {
118133
efree(php_v8_value->gc_data);
119134
}

src/php_v8_value.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ struct _php_v8_value_t {
111111
bool is_weak;
112112
v8::Persistent<v8::Value> *persistent;
113113
phpv8::PersistentData *persistent_data;
114+
zval exception;
114115

115116
zval *gc_data;
116117
int gc_data_count;

stubs/src/Isolate.php

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
namespace V8;
1616

1717

18+
use Throwable;
19+
use V8\Exceptions\ValueException;
20+
21+
1822
class Isolate
1923
{
2024
public function __construct(StartupData $snapshot = null)
@@ -79,11 +83,17 @@ public function GetEnteredContext(): Context
7983
* has been handled does it become legal to invoke JavaScript operations.
8084
*
8185
* @param Context $context
82-
* @param Value $value
86+
* @param Value $value
87+
* @param Throwable|null $e Exception to associate with a given value.
88+
* Because how underlying object wiring done, wiring PHP to V8 exceptions
89+
* is possible only for V8 exception that are instances of ObjectValue.
8390
*
8491
* @return void
92+
*
93+
* @throws ValueException When trying to associate external exception with non-object value
94+
* @throws ValueException When another external exception is already associated with a given value
8595
*/
86-
public function ThrowException(Context $context, Value $value)
96+
public function ThrowException(Context $context, Value $value, Throwable $e = null)
8797
{
8898
}
8999

@@ -130,20 +140,6 @@ public function CancelTerminateExecution()
130140
{
131141
}
132142

133-
// /**
134-
// * Request V8 to interrupt long running JavaScript code and invoke
135-
// * the given |callback| passing the given |data| to it. After |callback|
136-
// * returns control will be returned to the JavaScript code.
137-
// * There may be a number of interrupt requests in flight.
138-
// * Can be called from another thread without acquiring a |Locker|.
139-
// * Registered |callback| must not reenter interrupted Isolate.
140-
// */
141-
//// void RequestInterrupt(InterruptCallback callback, void* data);
142-
// public function RequestInterrupt()
143-
// {
144-
//
145-
// }
146-
147143
/**
148144
* Optional notification that the embedder is idle.
149145
* V8 uses the notification to perform garbage collection.
@@ -199,7 +195,7 @@ public function IsInUse(): bool
199195
* and report it to the message listeners. The option is off by default.
200196
*
201197
* @param bool $capture
202-
* @param int $frame_limit
198+
* @param int $frame_limit
203199
*/
204200
public function SetCaptureStackTraceForUncaughtExceptions(bool $capture, int $frame_limit = 10)
205201
{

stubs/src/TryCatch.php

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414

1515
namespace V8;
1616

17+
use Throwable;
18+
19+
1720
/**
1821
* An external exception handler.
1922
*/
@@ -47,6 +50,10 @@ class TryCatch
4750
* @var bool
4851
*/
4952
private $has_terminated;
53+
/**
54+
* @var null|Throwable
55+
*/
56+
private $external_exception;
5057

5158
/**
5259
* Creates a new try/catch block and registers it with v8. Note that
@@ -55,11 +62,12 @@ class TryCatch
5562
*
5663
* @param Isolate $isolate
5764
* @param Context $context
58-
* @param Value $exception
59-
* @param Value $stack_trace
65+
* @param Value $exception
66+
* @param Value $stack_trace
6067
* @param Message $message
61-
* @param bool $can_continue
62-
* @param bool $has_terminated
68+
* @param bool $can_continue
69+
* @param bool $has_terminated
70+
* @param Throwable|null $external_exception
6371
*/
6472
public function __construct(
6573
Isolate $isolate,
@@ -68,14 +76,16 @@ public function __construct(
6876
Value $stack_trace = null,
6977
Message $message = null,
7078
bool $can_continue = false,
71-
bool $has_terminated = false
79+
bool $has_terminated = false,
80+
Throwable $external_exception = null
7281
) {
7382
$this->isolate = $isolate;
7483
$this->exception = $exception;
7584
$this->stack_trace = $stack_trace;
7685
$this->message = $message;
7786
$this->can_continue = $can_continue;
7887
$this->has_terminated = $has_terminated;
88+
$this->external_exception = $external_exception;
7989
}
8090

8191
public function GetIsolate(): Isolate
@@ -160,4 +170,12 @@ public function HasTerminated(): bool
160170
{
161171
return $this->has_terminated;
162172
}
173+
174+
/**
175+
* @return null|Throwable
176+
*/
177+
public function getExternalException()
178+
{
179+
return $this->external_exception;
180+
}
163181
}

tests/V8ExceptionsTryCatchException.phpt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ object(V8\Exceptions\TryCatchException)#5 (10) {
5858
}
5959
}
6060
["try_catch":"V8\Exceptions\TryCatchException":private]=>
61-
object(V8\TryCatch)#4 (7) {
61+
object(V8\TryCatch)#4 (8) {
6262
["isolate":"V8\TryCatch":private]=>
6363
object(V8\Isolate)#2 (0) {
6464
}
@@ -78,6 +78,8 @@ object(V8\Exceptions\TryCatchException)#5 (10) {
7878
bool(false)
7979
["has_terminated":"V8\TryCatch":private]=>
8080
bool(false)
81+
["external_exception":"V8\TryCatch":private]=>
82+
NULL
8183
}
8284
}
8385

0 commit comments

Comments
 (0)