diff --git a/tests/commonjs_caching_001.phpt b/tests/commonjs_caching_001.phpt new file mode 100644 index 0000000..6efec77 --- /dev/null +++ b/tests/commonjs_caching_001.phpt @@ -0,0 +1,26 @@ +--TEST-- +Test V8Js::setModuleLoader : Returned modules are cached +--SKIPIF-- + +--FILE-- +setModuleLoader(function($module) { + print("setModuleLoader called for ".$module."\n"); + return 'exports.bar = 23;'; +}); + +$v8->executeString($JS, 'module.js'); +?> +===EOF=== +--EXPECT-- +setModuleLoader called for test +setModuleLoader called for test2 +===EOF=== diff --git a/tests/commonjs_caching_002.phpt b/tests/commonjs_caching_002.phpt new file mode 100644 index 0000000..bdc34b2 --- /dev/null +++ b/tests/commonjs_caching_002.phpt @@ -0,0 +1,34 @@ +--TEST-- +Test V8Js::setModuleLoader : module cache seperated per isolate +--SKIPIF-- + +--FILE-- +setModuleLoader(function($module) { + print("setModuleLoader called for ".$module."\n"); + return 'exports.bar = 23;'; +}); + +$v8two = new V8Js(); +$v8two->setModuleLoader(function($module) { + print("setModuleLoader called for ".$module."\n"); + return 'exports.bar = 23;'; +}); + +$v8->executeString($JS, 'module.js'); +echo "--- v8two ---\n"; +$v8two->executeString($JS, 'module.js'); +?> +===EOF=== +--EXPECT-- +setModuleLoader called for test +--- v8two --- +setModuleLoader called for test +===EOF=== diff --git a/tests/commonjs_normalise_001.phpt b/tests/commonjs_normalise_001.phpt new file mode 100644 index 0000000..ada367f --- /dev/null +++ b/tests/commonjs_normalise_001.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test V8Js::setModuleLoader : Path normalisation #001 +--SKIPIF-- + +--FILE-- +setModuleLoader(function($module) { + print("setModuleLoader called for ".$module."\n"); + return 'exports.bar = 23;'; +}); + +$v8->executeString($JS, 'module.js'); +?> +===EOF=== +--EXPECT-- +setModuleLoader called for test +===EOF=== diff --git a/tests/commonjs_normalise_002.phpt b/tests/commonjs_normalise_002.phpt new file mode 100644 index 0000000..b93dff4 --- /dev/null +++ b/tests/commonjs_normalise_002.phpt @@ -0,0 +1,23 @@ +--TEST-- +Test V8Js::setModuleLoader : Path normalisation #002 +--SKIPIF-- + +--FILE-- +setModuleLoader(function($module) { + print("setModuleLoader called for ".$module."\n"); + return 'exports.bar = 23;'; +}); + +$v8->executeString($JS, 'module.js'); +?> +===EOF=== +--EXPECT-- +setModuleLoader called for test +===EOF=== diff --git a/tests/commonjs_normalise_003.phpt b/tests/commonjs_normalise_003.phpt new file mode 100644 index 0000000..7f752d4 --- /dev/null +++ b/tests/commonjs_normalise_003.phpt @@ -0,0 +1,27 @@ +--TEST-- +Test V8Js::setModuleLoader : Path normalisation #003 +--SKIPIF-- + +--FILE-- +setModuleLoader(function($module) { + print("setModuleLoader called for ".$module."\n"); + return 'exports.bar = 23;'; +}); + +$v8->executeString($JS, 'module.js'); +?> +===EOF=== +--EXPECT-- +setModuleLoader called for foo/test +setModuleLoader called for foo/bar/baz/test +setModuleLoader called for foo/bar/baz/blub +===EOF=== diff --git a/tests/commonjs_normalise_004.phpt b/tests/commonjs_normalise_004.phpt new file mode 100644 index 0000000..f8f2b8f --- /dev/null +++ b/tests/commonjs_normalise_004.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test V8Js::setModuleLoader : Path normalisation #004 +--SKIPIF-- + +--FILE-- +setModuleLoader(function($module) { + print("setModuleLoader called for ".$module."\n"); + + switch($module) { + case 'foo/test': + return 'require("./blar");'; + case 'foo/blar': + return 'exports.bar = 23;'; + } +}); + +$v8->executeString($JS, 'module.js'); +?> +===EOF=== +--EXPECT-- +setModuleLoader called for foo/test +setModuleLoader called for foo/blar +===EOF=== diff --git a/tests/commonjs_normalise_005.phpt b/tests/commonjs_normalise_005.phpt new file mode 100644 index 0000000..73be992 --- /dev/null +++ b/tests/commonjs_normalise_005.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test V8Js::setModuleLoader : Path normalisation #005 +--SKIPIF-- + +--FILE-- +setModuleLoader(function($module) { + print("setModuleLoader called for ".$module."\n"); + + switch($module) { + case 'foo/test': + return 'require("../blar");'; + case 'blar': + return 'exports.bar = 23;'; + } +}); + +$v8->executeString($JS, 'module.js'); +?> +===EOF=== +--EXPECT-- +setModuleLoader called for foo/test +setModuleLoader called for blar +===EOF=== diff --git a/v8js_class.cc b/v8js_class.cc index d93a56f..3c25442 100644 --- a/v8js_class.cc +++ b/v8js_class.cc @@ -165,6 +165,7 @@ static void v8js_free_storage(void *object TSRMLS_DC) /* {{{ */ /* Clear persistent handles in module cache */ for (std::map::iterator it = c->modules_loaded.begin(); it != c->modules_loaded.end(); ++it) { + efree(it->first); it->second.Reset(); } c->modules_loaded.~map(); @@ -209,7 +210,7 @@ static zend_object_value v8js_new(zend_class_entry *ce TSRMLS_DC) /* {{{ */ new(&c->modules_stack) std::vector(); new(&c->modules_base) std::vector(); - new(&c->modules_loaded) std::map; + new(&c->modules_loaded) std::map; new(&c->template_cache) std::map(); new(&c->accessor_list) std::vector(); diff --git a/v8js_class.h b/v8js_class.h index 7b36c7e..43268bb 100644 --- a/v8js_class.h +++ b/v8js_class.h @@ -24,6 +24,12 @@ struct v8js_v8object; struct v8js_accessor_ctx; struct _v8js_script; +struct cmp_str { + bool operator()(char const *a, char const *b) const { + return strcmp(a, b) < 0; + } +}; + /* {{{ Context container */ struct v8js_ctx { zend_object std; @@ -44,7 +50,7 @@ struct v8js_ctx { zval *module_loader; std::vector modules_stack; std::vector modules_base; - std::map modules_loaded; + std::map modules_loaded; std::map template_cache; std::map weak_objects; diff --git a/v8js_commonjs.cc b/v8js_commonjs.cc index b737051..fcd59a1 100644 --- a/v8js_commonjs.cc +++ b/v8js_commonjs.cc @@ -22,22 +22,18 @@ extern "C" { #include "php_v8js_macros.h" -void v8js_commonjs_split_terms(char *identifier, std::vector &terms) +static void v8js_commonjs_split_terms(const char *identifier, std::vector &terms) { - char *term = (char *)malloc(PATH_MAX), *ptr = term; - - // Initialise the term string - *term = 0; + char *term = (char *) emalloc(PATH_MAX), *ptr = term; while (*identifier > 0) { if (*identifier == '/') { - if (strlen(term) > 0) { + if (ptr > term) { // Terminate term string and add to terms vector *ptr++ = 0; - terms.push_back(strdup(term)); + terms.push_back(estrdup(term)); // Reset term string - memset(term, 0, strlen(term)); ptr = term; } } else { @@ -47,18 +43,16 @@ void v8js_commonjs_split_terms(char *identifier, std::vector &terms) identifier++; } - if (strlen(term) > 0) { + if (ptr > term) { // Terminate term string and add to terms vector *ptr++ = 0; - terms.push_back(strdup(term)); + terms.push_back(estrdup(term)); } - if (term > 0) { - free(term); - } + efree(term); } -void v8js_commonjs_normalise_identifier(char *base, char *identifier, char *normalised_path, char *module_name) +void v8js_commonjs_normalise_identifier(const char *base, const char *identifier, char *normalised_path, char *module_name) { std::vector id_terms, terms; v8js_commonjs_split_terms(identifier, id_terms); @@ -78,12 +72,19 @@ void v8js_commonjs_normalise_identifier(char *base, char *identifier, char *norm if (!strcmp(term, "..")) { // Ignore parent term (..) if it's the first normalised term if (normalised_terms.size() > 0) { - // Remove the parent normalized term + // Remove the parent normalized term (and free it) + efree(normalised_terms.back()); normalised_terms.pop_back(); } + + // free the ".." term + efree(term); } else if (strcmp(term, ".")) { // Add the term if it's not the current term (.) normalised_terms.push_back(term); + } else { + // Discard "." term + efree(term); } } @@ -92,6 +93,8 @@ void v8js_commonjs_normalise_identifier(char *base, char *identifier, char *norm *module_name = 0; strcat(module_name, normalised_terms.back()); + + efree(normalised_terms.back()); normalised_terms.pop_back(); for (std::vector::iterator it = normalised_terms.begin(); it != normalised_terms.end(); it++) { @@ -102,5 +105,6 @@ void v8js_commonjs_normalise_identifier(char *base, char *identifier, char *norm } strcat(normalised_path, term); + efree(term); } } diff --git a/v8js_commonjs.h b/v8js_commonjs.h new file mode 100644 index 0000000..1c05fb6 --- /dev/null +++ b/v8js_commonjs.h @@ -0,0 +1,18 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2015 The PHP Group | + +----------------------------------------------------------------------+ + | http://www.opensource.org/licenses/mit-license.php MIT License | + +----------------------------------------------------------------------+ + | Author: Stefan Siegl | + +----------------------------------------------------------------------+ +*/ + +#ifndef V8JS_COMMONJS_H +#define V8JS_COMMONJS_H + +void v8js_commonjs_normalise_identifier(const char *base, const char *identifier, char *normalised_path, char *module_name); + +#endif /* V8JS_COMMONJS_H */ diff --git a/v8js_methods.cc b/v8js_methods.cc index 9c59655..2bd9013 100644 --- a/v8js_methods.cc +++ b/v8js_methods.cc @@ -16,14 +16,12 @@ #endif #include "php_v8js_macros.h" +#include "v8js_commonjs.h" extern "C" { #include "zend_exceptions.h" } -/* Forward declarations */ -void v8js_commonjs_normalise_identifier(char *base, char *identifier, char *normalised_path, char *module_name); - /* global.exit - terminate execution */ V8JS_METHOD(exit) /* {{{ */ { @@ -223,13 +221,11 @@ V8JS_METHOD(require) v8::String::Utf8Value module_id_v8(info[0]); - // Make sure to duplicate the module name string so it doesn't get freed by the V8 garbage collector - char *module_id = estrdup(ToCString(module_id_v8)); + const char *module_id = ToCString(module_id_v8); char *normalised_path = (char *)emalloc(PATH_MAX); char *module_name = (char *)emalloc(PATH_MAX); v8js_commonjs_normalise_identifier(c->modules_base.back(), module_id, normalised_path, module_name); - efree(module_id); char *normalised_module_id = (char *)emalloc(strlen(normalised_path)+1+strlen(module_name)+1); *normalised_module_id = 0; @@ -256,12 +252,13 @@ V8JS_METHOD(require) // If we have already loaded and cached this module then use it if (c->modules_loaded.count(normalised_module_id) > 0) { - efree(normalised_module_id); - efree(normalised_path); - v8::Persistent newobj; newobj.Reset(isolate, c->modules_loaded[normalised_module_id]); info.GetReturnValue().Set(newobj); + + efree(normalised_module_id); + efree(normalised_path); + return; } @@ -400,7 +397,6 @@ V8JS_METHOD(require) c->modules_loaded[normalised_module_id].Reset(isolate, newobj); info.GetReturnValue().Set(newobj); - efree(normalised_module_id); } void v8js_register_methods(v8::Handle global, v8js_ctx *c) /* {{{ */