Browse Source

Implement JS-side wrapper around PHP Generator objects

Stefan Siegl 9 years ago
parent
commit
a18ae9e853
6 changed files with 163 additions and 3 deletions
  1. 8 0
      config.m4
  2. 1 2
      config.w32
  3. 41 0
      tests/generators_to_v8_basic.phpt
  4. 73 0
      v8js_generator_export.cc
  5. 28 0
      v8js_generator_export.h
  6. 12 1
      v8js_object_export.cc

+ 8 - 0
config.m4

@@ -180,6 +180,7 @@ int main ()
     v8js_commonjs.cc		\
     v8js_commonjs.cc		\
     v8js_convert.cc			\
     v8js_convert.cc			\
     v8js_exceptions.cc		\
     v8js_exceptions.cc		\
+    v8js_generator_export.cc	\
     v8js_methods.cc			\
     v8js_methods.cc			\
     v8js_object_export.cc	\
     v8js_object_export.cc	\
 	v8js_timer.cc			\
 	v8js_timer.cc			\
@@ -190,3 +191,10 @@ int main ()
 
 
   PHP_ADD_MAKEFILE_FRAGMENT
   PHP_ADD_MAKEFILE_FRAGMENT
 fi
 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_API_VERSION", "4007075", "", false);
 		AC_DEFINE("PHP_V8_VERSION", "4.7.75", "", true);
 		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 {
 	} else {
 		WARNING("v8js not enabled, headers or libs not found");
 		WARNING("v8js not enabled, headers or libs not found");
 	}
 	}
 
 
 }
 }
-

+ 41 - 0
tests/generators_to_v8_basic.phpt

@@ -0,0 +1,41 @@
+--TEST--
+Test V8::executeString() : Generators PHP -> V8
+--SKIPIF--
+<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+--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===

+ 73 - 0
v8js_generator_export.cc

@@ -0,0 +1,73 @@
+/*
+  +----------------------------------------------------------------------+
+  | 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"
+
+v8::Local<v8::Value> v8js_wrap_generator(v8js_ctx *ctx, v8::Local<v8::Value> wrapped_object) /* {{{ */
+{
+	v8::Local<v8::Value> result;
+	V8JS_CTX_PROLOGUE_EX(ctx, result);
+
+	assert(!wrapped_object.IsEmpty());
+	assert(wrapped_object->IsObject());
+
+	v8::TryCatch try_catch;
+	v8::Local<v8::String> source = v8::String::NewFromUtf8(ctx->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;
+}
+/* }}} */
+
+/*
+ * 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
+ */

+ 28 - 0
v8js_generator_export.h

@@ -0,0 +1,28 @@
+/*
+  +----------------------------------------------------------------------+
+  | 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
+
+v8::Local<v8::Value> v8js_wrap_generator(v8js_ctx *ctx, v8::Local<v8::Value> wrapped_object);
+
+#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
+ */

+ 12 - 1
v8js_object_export.cc

@@ -17,6 +17,7 @@
 
 
 #include "php_v8js_macros.h"
 #include "php_v8js_macros.h"
 #include "v8js_array_access.h"
 #include "v8js_array_access.h"
+#include "v8js_generator_export.h"
 #include "v8js_object_export.h"
 #include "v8js_object_export.h"
 #include "v8js_v8object_class.h"
 #include "v8js_v8object_class.h"
 
 
@@ -26,6 +27,7 @@ extern "C" {
 #include "zend_interfaces.h"
 #include "zend_interfaces.h"
 #include "zend_closures.h"
 #include "zend_closures.h"
 #include "zend_exceptions.h"
 #include "zend_exceptions.h"
+#include "zend_generators.h"
 }
 }
 
 
 static void v8js_weak_object_callback(const v8::WeakCallbackData<v8::Object, zend_object> &data);
 static void v8js_weak_object_callback(const v8::WeakCallbackData<v8::Object, zend_object> &data);
@@ -955,7 +957,16 @@ v8::Handle<v8::Value> v8js_hash_to_jsobj(zval *value, v8::Isolate *isolate TSRML
 
 
 	/* If it's a PHP object, wrap it */
 	/* If it's a PHP object, wrap it */
 	if (ce) {
 	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);
+
+		if (ce == zend_ce_generator) {
+			/* Wrap PHP Generator object in a wrapper function that provides
+			 * ES6 style behaviour. */
+			v8js_ctx *ctx = (v8js_ctx *) isolate->GetData(0);
+			wrapped_object = v8js_wrap_generator(ctx, wrapped_object);
+		}
+
+		return wrapped_object;
 	}
 	}
 
 
 	/* Associative PHP arrays cannot be wrapped to JS arrays, convert them to
 	/* Associative PHP arrays cannot be wrapped to JS arrays, convert them to