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

Commit 7d1f899

Browse files
committed
Add signature support to Tpl:SetNativeDataProperty() and ObjTpl::SetAccessor() (simplified to $receiver)
1 parent d6e6ce4 commit 7d1f899

8 files changed

+169
-84
lines changed

src/php_v8_function_template.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_php_v8_function_template_SetNativeDataProperty, Z
482482
ZEND_ARG_CALLABLE_INFO(0, getter, 0)
483483
ZEND_ARG_CALLABLE_INFO(0, setter, 1)
484484
ZEND_ARG_TYPE_INFO(0, attributes, IS_LONG, 0)
485+
ZEND_ARG_OBJ_INFO(0, receiver, V8\\FunctionTemplate, 1)
485486
ZEND_ARG_TYPE_INFO(0, settings, IS_LONG, 0)
486487
ZEND_END_ARG_INFO()
487488

src/php_v8_object_template.cc

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,21 +209,25 @@ static PHP_METHOD(V8ObjectTemplate, NewInstance) {
209209

210210
static PHP_METHOD(V8ObjectTemplate, SetAccessor) {
211211
zval *php_v8_name_zv;
212+
zval *php_v8_receiver_zv = NULL;
213+
212214
zend_long attributes = 0;
213215
zend_long settings = 0;
216+
v8::Local<v8::AccessorSignature> signature;
214217

215218
zend_fcall_info getter_fci = empty_fcall_info;
216219
zend_fcall_info_cache getter_fci_cache = empty_fcall_info_cache;
217220

218221
zend_fcall_info setter_fci = empty_fcall_info;
219222
zend_fcall_info_cache setter_fci_cache = empty_fcall_info_cache;
220223

221-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "of|f!ll",
224+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "of|f!llo!",
222225
&php_v8_name_zv,
223226
&getter_fci, &getter_fci_cache,
224227
&setter_fci, &setter_fci_cache,
225228
&settings,
226-
&attributes
229+
&attributes,
230+
&php_v8_receiver_zv
227231
) == FAILURE) {
228232
return;
229233
}
@@ -247,7 +251,6 @@ static PHP_METHOD(V8ObjectTemplate, SetAccessor) {
247251
v8::AccessorNameGetterCallback getter;
248252
v8::AccessorNameSetterCallback setter = 0;
249253
v8::Local<v8::External> data;
250-
v8::Local<v8::AccessorSignature> signature; // TODO: add AccessorSignature support
251254

252255
phpv8::CallbacksBucket *bucket = php_v8_object_template->persistent_data->bucket("accessor_",
253256
local_name->IsSymbol(), name);
@@ -261,6 +264,13 @@ static PHP_METHOD(V8ObjectTemplate, SetAccessor) {
261264
setter = php_v8_callback_accessor_name_setter;
262265
}
263266

267+
if (php_v8_receiver_zv) {
268+
PHP_V8_FETCH_FUNCTION_TEMPLATE_WITH_CHECK(php_v8_receiver_zv, php_v8_receiver);
269+
PHP_V8_DATA_ISOLATES_CHECK(php_v8_object_template, php_v8_receiver);
270+
271+
signature = v8::AccessorSignature::New(isolate, php_v8_function_template_get_local(php_v8_receiver));
272+
}
273+
264274
local_obj_tpl->SetAccessor(local_name,
265275
getter,
266276
setter,
@@ -404,6 +414,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_php_v8_object_template_SetNativeDataProperty, ZEN
404414
ZEND_ARG_CALLABLE_INFO(0, getter, 0)
405415
ZEND_ARG_CALLABLE_INFO(0, setter, 1)
406416
ZEND_ARG_TYPE_INFO(0, attributes, IS_LONG, 0)
417+
ZEND_ARG_OBJ_INFO(0, receiver, V8\\FunctionTemplate, 1)
407418
ZEND_ARG_TYPE_INFO(0, settings, IS_LONG, 0)
408419
ZEND_END_ARG_INFO()
409420

@@ -420,6 +431,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_php_v8_object_template_SetAccessor, ZEND_SEND_BY_
420431
ZEND_ARG_CALLABLE_INFO(0, setter, 1)
421432
ZEND_ARG_TYPE_INFO(0, settings, IS_LONG, 0)
422433
ZEND_ARG_TYPE_INFO(0, attributes, IS_LONG, 0)
434+
ZEND_ARG_OBJ_INFO(0, receiver, V8\\FunctionTemplate, 1)
423435
ZEND_END_ARG_INFO()
424436

425437
// void method

src/php_v8_template.cc

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -217,18 +217,28 @@ void php_v8_template_SetAccessorProperty(v8::Isolate *isolate, v8::Local<T> loca
217217
template<class T, typename N>
218218
void php_v8_template_SetNativeDataProperty(v8::Isolate *isolate, v8::Local<T> local_template, N* php_v8_template, INTERNAL_FUNCTION_PARAMETERS) {
219219
zval *php_v8_name_zv;
220-
221-
zend_long attributes = v8::PropertyAttribute::None;
222-
zend_long settings = v8::AccessControl::DEFAULT;
220+
zval *php_v8_receiver_zv = NULL;
223221

224222
zend_fcall_info getter_fci = empty_fcall_info;
225223
zend_fcall_info_cache getter_fci_cache = empty_fcall_info_cache;
226224

227225
zend_fcall_info setter_fci = empty_fcall_info;
228226
zend_fcall_info_cache setter_fci_cache = empty_fcall_info_cache;
229227

230-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "of|f!ll", &php_v8_name_zv, &getter_fci, &getter_fci_cache, &setter_fci, &setter_fci_cache, &attributes, &settings) ==
231-
FAILURE) {
228+
v8::AccessorNameGetterCallback getter;
229+
v8::AccessorNameSetterCallback setter = 0;
230+
231+
zend_long attributes = v8::PropertyAttribute::None;
232+
v8::Local<v8::AccessorSignature> signature;
233+
zend_long settings = v8::AccessControl::DEFAULT;
234+
235+
v8::Local<v8::External> data;
236+
237+
if (zend_parse_parameters(ZEND_NUM_ARGS(),
238+
"of|f!lo!l",
239+
&php_v8_name_zv, &getter_fci, &getter_fci_cache, &setter_fci, &setter_fci_cache,
240+
&attributes, &php_v8_receiver_zv, &settings
241+
) == FAILURE) {
232242
return;
233243
}
234244

@@ -240,11 +250,6 @@ void php_v8_template_SetNativeDataProperty(v8::Isolate *isolate, v8::Local<T> lo
240250

241251
v8::Local<v8::Name> local_name = php_v8_value_get_local_as<v8::Name>(php_v8_name);
242252

243-
v8::AccessorNameGetterCallback getter;
244-
v8::AccessorNameSetterCallback setter = 0;
245-
v8::Local<v8::External> data;
246-
v8::Local<v8::AccessorSignature> signature; // TODO: add AccessorSignature support
247-
248253
PHP_V8_CONVERT_FROM_V8_STRING_TO_STRING(name, local_name);
249254

250255
phpv8::CallbacksBucket *bucket = php_v8_template->persistent_data->bucket("native_data_property_", local_name->IsSymbol(), name);
@@ -258,6 +263,13 @@ void php_v8_template_SetNativeDataProperty(v8::Isolate *isolate, v8::Local<T> lo
258263
setter = php_v8_callback_accessor_name_setter;
259264
}
260265

266+
if (php_v8_receiver_zv) {
267+
PHP_V8_FETCH_FUNCTION_TEMPLATE_WITH_CHECK(php_v8_receiver_zv, php_v8_receiver);
268+
PHP_V8_DATA_ISOLATES_CHECK(php_v8_template, php_v8_receiver);
269+
270+
signature = v8::AccessorSignature::New(isolate, php_v8_function_template_get_local(php_v8_receiver));
271+
}
272+
261273
local_template->SetNativeDataProperty(local_name,
262274
getter,
263275
setter,
@@ -290,6 +302,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_php_v8_template_SetNativeDataProperty, ZEND_SEND_
290302
ZEND_ARG_CALLABLE_INFO(0, getter, 0)
291303
ZEND_ARG_CALLABLE_INFO(0, setter, 1)
292304
ZEND_ARG_TYPE_INFO(0, attributes, IS_LONG, 0)
305+
ZEND_ARG_OBJ_INFO(0, receiver, V8\\FunctionTemplate, 1)
293306
ZEND_ARG_TYPE_INFO(0, settings, IS_LONG, 0)
294307
ZEND_END_ARG_INFO()
295308

stubs/src/AccessControl.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515

1616
namespace V8;
1717

18-
1918
class AccessControl
2019
{
21-
const DEFAULT_ACCESS = 0;
22-
const ALL_CAN_READ = 1;
23-
const ALL_CAN_WRITE = 2;
20+
const DEFAULT_ACCESS = 0; // do not allow cross-context access
21+
const ALL_CAN_READ = 1; // all cross-context reads are allowed
22+
const ALL_CAN_WRITE = 2; // all cross-context writes are allowed
23+
// ALL_CAN_READ | ALL_CAN_WRITE could be use allow all cross-context access
2424
}

stubs/src/ObjectTemplate.php

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -47,41 +47,33 @@ public function NewInstance(Context $context): ObjectValue
4747
* are called instead of getting and setting the property directly
4848
* on the JavaScript object.
4949
*
50-
* \param name The name of the property for which an accessor is added.
51-
* \param getter The callback to invoke when getting the property.
52-
* \param setter The callback to invoke when setting the property.
53-
* \param data A piece of data that will be passed to the getter and setter
54-
* callbacks whenever they are invoked.
55-
* \param settings Access control settings for the accessor. This is a bit
56-
* field consisting of one of more of
57-
* DEFAULT = 0, ALL_CAN_READ = 1, or ALL_CAN_WRITE = 2.
58-
* The default is to not allow cross-context access.
59-
* ALL_CAN_READ means that all cross-context reads are allowed.
60-
* ALL_CAN_WRITE means that all cross-context writes are allowed.
61-
* The combination ALL_CAN_READ | ALL_CAN_WRITE can be used to allow all
62-
* cross-context access.
63-
* \param attribute The attributes of the property for which an accessor
64-
* is added.
65-
* \param signature The signature describes valid receivers for the accessor
66-
* and is used to perform implicit instance checks against them. If the
67-
* receiver is incompatible (i.e. is not an instance of the constructor as
68-
* defined by FunctionTemplate::HasInstance()), an implicit TypeError is
69-
* thrown and no callback is invoked.
70-
*
71-
* @param NameValue $name
72-
* @param callable $getter
73-
* @param callable $setter
74-
* @param int $settings \v8\AccessControl constants (one or many)
75-
* @param int $attribute \v8\PropertyAttribute constants (one or many)
76-
*
77-
* TODO: add signature support
50+
* @param NameValue $name
51+
* @param NameValue $name The name of the property for which an accessor is added.
52+
*
53+
* @param callable $getter The callback to invoke when getting the property.
54+
* Callback signature should be (NameValue $property, PropertyCallbackInfo $info)
55+
*
56+
* @param callable $setter The callback to invoke when setting the property.
57+
* Callback signature should be (NameValue $property, PropertyCallbackInfo $info)
58+
*
59+
* @param int $settings Access control settings for the accessor.
60+
*
61+
* @param int $attributes The attributes of the property for which an accessor is added.
62+
*
63+
* @param FunctionTemplate $receiver The signature describes valid receivers for the accessor
64+
* and is used to perform implicit instance checks against them. If the
65+
* receiver is incompatible (i.e. is not an instance of the constructor as
66+
* defined by FunctionTemplate::HasInstance()), an implicit TypeError is
67+
* thrown and no callback is invoked.
7868
*/
69+
7970
public function SetAccessor(
8071
NameValue $name,
8172
callable $getter,
8273
callable $setter,
8374
$settings = AccessControl::DEFAULT_ACCESS,
84-
$attribute = PropertyAttribute::None
75+
$attributes = PropertyAttribute::None,
76+
FunctionTemplate $receiver
8577
) {
8678
}
8779

stubs/src/Template.php

Lines changed: 18 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -78,49 +78,30 @@ public function SetAccessorProperty(
7878
* are called instead of getting and setting the property directly
7979
* on the JavaScript object.
8080
*
81-
* \param name The name of the property for which an accessor is added.
82-
* \param getter The callback to invoke when getting the property.
83-
* \param setter The callback to invoke when setting the property.
84-
* \param data A piece of data that will be passed to the getter and setter
85-
* callbacks whenever they are invoked.
86-
* \param settings Access control settings for the accessor. This is a bit
87-
* field consisting of one of more of
88-
* DEFAULT = 0, ALL_CAN_READ = 1, or ALL_CAN_WRITE = 2.
89-
* The default is to not allow cross-context access.
90-
* ALL_CAN_READ means that all cross-context reads are allowed.
91-
* ALL_CAN_WRITE means that all cross-context writes are allowed.
92-
* The combination ALL_CAN_READ | ALL_CAN_WRITE can be used to allow all
93-
* cross-context access.
94-
* \param attribute The attributes of the property for which an accessor
95-
* is added.
96-
* \param signature The signature describes valid receivers for the accessor
97-
* and is used to perform implicit instance checks against them. If the
98-
* receiver is incompatible (i.e. is not an instance of the constructor as
99-
* defined by FunctionTemplate::HasInstance()), an implicit TypeError is
100-
* thrown and no callback is invoked.
101-
*/
102-
//public function void SetNativeDataProperty(Local<String> name,
103-
// AccessorGetterCallback getter,
104-
// AccessorSetterCallback setter = 0,
105-
// // TODO(dcarney): gcc can't handle Local below
106-
// Handle<Value> data = Handle<Value>(),
107-
// PropertyAttribute attribute = None,
108-
// Local<AccessorSignature> signature =
109-
// Local<AccessorSignature>(),
110-
// AccessControl settings = DEFAULT);
111-
112-
/**
113-
* @param NameValue $name
114-
* @param callable $getter Callable that will accept (string $property, PropertyCallbackInfo $info)
115-
* @param callable $setter Callable that will accept (string $property, PropertyCallbackInfo $info)
116-
* @param int $attribute
117-
* @param int $settings
81+
* @param NameValue $name The name of the property for which an accessor is added.
82+
*
83+
* @param callable $getter The callback to invoke when getting the property.
84+
*
85+
* Callback signature should be (NameValue $property, PropertyCallbackInfo $info)
86+
* @param callable $setter The callback to invoke when setting the property.
87+
* Callback signature should be (NameValue $property, PropertyCallbackInfo $info)
88+
*
89+
* @param int $attribute The attributes of the property for which an accessor is added.
90+
*
91+
* @param FunctionTemplate $receiver The signature describes valid receivers for the accessor
92+
* and is used to perform implicit instance checks against them. If the
93+
* receiver is incompatible (i.e. is not an instance of the constructor as
94+
* defined by FunctionTemplate::HasInstance()), an implicit TypeError is
95+
* thrown and no callback is invoked.
96+
*
97+
* @param int $settings Access control settings for the accessor.
11898
*/
11999
public function SetNativeDataProperty(
120100
NameValue $name,
121101
callable $getter,
122102
callable $setter = null,
123103
$attribute = PropertyAttribute::None,
104+
FunctionTemplate $receiver,
124105
$settings = AccessControl::DEFAULT_ACCESS
125106
) {
126107
}

tests/V8FunctionTemplate_constructor_receiver.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
V8\FunctionTemplate - constructor receiver
2+
V8\FunctionTemplate::__construct() - with receiver
33
--SKIPIF--
44
<?php if (!extension_loaded("v8")) {
55
print "skip";
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
--TEST--
2+
V8\ObjectTemplate::SetAccessor() - with receiver
3+
--SKIPIF--
4+
<?php if (!extension_loaded("v8")) {
5+
print "skip";
6+
} ?>
7+
--FILE--
8+
<?php
9+
/** @var \Phpv8Testsuite $helper */
10+
$helper = require '.testsuite.php';
11+
require '.v8-helpers.php';
12+
$v8_helper = new PhpV8Helpers($helper);
13+
14+
$isolate = new \V8\Isolate();
15+
$context = new \V8\Context($isolate);
16+
$v8_helper->injectConsoleLog($context);
17+
18+
$getter_checks = 0;
19+
20+
$getter = function (\V8\NameValue $prop, \V8\PropertyCallbackInfo $info) use (&$getter_checks) {
21+
echo 'Getter callback', PHP_EOL;
22+
$getter_checks++;
23+
};
24+
25+
$setter_checks = 0;
26+
27+
$setter = function (\V8\NameValue $prop, \V8\Value $value, \V8\PropertyCallbackInfo $info) use (&$setter_checks) {
28+
echo 'Setter callback', PHP_EOL;
29+
$setter_checks++;
30+
};
31+
32+
33+
$templ = new \V8\FunctionTemplate($isolate);
34+
35+
$inst = $templ->InstanceTemplate();
36+
$inst->SetAccessor(new \V8\StringValue($isolate, 'foo'), $getter, $setter, \V8\AccessControl::DEFAULT_ACCESS, \V8\PropertyAttribute::None, $templ);
37+
38+
$context->GlobalObject()->Set($context, new \V8\StringValue($isolate, 'f'), $templ->GetFunction($context));
39+
40+
$helper->header('Testing positive');
41+
$obj = $v8_helper->CompileRun($context, "var obj = new f(); obj");
42+
$v8_helper->CHECK($templ->HasInstance($obj), '$obj instance of $templ');
43+
44+
// Test path through generic runtime code.
45+
$v8_helper->CompileRun($context, "obj.foo");
46+
$v8_helper->CompileRun($context, "obj.foo = 23");
47+
48+
$helper->line();
49+
50+
$helper->header('Testing negative');
51+
52+
$obj = $v8_helper->CompileRun($context, "var obj = {}; obj.__proto__ = new f(); obj");
53+
$v8_helper->CHECK(!$templ->HasInstance($obj), '$obj is not an instance of $templ');
54+
55+
// Test path through generic runtime code.
56+
try {
57+
$v8_helper->CompileRun($context, "obj.foo");
58+
$helper->fail();
59+
} catch (\V8\Exceptions\TryCatchException $e) {
60+
$helper->exception_export($e);
61+
}
62+
63+
try {
64+
$v8_helper->CompileRun($context, "obj.foo = 23");
65+
$helper->fail();
66+
} catch (\V8\Exceptions\TryCatchException $e) {
67+
$helper->exception_export($e);
68+
}
69+
70+
$helper->line();
71+
echo 'Done here for now', PHP_EOL;
72+
?>
73+
--EXPECT--
74+
Testing positive:
75+
-----------------
76+
CHECK $obj instance of $templ: OK
77+
Getter callback
78+
Setter callback
79+
80+
Testing negative:
81+
-----------------
82+
CHECK $obj is not an instance of $templ: OK
83+
V8\Exceptions\TryCatchException: TypeError: Method foo called on incompatible receiver [object Object]
84+
V8\Exceptions\TryCatchException: TypeError: Method foo called on incompatible receiver [object Object]
85+
86+
Done here for now

0 commit comments

Comments
 (0)