diff --git a/Commandfile b/Commandfile index 35b6fee..635f100 100644 --- a/Commandfile +++ b/Commandfile @@ -26,7 +26,7 @@ command 'clean', command 'build', description: 'executes "make"', script: <<-eof - cd /data/build; `which gmake || which make` + cd /data/build; `which gmake || which make` -j4 eof command 'test', diff --git a/tests/issue_349_basic.phpt b/tests/issue_349_basic.phpt new file mode 100644 index 0000000..f7a6d7d --- /dev/null +++ b/tests/issue_349_basic.phpt @@ -0,0 +1,82 @@ +--TEST-- +Test V8Js::setModuleNormaliser : Custom normalisation #005 +--SKIPIF-- + +--FILE-- +setModuleNormaliser(function ($base, $moduleName) { + var_dump($base, $moduleName); + if ($base == '' && $moduleName == './tags') { + return ['./tags', 'index.js']; + } + if ($base == './tags' && $moduleName == './if.js') { + return ['./tags', 'if.js']; + } + return [$base, $moduleName]; +}); + +$v8->setModuleLoader(function ($moduleName) { + print("setModuleLoader called for ".$moduleName."\n"); + switch ($moduleName) { + case './app.js': + return "require('./tags')"; + case './tags/index.js': + return "require('./if.js')"; + } +}); + +$v8->executeString("require('./app.js')"); + +echo "------------------------------------------------\n"; + +$v8 = new V8Js(); + +$v8->setModuleNormaliser(function ($base, $moduleName) { + var_dump($base, $moduleName); + if ($base == '' && $moduleName == './tags') { + return ['./tags', 'index.js']; + } + if ($base == './tags' && $moduleName == './if.js') { + return ['./tags', 'if.js']; + } + return [$base, $moduleName]; +}); + +$v8->setModuleLoader(function ($moduleName) { + print("setModuleLoader called for ".$moduleName."\n"); + switch ($moduleName) { + case './app.js': + return "require('./tags')()"; // different + case './tags/index.js': + return "module.exports = function() {require('./if.js')}"; // different + } +}); + +$v8->executeString("require('./app.js')"); + +?> +===EOF=== +--EXPECT-- +string(0) "" +string(8) "./app.js" +setModuleLoader called for ./app.js +string(0) "" +string(6) "./tags" +setModuleLoader called for ./tags/index.js +string(6) "./tags" +string(7) "./if.js" +setModuleLoader called for ./tags/if.js +------------------------------------------------ +string(0) "" +string(8) "./app.js" +setModuleLoader called for ./app.js +string(0) "" +string(6) "./tags" +setModuleLoader called for ./tags/index.js +string(6) "./tags" +string(7) "./if.js" +setModuleLoader called for ./tags/if.js +===EOF=== diff --git a/v8js_class.cc b/v8js_class.cc index a572864..dc366aa 100644 --- a/v8js_class.cc +++ b/v8js_class.cc @@ -200,7 +200,6 @@ static void v8js_free_storage(zend_object *object) /* {{{ */ } c->modules_stack.~vector(); - c->modules_base.~vector(); zval_ptr_dtor(&c->zval_snapshot_blob); @@ -226,7 +225,6 @@ static zend_object* v8js_new(zend_class_entry *ce) /* {{{ */ new(&c->array_tmpl) v8::Persistent(); new(&c->modules_stack) std::vector(); - new(&c->modules_base) std::vector(); new(&c->modules_loaded) std::map; new(&c->template_cache) std::map(); diff --git a/v8js_class.h b/v8js_class.h index 4886754..cd06fc5 100644 --- a/v8js_class.h +++ b/v8js_class.h @@ -57,7 +57,6 @@ struct v8js_ctx { zval module_loader; std::vector modules_stack; - std::vector modules_base; std::map modules_loaded; std::map template_cache; diff --git a/v8js_methods.cc b/v8js_methods.cc index 947d348..4bee095 100644 --- a/v8js_methods.cc +++ b/v8js_methods.cc @@ -205,10 +205,10 @@ V8JS_METHOD(var_dump) /* {{{ */ V8JS_METHOD(require) { v8::Isolate *isolate = info.GetIsolate(); + v8js_ctx *c = (v8js_ctx *) isolate->GetData(0); - // Get the extension context - v8::Local data = v8::Local::Cast(info.Data()); - v8js_ctx *c = static_cast(data->Value()); + v8::String::Utf8Value module_base(info.Data()); + const char *module_base_cstr = ToCString(module_base); // Check that we have a module loader if(Z_TYPE(c->module_loader) == IS_NULL) { @@ -225,7 +225,7 @@ V8JS_METHOD(require) normalised_path = (char *)emalloc(PATH_MAX); module_name = (char *)emalloc(PATH_MAX); - v8js_commonjs_normalise_identifier(c->modules_base.back(), module_id, normalised_path, module_name); + v8js_commonjs_normalise_identifier(module_base_cstr, module_id, normalised_path, module_name); } else { // Call custom normaliser @@ -238,7 +238,7 @@ V8JS_METHOD(require) isolate->Exit(); v8::Unlocker unlocker(isolate); - ZVAL_STRING(¶ms[0], c->modules_base.back()); + ZVAL_STRING(¶ms[0], module_base_cstr); ZVAL_STRING(¶ms[1], module_id); call_result = call_user_function_ex(EG(function_table), NULL, &c->module_normaliser, @@ -445,7 +445,7 @@ V8JS_METHOD(require) v8::Local source = V8JS_ZSTR(Z_STR(module_code)); zval_ptr_dtor(&module_code); - source = v8::String::Concat(V8JS_SYM("(function (exports, module) {"), source); + source = v8::String::Concat(V8JS_SYM("(function (exports, module, require) {"), source); source = v8::String::Concat(source, V8JS_SYM("\n});")); // Create and compile script @@ -459,9 +459,19 @@ V8JS_METHOD(require) return; } + v8::Local base_path = V8JS_STR(normalised_path); + v8::MaybeLocal require_fn = v8::FunctionTemplate::New(isolate, V8JS_MN(require), base_path)->GetFunction(); + + if (require_fn.IsEmpty()) { + efree(normalised_path); + efree(normalised_module_id); + info.GetReturnValue().Set(isolate->ThrowException(V8JS_SYM("Failed to create require method"))); + return; + } + + // Add this module and path to the stack c->modules_stack.push_back(normalised_module_id); - c->modules_base.push_back(normalised_path); // Run script to evaluate closure v8::Local module_function = script->Run(); @@ -474,20 +484,22 @@ V8JS_METHOD(require) module->Set(V8JS_SYM("exports"), exports); if (module_function->IsFunction()) { - v8::Local *jsArgv = static_cast *>(alloca(2 * sizeof(v8::Local))); + v8::Local *jsArgv = static_cast *>(alloca(3 * sizeof(v8::Local))); new(&jsArgv[0]) v8::Local; jsArgv[0] = exports; new(&jsArgv[1]) v8::Local; jsArgv[1] = module; + new(&jsArgv[2]) v8::Local; + jsArgv[2] = require_fn.ToLocalChecked(); + // actually call the module - v8::Local::Cast(module_function)->Call(exports, 2, jsArgv); + v8::Local::Cast(module_function)->Call(exports, 3, jsArgv); } // Remove this module and path from the stack c->modules_stack.pop_back(); - c->modules_base.pop_back(); efree(normalised_path); @@ -532,8 +544,8 @@ void v8js_register_methods(v8::Local global, v8js_ctx *c) /* global->Set(V8JS_SYM("print"), v8::FunctionTemplate::New(isolate, V8JS_MN(print)), v8::ReadOnly); global->Set(V8JS_SYM("var_dump"), v8::FunctionTemplate::New(isolate, V8JS_MN(var_dump)), v8::ReadOnly); - c->modules_base.push_back(""); - global->Set(V8JS_SYM("require"), v8::FunctionTemplate::New(isolate, V8JS_MN(require), v8::External::New(isolate, c)), v8::ReadOnly); + v8::Local base_path = V8JS_STRL("", 0); + global->Set(V8JS_SYM("require"), v8::FunctionTemplate::New(isolate, V8JS_MN(require), base_path), v8::ReadOnly); } /* }}} */