Skip to content

Commit 829bac9

Browse files
committed
Merge pull request #150 from stesie/issue-140
Fix module caching & memory leaks
2 parents 204cc0f + 6bbe4d9 commit 829bac9

12 files changed

+245
-27
lines changed

tests/commonjs_caching_001.phpt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
Test V8Js::setModuleLoader : Returned modules are cached
3+
--SKIPIF--
4+
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
8+
$JS = <<< EOT
9+
var foo = require("test");
10+
var bar = require("test2");
11+
var baz = require("test");
12+
EOT;
13+
14+
$v8 = new V8Js();
15+
$v8->setModuleLoader(function($module) {
16+
print("setModuleLoader called for ".$module."\n");
17+
return 'exports.bar = 23;';
18+
});
19+
20+
$v8->executeString($JS, 'module.js');
21+
?>
22+
===EOF===
23+
--EXPECT--
24+
setModuleLoader called for test
25+
setModuleLoader called for test2
26+
===EOF===

tests/commonjs_caching_002.phpt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
--TEST--
2+
Test V8Js::setModuleLoader : module cache seperated per isolate
3+
--SKIPIF--
4+
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
8+
$JS = <<< EOT
9+
var foo = require("test");
10+
var baz = require("test");
11+
EOT;
12+
13+
$v8 = new V8Js();
14+
$v8->setModuleLoader(function($module) {
15+
print("setModuleLoader called for ".$module."\n");
16+
return 'exports.bar = 23;';
17+
});
18+
19+
$v8two = new V8Js();
20+
$v8two->setModuleLoader(function($module) {
21+
print("setModuleLoader called for ".$module."\n");
22+
return 'exports.bar = 23;';
23+
});
24+
25+
$v8->executeString($JS, 'module.js');
26+
echo "--- v8two ---\n";
27+
$v8two->executeString($JS, 'module.js');
28+
?>
29+
===EOF===
30+
--EXPECT--
31+
setModuleLoader called for test
32+
--- v8two ---
33+
setModuleLoader called for test
34+
===EOF===

tests/commonjs_normalise_001.phpt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Test V8Js::setModuleLoader : Path normalisation #001
3+
--SKIPIF--
4+
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
8+
$JS = <<< EOT
9+
var foo = require("./test");
10+
EOT;
11+
12+
$v8 = new V8Js();
13+
$v8->setModuleLoader(function($module) {
14+
print("setModuleLoader called for ".$module."\n");
15+
return 'exports.bar = 23;';
16+
});
17+
18+
$v8->executeString($JS, 'module.js');
19+
?>
20+
===EOF===
21+
--EXPECT--
22+
setModuleLoader called for test
23+
===EOF===

tests/commonjs_normalise_002.phpt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Test V8Js::setModuleLoader : Path normalisation #002
3+
--SKIPIF--
4+
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
8+
$JS = <<< EOT
9+
var foo = require("../../../test");
10+
EOT;
11+
12+
$v8 = new V8Js();
13+
$v8->setModuleLoader(function($module) {
14+
print("setModuleLoader called for ".$module."\n");
15+
return 'exports.bar = 23;';
16+
});
17+
18+
$v8->executeString($JS, 'module.js');
19+
?>
20+
===EOF===
21+
--EXPECT--
22+
setModuleLoader called for test
23+
===EOF===

tests/commonjs_normalise_003.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
Test V8Js::setModuleLoader : Path normalisation #003
3+
--SKIPIF--
4+
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
8+
$JS = <<< EOT
9+
var foo = require("foo/test");
10+
var foo = require("foo/bar/baz/test");
11+
var foo = require("foo//bar//baz//blub");
12+
EOT;
13+
14+
$v8 = new V8Js();
15+
$v8->setModuleLoader(function($module) {
16+
print("setModuleLoader called for ".$module."\n");
17+
return 'exports.bar = 23;';
18+
});
19+
20+
$v8->executeString($JS, 'module.js');
21+
?>
22+
===EOF===
23+
--EXPECT--
24+
setModuleLoader called for foo/test
25+
setModuleLoader called for foo/bar/baz/test
26+
setModuleLoader called for foo/bar/baz/blub
27+
===EOF===

tests/commonjs_normalise_004.phpt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
Test V8Js::setModuleLoader : Path normalisation #004
3+
--SKIPIF--
4+
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
8+
$JS = <<< EOT
9+
var foo = require("foo/test");
10+
EOT;
11+
12+
$v8 = new V8Js();
13+
$v8->setModuleLoader(function($module) {
14+
print("setModuleLoader called for ".$module."\n");
15+
16+
switch($module) {
17+
case 'foo/test':
18+
return 'require("./blar");';
19+
case 'foo/blar':
20+
return 'exports.bar = 23;';
21+
}
22+
});
23+
24+
$v8->executeString($JS, 'module.js');
25+
?>
26+
===EOF===
27+
--EXPECT--
28+
setModuleLoader called for foo/test
29+
setModuleLoader called for foo/blar
30+
===EOF===

tests/commonjs_normalise_005.phpt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
Test V8Js::setModuleLoader : Path normalisation #005
3+
--SKIPIF--
4+
<?php require_once(dirname(__FILE__) . '/skipif.inc'); ?>
5+
--FILE--
6+
<?php
7+
8+
$JS = <<< EOT
9+
var foo = require("foo/test");
10+
EOT;
11+
12+
$v8 = new V8Js();
13+
$v8->setModuleLoader(function($module) {
14+
print("setModuleLoader called for ".$module."\n");
15+
16+
switch($module) {
17+
case 'foo/test':
18+
return 'require("../blar");';
19+
case 'blar':
20+
return 'exports.bar = 23;';
21+
}
22+
});
23+
24+
$v8->executeString($JS, 'module.js');
25+
?>
26+
===EOF===
27+
--EXPECT--
28+
setModuleLoader called for foo/test
29+
setModuleLoader called for blar
30+
===EOF===

v8js_class.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ static void v8js_free_storage(void *object TSRMLS_DC) /* {{{ */
165165
/* Clear persistent handles in module cache */
166166
for (std::map<char *, v8js_persistent_obj_t>::iterator it = c->modules_loaded.begin();
167167
it != c->modules_loaded.end(); ++it) {
168+
efree(it->first);
168169
it->second.Reset();
169170
}
170171
c->modules_loaded.~map();
@@ -209,7 +210,7 @@ static zend_object_value v8js_new(zend_class_entry *ce TSRMLS_DC) /* {{{ */
209210

210211
new(&c->modules_stack) std::vector<char*>();
211212
new(&c->modules_base) std::vector<char*>();
212-
new(&c->modules_loaded) std::map<char *, v8js_persistent_obj_t>;
213+
new(&c->modules_loaded) std::map<char *, v8js_persistent_obj_t, cmp_str>;
213214

214215
new(&c->template_cache) std::map<const char *,v8js_tmpl_t>();
215216
new(&c->accessor_list) std::vector<v8js_accessor_ctx *>();

v8js_class.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ struct v8js_v8object;
2424
struct v8js_accessor_ctx;
2525
struct _v8js_script;
2626

27+
struct cmp_str {
28+
bool operator()(char const *a, char const *b) const {
29+
return strcmp(a, b) < 0;
30+
}
31+
};
32+
2733
/* {{{ Context container */
2834
struct v8js_ctx {
2935
zend_object std;
@@ -44,7 +50,7 @@ struct v8js_ctx {
4450
zval *module_loader;
4551
std::vector<char *> modules_stack;
4652
std::vector<char *> modules_base;
47-
std::map<char *, v8js_persistent_obj_t> modules_loaded;
53+
std::map<char *, v8js_persistent_obj_t, cmp_str> modules_loaded;
4854
std::map<const char *,v8js_tmpl_t> template_cache;
4955

5056
std::map<zval *, v8js_persistent_obj_t> weak_objects;

v8js_commonjs.cc

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,18 @@ extern "C" {
2222

2323
#include "php_v8js_macros.h"
2424

25-
void v8js_commonjs_split_terms(char *identifier, std::vector<char *> &terms)
25+
static void v8js_commonjs_split_terms(const char *identifier, std::vector<char *> &terms)
2626
{
27-
char *term = (char *)malloc(PATH_MAX), *ptr = term;
28-
29-
// Initialise the term string
30-
*term = 0;
27+
char *term = (char *) emalloc(PATH_MAX), *ptr = term;
3128

3229
while (*identifier > 0) {
3330
if (*identifier == '/') {
34-
if (strlen(term) > 0) {
31+
if (ptr > term) {
3532
// Terminate term string and add to terms vector
3633
*ptr++ = 0;
37-
terms.push_back(strdup(term));
34+
terms.push_back(estrdup(term));
3835

3936
// Reset term string
40-
memset(term, 0, strlen(term));
4137
ptr = term;
4238
}
4339
} else {
@@ -47,18 +43,16 @@ void v8js_commonjs_split_terms(char *identifier, std::vector<char *> &terms)
4743
identifier++;
4844
}
4945

50-
if (strlen(term) > 0) {
46+
if (ptr > term) {
5147
// Terminate term string and add to terms vector
5248
*ptr++ = 0;
53-
terms.push_back(strdup(term));
49+
terms.push_back(estrdup(term));
5450
}
5551

56-
if (term > 0) {
57-
free(term);
58-
}
52+
efree(term);
5953
}
6054

61-
void v8js_commonjs_normalise_identifier(char *base, char *identifier, char *normalised_path, char *module_name)
55+
void v8js_commonjs_normalise_identifier(const char *base, const char *identifier, char *normalised_path, char *module_name)
6256
{
6357
std::vector<char *> id_terms, terms;
6458
v8js_commonjs_split_terms(identifier, id_terms);
@@ -78,12 +72,19 @@ void v8js_commonjs_normalise_identifier(char *base, char *identifier, char *norm
7872
if (!strcmp(term, "..")) {
7973
// Ignore parent term (..) if it's the first normalised term
8074
if (normalised_terms.size() > 0) {
81-
// Remove the parent normalized term
75+
// Remove the parent normalized term (and free it)
76+
efree(normalised_terms.back());
8277
normalised_terms.pop_back();
8378
}
79+
80+
// free the ".." term
81+
efree(term);
8482
} else if (strcmp(term, ".")) {
8583
// Add the term if it's not the current term (.)
8684
normalised_terms.push_back(term);
85+
} else {
86+
// Discard "." term
87+
efree(term);
8788
}
8889
}
8990

@@ -92,6 +93,8 @@ void v8js_commonjs_normalise_identifier(char *base, char *identifier, char *norm
9293
*module_name = 0;
9394

9495
strcat(module_name, normalised_terms.back());
96+
97+
efree(normalised_terms.back());
9598
normalised_terms.pop_back();
9699

97100
for (std::vector<char *>::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
102105
}
103106

104107
strcat(normalised_path, term);
108+
efree(term);
105109
}
106110
}

0 commit comments

Comments
 (0)