Explorar o código

Merge branch 'issue-123', closes #123, closes #24

Stefan Siegl %!s(int64=10) %!d(string=hai) anos
pai
achega
75b7b27587

+ 6 - 0
.gitignore

@@ -28,3 +28,9 @@ modules
 v8js.la
 /run-tests.php
 .*.sw[poq]
+
+/tests/*.diff
+/tests/*.exp
+/tests/*.out
+/tests/*.php
+/tests/*.sh

+ 125 - 0
README.Win32.md

@@ -0,0 +1,125 @@
+V8Js on Windows
+===============
+
+The V8Js PHP extension is primarily targeted at Unix platforms, especially
+GNU/Linux.  However it is possible (and supported) to build on Windows, using
+Microsoft Visual Studio.
+
+Building with MinGW or Cygwin is not officially supported; mainly since
+Google v8 does not support builds on Cygwin (and building it on Cygwin is
+currently broken).
+
+Compared to installation on GNU/Linux it's way more tedious to install V8Js
+on Windows, since you need to compile PHP with all its dependencies beforehand.
+
+The problem is that Google v8 requires (at least) Visual Studio 2013 as it
+uses certain C++11 features not available in Visual Studio 2012.
+
+Unfortunately the [PHP for Windows](http://windows.php.net/) project still
+relies on either Visual Studio 2012 or Visual Studio 2008.
+It supplies pre-compiled binary archives of required dependencies, however also
+compiled with Visual Studio 2008 and 2012 only.
+
+Since it is not compatible to link a v8 compiled with Visual Studio 2013 with
+a PHP interpreter compiled with Visual Studio 2012, you need to step up and
+compile PHP with Visual Studio 2013.  This requires to compile dependencies as
+well, if you would like to use certain extensions or e.g. the Apache SAPI.
+
+
+Compiling v8
+------------
+
+The Google v8 project already has excellent step-by-step guide on
+[how to build with gyp](https://code.google.com/p/v8-wiki/wiki/BuildingWithGYP).
+
+As a short run through:
+
+* Download and install Python (make sure it adds python.exe to PATH during install)
+  from http://www.python.org/download/
+* Install Git from https://github.com/msysgit/msysgit/releases/download/Git-1.9.4-preview20140929/Git-1.9.4-preview20140929.exe
+* Install Subversion from http://sourceforge.net/projects/win32svn/files/latest/download
+
+Then open a command prompt
+
+```
+cd \
+git clone https://github.com/v8/v8.git
+cd v8
+svn co http://gyp.googlecode.com/svn/trunk build/gyp
+svn co https://src.chromium.org/chrome/trunk/deps/third_party/icu46 third_party/icu
+svn co http://src.chromium.org/svn/trunk/tools/third_party/python_26@89111 third_party/python_26
+svn co http://src.chromium.org/svn/trunk/deps/third_party/cygwin@66844 third_party/cygwin
+svn co http://googletest.googlecode.com/svn/trunk testing/gtest --revision 643
+svn co http://googlemock.googlecode.com/svn/trunk testing/gmock --revision 410
+
+python build\gyp_v8 -Dcomponent=shared_library
+"\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\devenv.com" /build Release build/All.sln
+```
+
+(alternatively `start build/all.sln` ... and build within the Visual Studio GUI)
+
+
+
+Compiling PHP
+-------------
+
+There [Build your own PHP on Windows](https://wiki.php.net/internals/windows/stepbystepbuild)
+guide on the PHP wiki.  However it concentrates on Visual Studio 2008 and 2012.
+Since you need to use Visual Studio 2013 it doesn't apply very well.
+
+This document concentrates on building V8Js for CLI SAPI (only).  In order
+to enable more extensions you need to provide further dependencies, which may
+be more or less cumbersome to build with Visual Studio beforehand.
+
+A quick run through:
+
+* install 7Zip from http://downloads.sourceforge.net/sevenzip/7z920-x64.msi
+* create directory \php-sdk
+* Download http://windows.php.net/downloads/php-sdk/php-sdk-binary-tools-20110915.zip
+* ... and unpack to \php-sdk
+
+Open "VS2013 x86 Native Tools Command Prompt"
+
+```
+cd \php-sdk
+bin\phpsdk_setvars.bat
+bin\phpsdk_buildtree.bat phpdev
+
+mkdir vc12
+mkdir vc12\x86
+mkdir vc12\x86\deps
+mkdir vc12\x86\deps\bin
+mkdir vc12\x86\deps\include
+mkdir vc12\x86\deps\lib
+```
+
+* download PHP from http://php.net/get/php-5.5.18.tar.gz/from/a/mirror
+  and unpack to below `\php-sdk\phpdev\vc12\x86`
+* from `\v8\build\Release\lib` copy `icui18n.lib`, `icuuc.lib` and `v8.lib`
+  to deps\lib folder
+* from `\v8\include copy` all v8*.h files to deps\include folder
+
+(still in "VS2013 x86 Native Tools Command Prompt")
+
+```
+cd \php-sdk\phpdev\vc12\x86\php-5.5.18\
+
+buildconf
+configure --disable-all --enable-cli --with-v8js
+nmake
+```
+
+After nmake completes the php.exe is in Release_TS\ directory.
+
+In order to try things out in place, copy over `icui18n.dll`, `icuuc.dll` and
+`v8.dll` file from `\v8\build\Release` folder to
+`\php-sdk\phpdev\vc12\x86\php-5.5.18\Release_TS\` first.
+
+Then run
+
+```
+php.exe -d extension=php_v8js.dll -d extension_dir=\php-sdk\phpdev\vc12\x86\php-5.5.18\Release_TS\
+```
+
+Alternatively copy all stuff to c:\php\ (including the three DLL files from
+v8 build).

+ 3 - 0
README.md

@@ -33,6 +33,9 @@ repository](https://registry.hub.docker.com/u/stesie/v8js/).
 You also might want to try the Debian & Ubuntu packages available from
 the Jenkins site at https://jenkins.brokenpipe.de/
 
+Building on Microsoft Windows is more complicated, see README.Win32.md file
+for a quick run through.
+
 Compile latest v8
 -----------------
 

+ 3 - 1
config.w32

@@ -7,11 +7,13 @@ if (PHP_V8JS != "no") {
 
 		ADD_FLAG("CFLAGS_V8JS", "/EHcs");
 		ADD_FLAG("CFLAGS_V8JS", "/D _ALLOW_KEYWORD_MACROS");
+		ADD_FLAG("CFLAGS_V8JS", "/D ZEND_WIN32_FORCE_INLINE");
+		ADD_FLAG("CFLAGS_V8JS", "/D __STDC_LIMIT_MACROS");
 
 		AC_DEFINE("PHP_V8_API_VERSION", "3017015", "", false);
 		AC_DEFINE("PHP_V8_VERSION", "3.17.15", "", true);
 
-		EXTENSION("v8js", "v8js.cc v8js_convert.cc v8js_methods.cc v8js_variables.cc", "yes");
+		EXTENSION("v8js", "v8js.cc v8js_commonjs.cc v8js_convert.cc v8js_methods.cc v8js_variables.cc", "yes");
 
 	} else {
 		WARNING("v8js not enabled, headers or libs not found");

+ 14 - 0
php_v8js_macros.h

@@ -25,6 +25,12 @@ extern "C" {
 #include "php_v8js.h"
 }
 
+#ifdef _WIN32
+/* On Windows a symbol COMPILER is defined.  However v8.h has an enum with that
+ * name which hence would be broken unless undefined here. */
+#undef COMPILER
+#endif
+
 #include <v8.h>
 
 #include <chrono>
@@ -36,6 +42,14 @@ extern "C" {
 #include <vector>
 #include <mutex>
 
+#ifndef PATH_MAX
+/* Some platforms (Windows among others) don't have a PATH_MAX, for the moment
+ * just assume an arbitrary upper bound of 4096 chars.
+ * Anyways we should fix (get rid of) the code that uses PATH_MAX as it doesn't
+ * even check for buffer overflows.  FIXME */
+#define PATH_MAX 4096
+#endif
+
 /* V8Js Version */
 #define V8JS_VERSION "0.1.5"
 

+ 6 - 1
tests/commonjs_modules.phpt

@@ -1,7 +1,12 @@
 --TEST--
 Test V8Js::setModuleLoader : CommonJS modules
 --SKIPIF--
-<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+<?php
+if(!function_exists('json_encode')) {
+  die('SKIP');
+}
+require_once(dirname(__FILE__) . '/skipif.inc');
+?>
 --FILE--
 <?php
 

+ 4 - 4
tests/ctx_lifetime.phpt

@@ -46,10 +46,10 @@ object(V8Object)#%d (0) {
 }
 string(55) "Can't access V8Object after V8Js instance is destroyed!"
 
-Warning: Uncaught exception 'V8JsScriptException' with message 'Can't access V8Object after V8Js instance is destroyed!' in %s/tests/ctx_lifetime.php:35
+Warning: Uncaught exception 'V8JsScriptException' with message 'Can't access V8Object after V8Js instance is destroyed!' in %s%etests%ectx_lifetime.php:35
 Stack trace:
-#0 %s/tests/ctx_lifetime.php(35): unknown()
+#0 %s%etests%ectx_lifetime.php(35): unknown()
 #1 {main}
-  thrown in %s/tests/ctx_lifetime.php on line 35
+  thrown in %s%etests%ectx_lifetime.php on line 35
 
-Fatal error: Call to undefined method V8Object::hello() in %s/tests/ctx_lifetime.php on line 35
+Fatal error: Call to undefined method V8Object::hello() in %s%etests%ectx_lifetime.php on line 35

+ 2 - 2
tests/fatal_error_ignore_non_fatals.phpt

@@ -24,8 +24,8 @@ $js->executeString($script);
 ?>
 ===EOF===
 --EXPECTF--
-Notice: Undefined variable: bar in %s/fatal_error_ignore_non_fatals.php on line 6
+Notice: Undefined variable: bar in %s%efatal_error_ignore_non_fatals.php on line 6
 
-Warning: Foo Bar! in %s/fatal_error_ignore_non_fatals.php on line 7
+Warning: Foo Bar! in %s%efatal_error_ignore_non_fatals.php on line 7
 blar foo
 ===EOF===

+ 1 - 1
tests/fatal_error_no_uninstall_inner_frame.phpt

@@ -27,4 +27,4 @@ $js->executeString("PHP.foo();");
 --EXPECTF--
 nothing.
 
-Fatal error: Call to a member function foo() on %s in %s/fatal_error_no_uninstall_inner_frame.php on line 15
+Fatal error: Call to a member function foo() on %s in %s%efatal_error_no_uninstall_inner_frame.php on line 15

+ 1 - 1
tests/fatal_error_rethrow.phpt

@@ -23,4 +23,4 @@ $js->executeString($script);
 ?>
 ===EOF===
 --EXPECTF--
-Fatal error: Call to a member function bar() on %s in %s/fatal_error_rethrow.php on line 7
+Fatal error: Call to a member function bar() on %s in %s%efatal_error_rethrow.php on line 7

+ 1 - 1
tests/fatal_error_uninstall_in_first_frame.phpt

@@ -29,4 +29,4 @@ $bar->foo();
 --EXPECTF--
 nothing.
 
-Fatal error: Call to a member function foo() on %s in %s/fatal_error_uninstall_in_first_frame.php on line 20
+Fatal error: Call to a member function foo() on %s in %s%efatal_error_uninstall_in_first_frame.php on line 20

+ 5 - 2
tests/memory_limit.phpt

@@ -7,8 +7,11 @@ Test V8::executeString() : Time limit
 $JS = <<< EOT
 var text = "abcdefghijklmnopqrstuvwyxz0123456789";
 var memory = "";
-for (var i = 0; i < 1000000; ++i) {
-    memory += text;
+for (var i = 0; i < 100; ++i) {
+    for (var j = 0; j < 10000; ++j) {
+        memory += text;
+    }
+    sleep(0);
 }
 EOT;
 

+ 6 - 1
tests/object_dom.phpt

@@ -1,7 +1,12 @@
 --TEST--
 Test V8::executeString() : DOM object passed from PHP
 --SKIPIF--
-<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+<?php
+if(!class_exists('DomDocument')) {
+  die('skip');
+}
+require_once(dirname(__FILE__) . '/skipif.inc');
+?>
 --FILE--
 <?php
 

+ 6 - 1
tests/timezones.phpt

@@ -1,7 +1,12 @@
 --TEST--
 Test V8::executeString() : Check timezone handling
 --SKIPIF--
-<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
+<?php
+if(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
+	die('SKIP TZ not handled by v8 on Windows');
+}
+require_once(dirname(__FILE__) . '/skipif.inc');
+?>
 --FILE--
 <?php
 

+ 8 - 17
v8js.cc

@@ -21,7 +21,7 @@
 
 #include <v8-debug.h>
 
-#if PHP_V8_API_VERSION >= 3029036
+#if !defined(_WIN32) && PHP_V8_API_VERSION >= 3029036
 #include <libplatform/libplatform.h>
 #endif
 
@@ -478,10 +478,11 @@ static int php_v8js_v8_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS) /
 		cb = v8::Local<v8::Function>::Cast(v8obj->Get(method_name));
 	}
 
-	v8::Local<v8::Value> jsArgv[argc];
+	v8::Local<v8::Value> *jsArgv = static_cast<v8::Local<v8::Value> *>(alloca(sizeof(v8::Local<v8::Value>) * argc));
 	v8::Local<v8::Value> js_retval;
 
 	for (i = 0; i < argc; i++) {
+		new(&jsArgv[i]) v8::Local<v8::Value>;
 		jsArgv[i] = v8::Local<v8::Value>::New(isolate, zval_to_v8js(*argv[i], isolate TSRMLS_CC));
 	}
 
@@ -676,20 +677,6 @@ static void php_v8js_free_storage(void *object TSRMLS_DC) /* {{{ */
 	}
 	c->context.~Persistent();
 
-	/* Force garbage collection on our isolate, this is needed that V8 triggers
-	 * our MakeWeak callbacks.  Without these we won't remove our references
-	 * on the PHP objects leading to memory leaks in PHP context.
-	 */
-	{
-		v8::Locker locker(c->isolate);
-		v8::Isolate::Scope isolate_scope(c->isolate);
-#if PHP_V8_API_VERSION < 3028036
-		while(!v8::V8::IdleNotification()) {};
-#else
-		while(!c->isolate->IdleNotification(500)) {};
-#endif
-	}
-
 	/* Dispose yet undisposed weak refs */
 	for (std::map<zval *, v8js_persistent_obj_t>::iterator it = c->weak_objects.begin();
 		 it != c->weak_objects.end(); ++it) {
@@ -869,7 +856,7 @@ static void php_v8js_init(TSRMLS_D) /* {{{ */
 		return;
 	}
 
-#if PHP_V8_API_VERSION >= 3029036
+#if !defined(_WIN32) && PHP_V8_API_VERSION >= 3029036
 	v8::Platform* platform = v8::platform::CreateDefaultPlatform();
 	v8::V8::InitializePlatform(platform);
 #endif
@@ -1133,8 +1120,12 @@ static void php_v8js_timer_thread(TSRMLS_D)
 		}
 
 		// Sleep for 10ms
+#ifdef _WIN32
+		concurrency::wait(10);
+#else
 		std::chrono::milliseconds duration(10);
 		std::this_thread::sleep_for(duration);
+#endif
 	}
 }
 

+ 35 - 5
v8js_convert.cc

@@ -471,10 +471,11 @@ static void php_v8js_named_property_enumerator(const v8::PropertyCallbackInfo<v8
 			// prefix enumerated property names with '$' so they can be
 			// dereferenced unambiguously (ie, don't conflict with method
 			// names)
-			char prefixed[key_len + 1];
+			char *prefixed = static_cast<char *>(emalloc(key_len + 1));
 			prefixed[0] = '$';
 			strncpy(prefixed + 1, key, key_len);
 			result->Set(result_len++, V8JS_STRL(prefixed, key_len));
+			efree(prefixed);
 		} else {
 			// even numeric indices are enumerated as strings in JavaScript
 			result->Set(result_len++, V8JS_FLOAT((double) index)->ToString());
@@ -492,12 +493,13 @@ static void php_v8js_invoke_callback(const v8::FunctionCallbackInfo<v8::Value>&
 	v8::Local<v8::Object> self = info.Holder();
 	v8::Local<v8::Function> cb = v8::Local<v8::Function>::Cast(info.Data());
 	int argc = info.Length(), i;
-	v8::Local<v8::Value> argv[argc];
+	v8::Local<v8::Value> *argv = static_cast<v8::Local<v8::Value> *>(alloca(sizeof(v8::Local<v8::Value>) * argc));
 	v8::Local<v8::Value> result;
 
 	V8JS_TSRMLS_FETCH();
 
 	for (i=0; i<argc; i++) {
+		new(&argv[i]) v8::Local<v8::Value>;
 		argv[i] = info[i];
 	}
 
@@ -595,8 +597,9 @@ static void php_v8js_fake_call_impl(const v8::FunctionCallbackInfo<v8::Value>& i
 	// use php_v8js_php_callback to actually execute the method
 	v8::Local<v8::Function> cb = PHP_V8JS_CALLBACK(isolate, method_ptr, tmpl);
 	uint32_t i, argc = args->Length();
-	v8::Local<v8::Value> argv[argc];
+	v8::Local<v8::Value> *argv = static_cast<v8::Local<v8::Value> *>(alloca(sizeof(v8::Local<v8::Value>) * argc));
 	for (i=0; i<argc; i++) {
+		new(&argv[i]) v8::Local<v8::Value>;
 		argv[i] = args->Get(i);
 	}
 	return_value = cb->Call(info.This(), (int) argc, argv);
@@ -1093,6 +1096,10 @@ v8::Handle<v8::Value> zval_to_v8js(zval *value, v8::Isolate *isolate TSRMLS_DC)
 
 		case IS_LONG:
 		    v = Z_LVAL_P(value);
+			/* On Windows there are max and min macros, which would clobber the
+			 * method names of std::numeric_limits< > otherwise. */
+#undef max
+#undef min
 			if (v < - std::numeric_limits<int32_t>::min() || v > std::numeric_limits<int32_t>::max()) {
 				jsValue = V8JS_FLOAT((double)v);
 			} else {
@@ -1142,12 +1149,32 @@ int v8js_to_zval(v8::Handle<v8::Value> jsValue, zval *return_value, int flags, v
 	{
 		v8::String::Utf8Value str(jsValue);
 		const char *cstr = ToCString(str);
+
+		/* cstr has two timezone specifications:
+		 *
+		 * example from Linux:
+		 * Mon Sep 08 1975 09:00:00 GMT+0000 (UTC)
+		 *
+		 * example from Windows:
+		 * Mon Sep 08 1975 11:00:00 GMT+0200 (W. Europe Daylight Time)
+		 *
+		 * ... problem is, that PHP can't parse the second timezone
+		 * specification as returned by v8 running on Windows.  And as a
+		 * matter of that fails due to inconsistent second timezone spec
+		 */
+		char *date_str = estrdup(cstr);
+		char *paren_ptr = strchr(date_str, '(');
+
+		if (paren_ptr != NULL) {
+			*paren_ptr = 0;
+		}
+
 		zend_class_entry *ce = php_date_get_date_ce();
 #if PHP_VERSION_ID < 50304
 		zval *param;
 
 		MAKE_STD_ZVAL(param);
-		ZVAL_STRING(param, cstr, 1);
+		ZVAL_STRING(param, date_str, 0);
 
 		object_init_ex(return_value, ce TSRMLS_CC);
 		zend_call_method_with_1_params(&return_value, ce, &ce->constructor, "__construct", NULL, param);
@@ -1158,9 +1185,12 @@ int v8js_to_zval(v8::Handle<v8::Value> jsValue, zval *return_value, int flags, v
 		}
 #else
 		php_date_instantiate(ce, return_value TSRMLS_CC);
-		if (!php_date_initialize((php_date_obj *) zend_object_store_get_object(return_value TSRMLS_CC), (char *) cstr, strlen(cstr), NULL, NULL, 0 TSRMLS_CC)) {
+		if (!php_date_initialize((php_date_obj *) zend_object_store_get_object(return_value TSRMLS_CC), date_str, strlen(date_str), NULL, NULL, 0 TSRMLS_CC)) {
+			efree(date_str);
 			return FAILURE;
 		}
+
+		efree(date_str);
 #endif
 	}
 	else if (jsValue->IsObject())