diff --git a/.gitignore b/.gitignore index 7fe5eb2..4dcc784 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,9 @@ modules v8js.la /run-tests.php .*.sw[poq] + +/tests/*.diff +/tests/*.exp +/tests/*.out +/tests/*.php +/tests/*.sh diff --git a/README.Win32.md b/README.Win32.md new file mode 100644 index 0000000..4df1f8c --- /dev/null +++ b/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). diff --git a/README.md b/README.md index fec26fc..55bf9b1 100644 --- a/README.md +++ b/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 ----------------- diff --git a/config.w32 b/config.w32 index f5bb80b..50f8257 100644 --- a/config.w32 +++ b/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"); diff --git a/php_v8js_macros.h b/php_v8js_macros.h index 04a7f51..87f15ef 100644 --- a/php_v8js_macros.h +++ b/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 #include @@ -36,6 +42,14 @@ extern "C" { #include #include +#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" diff --git a/tests/commonjs_modules.phpt b/tests/commonjs_modules.phpt index dabab66..11c7ebb 100644 --- a/tests/commonjs_modules.phpt +++ b/tests/commonjs_modules.phpt @@ -1,7 +1,12 @@ --TEST-- Test V8Js::setModuleLoader : CommonJS modules --SKIPIF-- - + --FILE-- 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=== \ No newline at end of file diff --git a/tests/fatal_error_no_uninstall_inner_frame.phpt b/tests/fatal_error_no_uninstall_inner_frame.phpt index a7ea4ac..6c9aafc 100644 --- a/tests/fatal_error_no_uninstall_inner_frame.phpt +++ b/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 diff --git a/tests/fatal_error_rethrow.phpt b/tests/fatal_error_rethrow.phpt index a25a1be..7943cde 100644 --- a/tests/fatal_error_rethrow.phpt +++ b/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 diff --git a/tests/fatal_error_uninstall_in_first_frame.phpt b/tests/fatal_error_uninstall_in_first_frame.phpt index 6bce47b..518f3df 100644 --- a/tests/fatal_error_uninstall_in_first_frame.phpt +++ b/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 diff --git a/tests/memory_limit.phpt b/tests/memory_limit.phpt index 271a6a2..4f08dae 100644 --- a/tests/memory_limit.phpt +++ b/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; diff --git a/tests/object_dom.phpt b/tests/object_dom.phpt index 3a04f1b..958016e 100644 --- a/tests/object_dom.phpt +++ b/tests/object_dom.phpt @@ -1,7 +1,12 @@ --TEST-- Test V8::executeString() : DOM object passed from PHP --SKIPIF-- - + --FILE-- + --FILE-- -#if PHP_V8_API_VERSION >= 3029036 +#if !defined(_WIN32) && PHP_V8_API_VERSION >= 3029036 #include #endif @@ -478,10 +478,11 @@ static int php_v8js_v8_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS) / cb = v8::Local::Cast(v8obj->Get(method_name)); } - v8::Local jsArgv[argc]; + v8::Local *jsArgv = static_cast *>(alloca(sizeof(v8::Local) * argc)); v8::Local js_retval; for (i = 0; i < argc; i++) { + new(&jsArgv[i]) v8::Local; jsArgv[i] = v8::Local::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::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 } } diff --git a/v8js_convert.cc b/v8js_convert.cc index 5eec007..76e5944 100644 --- a/v8js_convert.cc +++ b/v8js_convert.cc @@ -471,10 +471,11 @@ static void php_v8js_named_property_enumerator(const v8::PropertyCallbackInfo(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::Local self = info.Holder(); v8::Local cb = v8::Local::Cast(info.Data()); int argc = info.Length(), i; - v8::Local argv[argc]; + v8::Local *argv = static_cast *>(alloca(sizeof(v8::Local) * argc)); v8::Local result; V8JS_TSRMLS_FETCH(); for (i=0; i; argv[i] = info[i]; } @@ -595,8 +597,9 @@ static void php_v8js_fake_call_impl(const v8::FunctionCallbackInfo& i // use php_v8js_php_callback to actually execute the method v8::Local cb = PHP_V8JS_CALLBACK(isolate, method_ptr, tmpl); uint32_t i, argc = args->Length(); - v8::Local argv[argc]; + v8::Local *argv = static_cast *>(alloca(sizeof(v8::Local) * argc)); for (i=0; i; argv[i] = args->Get(i); } return_value = cb->Call(info.This(), (int) argc, argv); @@ -1093,6 +1096,10 @@ v8::Handle 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::min() || v > std::numeric_limits::max()) { jsValue = V8JS_FLOAT((double)v); } else { @@ -1142,12 +1149,32 @@ int v8js_to_zval(v8::Handle 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 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())