diff --git a/config.m4 b/config.m4 index 1e3c488..5d9e4eb 100644 --- a/config.m4 +++ b/config.m4 @@ -180,6 +180,7 @@ int main () v8js_commonjs.cc \ v8js_convert.cc \ v8js_exceptions.cc \ + v8js_generator_export.cc \ v8js_methods.cc \ v8js_object_export.cc \ v8js_timer.cc \ @@ -190,3 +191,10 @@ int main () PHP_ADD_MAKEFILE_FRAGMENT fi + +dnl Local variables: +dnl tab-width: 4 +dnl c-basic-offset: 4 +dnl End: +dnl vim600: noet sw=4 ts=4 fdm=marker +dnl vim<600: noet sw=4 ts=4 diff --git a/config.w32 b/config.w32 index 92b90ee..8ac5694 100644 --- a/config.w32 +++ b/config.w32 @@ -17,11 +17,10 @@ if (PHP_V8JS != "no") { AC_DEFINE("PHP_V8_API_VERSION", "4007075", "", false); AC_DEFINE("PHP_V8_VERSION", "4.7.75", "", true); - EXTENSION("v8js", "v8js_array_access.cc v8js.cc v8js_class.cc v8js_commonjs.cc v8js_convert.cc v8js_exceptions.cc v8js_methods.cc v8js_object_export.cc v8js_timer.cc v8js_v8.cc v8js_v8object_class.cc v8js_variables.cc", "yes"); + EXTENSION("v8js", "v8js_array_access.cc v8js.cc v8js_class.cc v8js_commonjs.cc v8js_convert.cc v8js_exceptions.cc v8js_generator_export.cc v8js_methods.cc v8js_object_export.cc v8js_timer.cc v8js_v8.cc v8js_v8object_class.cc v8js_variables.cc", "yes"); } else { WARNING("v8js not enabled, headers or libs not found"); } } - diff --git a/php_v8js_macros.h b/php_v8js_macros.h index ebc9a55..acffbc1 100644 --- a/php_v8js_macros.h +++ b/php_v8js_macros.h @@ -63,6 +63,7 @@ extern "C" { #if PHP_V8_API_VERSION >= 3030000 #define V8JS_V8GENERATOR_SUPPORT 1 +#define V8JS_GENERATOR_EXPORT_SUPPORT 1 #endif /* method signatures of zend_update_property and zend_read_property were diff --git a/tests/generators_to_v8_001.phpt b/tests/generators_to_v8_001.phpt new file mode 100644 index 0000000..7e53614 --- /dev/null +++ b/tests/generators_to_v8_001.phpt @@ -0,0 +1,38 @@ +--TEST-- +Test V8::executeString() : Generators PHP -> V8 (instantite in JS) +--SKIPIF-- + +--FILE-- +Gen = function() { + for($i = 0; $i < 4; $i ++) { + yield $i; + } +}; + +$v8->executeString($js); + +?> +===EOF=== +--EXPECTF-- +int(0) +int(1) +int(2) +int(3) +===EOF=== diff --git a/tests/generators_to_v8_002.phpt b/tests/generators_to_v8_002.phpt new file mode 100644 index 0000000..2881225 --- /dev/null +++ b/tests/generators_to_v8_002.phpt @@ -0,0 +1,36 @@ +--TEST-- +Test V8::executeString() : Generators PHP -> V8 (instantiate in JS, iterate in PHP) +--SKIPIF-- + +--FILE-- +Gen = function() { + for($i = 0; $i < 4; $i ++) { + yield $i; + } +}; + +$gen = $v8->executeString('(PHP.Gen())'); + +foreach($gen as $i) { + var_dump($i); +} + +?> +===EOF=== +--EXPECTF-- +int(0) +int(1) +int(2) +int(3) +===EOF=== diff --git a/tests/generators_to_v8_003.phpt b/tests/generators_to_v8_003.phpt new file mode 100644 index 0000000..269b45a --- /dev/null +++ b/tests/generators_to_v8_003.phpt @@ -0,0 +1,55 @@ +--TEST-- +Test V8::executeString() : Generators PHP -> V8 (instantiate in JS, iterate in PHP) +--SKIPIF-- + +--FILE-- +Gen = function() { + for($i = 0; $i < 6; $i ++) { + yield $i; + } +}; + +$JS = <<executeString($JS); + +foreach($gen as $i) { + var_dump($i); +} + +?> +===EOF=== +--EXPECTF-- +object(Object)#%d (2) { + ["value"] => + int(0) + ["done"] => + bool(false) +} +object(Object)#%d (2) { + ["value"] => + int(1) + ["done"] => + bool(false) +} +int(2) +int(3) +int(4) +int(5) +===EOF=== diff --git a/tests/generators_to_v8_004.phpt b/tests/generators_to_v8_004.phpt new file mode 100644 index 0000000..ea2e6d8 --- /dev/null +++ b/tests/generators_to_v8_004.phpt @@ -0,0 +1,56 @@ +--TEST-- +Test V8::executeString() : Generators PHP -> V8 (yield from) +--SKIPIF-- + +--FILE-- +fibs = FibonacciGenerator(); + +$JS = <<executeString($JS); +?> +===EOF=== +--EXPECT-- +int(23) +int(42) +int(1) +int(1) +int(2) +int(3) +int(5) +int(8) +int(13) +int(21) +===EOF=== diff --git a/tests/generators_to_v8_basic.phpt b/tests/generators_to_v8_basic.phpt new file mode 100644 index 0000000..7382755 --- /dev/null +++ b/tests/generators_to_v8_basic.phpt @@ -0,0 +1,49 @@ +--TEST-- +Test V8::executeString() : Generators PHP -> V8 +--SKIPIF-- + +--FILE-- +gen = TheGenerator(); +$gen = $v8->executeString($js); + +?> +===EOF=== +--EXPECTF-- +int(0) +int(1) +int(2) +int(3) +int(0) +int(1) +int(2) +int(3) +===EOF=== diff --git a/v8js_generator_export.cc b/v8js_generator_export.cc new file mode 100644 index 0000000..1623fa4 --- /dev/null +++ b/v8js_generator_export.cc @@ -0,0 +1,76 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 2016 The PHP Group | + +----------------------------------------------------------------------+ + | http://www.opensource.org/licenses/mit-license.php MIT License | + +----------------------------------------------------------------------+ + | Author: Stefan Siegl | + +----------------------------------------------------------------------+ +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "php_v8js_macros.h" + +#ifdef V8JS_GENERATOR_EXPORT_SUPPORT + +v8::Local v8js_wrap_generator(v8::Isolate *isolate, v8::Local wrapped_object) /* {{{ */ +{ + v8::Local result; + + assert(!wrapped_object.IsEmpty()); + assert(wrapped_object->IsObject()); + + v8::TryCatch try_catch; + v8::Local source = v8::String::NewFromUtf8(isolate, "(\ +function(wrapped_object) { \ + return (function*() { \ + for(;;) { \ + if(!wrapped_object.valid()) { \ + return; \ + } \ + yield wrapped_object.current(); \ + wrapped_object.next(); \ + } \ + })(); \ +})"); + v8::Local script = v8::Script::Compile(source); + + if(script.IsEmpty()) { + zend_error(E_ERROR, "Failed to compile Generator object wrapper"); + return result; + } + + v8::Local wrapper_fn_val = script->Run(); + + if(wrapper_fn_val.IsEmpty() || !wrapper_fn_val->IsFunction()) { + zend_error(E_ERROR, "Failed to create Generator object wrapper function"); + return result; + } + + v8::Local wrapper_fn = v8::Local::Cast(wrapper_fn_val); + v8::Local *jsArgv = static_cast *>(alloca(sizeof(v8::Local))); + + new(&jsArgv[0]) v8::Local; + jsArgv[0] = v8::Local::New(isolate, wrapped_object); + + result = wrapper_fn->Call(V8JS_GLOBAL(isolate), 1, jsArgv); + return result; +} +/* }}} */ + +#endif /* V8JS_GENERATOR_EXPORT_SUPPORT */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/v8js_generator_export.h b/v8js_generator_export.h new file mode 100644 index 0000000..8c8becf --- /dev/null +++ b/v8js_generator_export.h @@ -0,0 +1,32 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 2016 The PHP Group | + +----------------------------------------------------------------------+ + | http://www.opensource.org/licenses/mit-license.php MIT License | + +----------------------------------------------------------------------+ + | Author: Stefan Siegl | + +----------------------------------------------------------------------+ +*/ + +#ifndef V8JS_GENERATOR_EXPORT_H +#define V8JS_GENERATOR_EXPORT_H + +#ifdef V8JS_GENERATOR_EXPORT_SUPPORT + +v8::Local v8js_wrap_generator(v8::Isolate *isolate, v8::Local wrapped_object); + +#endif /* V8JS_GENERATOR_EXPORT_SUPPORT */ + +#endif /* V8JS_GENERATOR_EXPORT_H */ + +/* + * Local variables: + * mode: c++ + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ diff --git a/v8js_object_export.cc b/v8js_object_export.cc index c34e82b..85aa33e 100644 --- a/v8js_object_export.cc +++ b/v8js_object_export.cc @@ -17,6 +17,7 @@ #include "php_v8js_macros.h" #include "v8js_array_access.h" +#include "v8js_generator_export.h" #include "v8js_object_export.h" #include "v8js_v8object_class.h" @@ -26,6 +27,7 @@ extern "C" { #include "zend_interfaces.h" #include "zend_closures.h" #include "zend_exceptions.h" +#include "zend_generators.h" } static void v8js_weak_object_callback(const v8::WeakCallbackData &data); @@ -955,7 +957,17 @@ v8::Handle v8js_hash_to_jsobj(zval *value, v8::Isolate *isolate TSRML /* If it's a PHP object, wrap it */ if (ce) { - return v8js_wrap_object(isolate, ce, value TSRMLS_CC); + v8::Local wrapped_object = v8js_wrap_object(isolate, ce, value TSRMLS_CC); + +#ifdef V8JS_GENERATOR_EXPORT_SUPPORT + if (ce == zend_ce_generator) { + /* Wrap PHP Generator object in a wrapper function that provides + * ES6 style behaviour. */ + wrapped_object = v8js_wrap_generator(isolate, wrapped_object); + } +#endif + + return wrapped_object; } /* Associative PHP arrays cannot be wrapped to JS arrays, convert them to