Quellcode durchsuchen

Merge pull request #201 from stesie/generator-php-to-v8

Generator object passing from PHP to JS
Stefan Siegl vor 9 Jahren
Ursprung
Commit
b4b6a8a1a3

+ 8 - 0
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

+ 1 - 2
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");
 	}
 
 }
-

+ 1 - 0
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

+ 38 - 0
tests/generators_to_v8_001.phpt

@@ -0,0 +1,38 @@
+--TEST--
+Test V8::executeString() : Generators PHP -> V8 (instantite in JS)
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . '/skipif.inc');
+
+// Actually this check is a bit bad as it tests import, but currently
+// there is no flag we can check for export
+if (!class_exists('V8Generator')) {
+    die("skip Installed V8 version doesn't support generators");
+}
+?>
+--FILE--
+<?php
+
+$js = <<<EOJS
+for(var i of PHP.Gen()) {
+    var_dump(i);
+}
+EOJS;
+
+$v8 = new V8Js();
+$v8->Gen = function() {
+    for($i = 0; $i < 4; $i ++) {
+        yield $i;
+    }
+};
+
+$v8->executeString($js);
+
+?>
+===EOF===
+--EXPECTF--
+int(0)
+int(1)
+int(2)
+int(3)
+===EOF===

+ 36 - 0
tests/generators_to_v8_002.phpt

@@ -0,0 +1,36 @@
+--TEST--
+Test V8::executeString() : Generators PHP -> V8 (instantiate in JS, iterate in PHP)
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . '/skipif.inc');
+
+// Actually this check is a bit bad as it tests import, but currently
+// there is no flag we can check for export
+if (!class_exists('V8Generator')) {
+    die("skip Installed V8 version doesn't support generators");
+}
+?>
+--FILE--
+<?php
+
+$v8 = new V8Js();
+$v8->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===

+ 55 - 0
tests/generators_to_v8_003.phpt

@@ -0,0 +1,55 @@
+--TEST--
+Test V8::executeString() : Generators PHP -> V8 (instantiate in JS, iterate in PHP)
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . '/skipif.inc');
+
+// Actually this check is a bit bad as it tests import, but currently
+// there is no flag we can check for export
+if (!class_exists('V8Generator')) {
+    die("skip Installed V8 version doesn't support generators");
+}
+?>
+--FILE--
+<?php
+
+$v8 = new V8Js();
+$v8->Gen = function() {
+    for($i = 0; $i < 6; $i ++) {
+        yield $i;
+    }
+};
+
+$JS = <<<EOJS
+var g = PHP.Gen();
+var_dump(g.next());
+var_dump(g.next());
+(g);
+EOJS;
+
+$gen = $v8->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===

+ 56 - 0
tests/generators_to_v8_004.phpt

@@ -0,0 +1,56 @@
+--TEST--
+Test V8::executeString() : Generators PHP -> V8 (yield from)
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . '/skipif.inc');
+
+// Actually this check is a bit bad as it tests import, but currently
+// there is no flag we can check for export
+if (!class_exists('V8Generator')) {
+    die("skip Installed V8 version doesn't support generators");
+}
+?>
+--FILE--
+<?php
+
+function FibonacciGenerator()
+{
+    $i = 0;
+    $j = 1;
+    for(;;) {
+        yield $j;
+        list($i, $j) = array($j, $i + $j);
+    }
+}
+
+$v8 = new V8Js();
+$v8->fibs = FibonacciGenerator();
+
+$JS = <<<EOJS
+function* prefixer() {
+    yield* arguments;
+    yield* PHP.fibs;
+}
+
+var gen = prefixer(23, 42);
+
+for(var i = 0; i < 10; i ++) {
+    var_dump(gen.next().value);
+}
+EOJS;
+
+$v8->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===

+ 49 - 0
tests/generators_to_v8_basic.phpt

@@ -0,0 +1,49 @@
+--TEST--
+Test V8::executeString() : Generators PHP -> V8
+--SKIPIF--
+<?php
+require_once(dirname(__FILE__) . '/skipif.inc');
+
+// Actually this check is a bit bad as it tests import, but currently
+// there is no flag we can check for export
+if (!class_exists('V8Generator')) {
+    die("skip Installed V8 version doesn't support generators");
+}
+?>
+--FILE--
+<?php
+
+function TheGenerator()
+{
+    for($i = 0; $i < 4; $i ++) {
+	yield $i;
+    }
+}
+
+foreach(TheGenerator() as $i) {
+    var_dump($i);
+}
+
+
+$js = <<<EOJS
+for(var i of PHP.gen) {
+  var_dump(i);
+}
+EOJS;
+
+$v8 = new V8Js();
+$v8->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===

+ 76 - 0
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 <[email protected]>                                |
+  +----------------------------------------------------------------------+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include "php_v8js_macros.h"
+
+#ifdef V8JS_GENERATOR_EXPORT_SUPPORT
+
+v8::Local<v8::Value> v8js_wrap_generator(v8::Isolate *isolate, v8::Local<v8::Value> wrapped_object) /* {{{ */
+{
+	v8::Local<v8::Value> result;
+
+	assert(!wrapped_object.IsEmpty());
+	assert(wrapped_object->IsObject());
+
+	v8::TryCatch try_catch;
+	v8::Local<v8::String> 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<v8::Script> script = v8::Script::Compile(source);
+
+	if(script.IsEmpty()) {
+		zend_error(E_ERROR, "Failed to compile Generator object wrapper");
+		return result;
+	}
+
+	v8::Local<v8::Value> 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<v8::Function> wrapper_fn = v8::Local<v8::Function>::Cast(wrapper_fn_val);
+	v8::Local<v8::Value> *jsArgv = static_cast<v8::Local<v8::Value> *>(alloca(sizeof(v8::Local<v8::Value>)));
+
+	new(&jsArgv[0]) v8::Local<v8::Value>;
+	jsArgv[0] = v8::Local<v8::Value>::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
+ */

+ 32 - 0
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 <[email protected]>                                |
+  +----------------------------------------------------------------------+
+*/
+
+#ifndef V8JS_GENERATOR_EXPORT_H
+#define V8JS_GENERATOR_EXPORT_H
+
+#ifdef V8JS_GENERATOR_EXPORT_SUPPORT
+
+v8::Local<v8::Value> v8js_wrap_generator(v8::Isolate *isolate, v8::Local<v8::Value> 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
+ */

+ 13 - 1
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<v8::Object, zend_object> &data);
@@ -955,7 +957,17 @@ v8::Handle<v8::Value> 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<v8::Value> 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