Explorar o código

First working version of CommonJS modules.

Simon Best %!s(int64=12) %!d(string=hai) anos
pai
achega
84baa7614a
Modificáronse 3 ficheiros con 147 adicións e 16 borrados
  1. 15 3
      php_v8js_macros.h
  2. 25 12
      v8js.cc
  3. 107 1
      v8js_methods.cc

+ 15 - 3
php_v8js_macros.h

@@ -91,12 +91,24 @@ v8::Handle<v8::Value> zval_to_v8js(zval * TSRMLS_DC);
 /* Convert V8 value into zval */
 int v8js_to_zval(v8::Handle<v8::Value>, zval *, int TSRMLS_DC);
 
-/* Register builtin methods into passed object */
-void php_v8js_register_methods(v8::Handle<v8::ObjectTemplate>);
-
 /* Register accessors into passed object */
 void php_v8js_register_accessors(v8::Local<v8::ObjectTemplate>, zval * TSRMLS_DC);
 
+/* {{{ Context container */
+struct php_v8js_ctx {
+  zend_object std;
+  v8::Persistent<v8::String> object_name;
+  v8::Persistent<v8::Context> context;
+  zend_bool report_uncaught;
+  zval *pending_exception;
+  int in_execution;
+  zval *module_loader;
+};
+/* }}} */
+
+/* Register builtin methods into passed object */
+void php_v8js_register_methods(v8::Handle<v8::ObjectTemplate>, php_v8js_ctx *c);
+
 #endif	/* PHP_V8JS_MACROS_H */
 
 /*

+ 25 - 12
v8js.cc

@@ -126,17 +126,6 @@ struct php_v8js_jsext {
 };
 /* }}} */
 
-/* {{{ Context container */
-struct php_v8js_ctx {
-	zend_object std;
-	v8::Persistent<v8::String> object_name;
-	v8::Persistent<v8::Context> context;
-	zend_bool report_uncaught;
-	zval *pending_exception;
-	int in_execution;
-};
-/* }}} */
-
 /* {{{ Object container */
 struct php_v8js_object {
 	zend_object std;
@@ -579,6 +568,7 @@ static PHP_METHOD(V8Js, __construct)
 	c->report_uncaught = report_uncaught;
 	c->pending_exception = NULL;
 	c->in_execution = 0;
+	c->module_loader = NULL;
 
 	/* Initialize V8 */
 	php_v8js_init(TSRMLS_C);
@@ -606,7 +596,7 @@ static PHP_METHOD(V8Js, __construct)
 		V8JSG(global_template)->SetClassName(V8JS_SYM("V8Js"));
 
 		/* Register builtin methods */
-		php_v8js_register_methods(V8JSG(global_template)->InstanceTemplate());
+		php_v8js_register_methods(V8JSG(global_template)->InstanceTemplate(), c);
 	}
 
 	/* Create context */
@@ -766,6 +756,24 @@ static PHP_METHOD(V8Js, getPendingException)
 }
 /* }}} */
 
+/* {{{ proto void V8Js::setModuleLoader(string module)
+ */
+static PHP_METHOD(V8Js, setModuleLoader)
+{
+	php_v8js_ctx *c;
+	zval *callable;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &callable) == FAILURE) {
+		return;
+	}
+
+	c = (php_v8js_ctx *) zend_object_store_get_object(getThis() TSRMLS_CC);
+
+	c->module_loader = callable;
+	Z_ADDREF_P(c->module_loader);
+}
+/* }}} */
+
 static void php_v8js_persistent_zval_ctor(zval **p) /* {{{ */
 {
 	zval *orig_ptr = *p;
@@ -925,6 +933,10 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_INFO(arginfo_v8js_getpendingexception, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_setmoduleloader, 0, 0, 1)
+	ZEND_ARG_INFO(0, callable)
+ZEND_END_ARG_INFO()
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_v8js_registerextension, 0, 0, 2)
 	ZEND_ARG_INFO(0, extension_name)
 	ZEND_ARG_INFO(0, script)
@@ -943,6 +955,7 @@ static const zend_function_entry v8js_methods[] = { /* {{{ */
 	PHP_ME(V8Js,	__construct,			arginfo_v8js_construct,				ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
 	PHP_ME(V8Js,	executeString,			arginfo_v8js_executestring,			ZEND_ACC_PUBLIC)
 	PHP_ME(V8Js,	getPendingException,	arginfo_v8js_getpendingexception,	ZEND_ACC_PUBLIC)
+	PHP_ME(V8Js,	setModuleLoader,		arginfo_v8js_setmoduleloader,		ZEND_ACC_PUBLIC)
 	PHP_ME(V8Js,	registerExtension,		arginfo_v8js_registerextension,		ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 	PHP_ME(V8Js,	getExtensions,			arginfo_v8js_getextensions,			ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 	{NULL, NULL, NULL}

+ 107 - 1
v8js_methods.cc

@@ -29,6 +29,7 @@ extern "C" {
 
 #include "php_v8js_macros.h"
 #include <v8.h>
+#include <map>
 
 /* global.exit - terminate execution */
 V8JS_METHOD(exit) /* {{{ */
@@ -167,12 +168,117 @@ V8JS_METHOD(var_dump) /* {{{ */
 }
 /* }}} */
 
-void php_v8js_register_methods(v8::Handle<v8::ObjectTemplate> global) /* {{{ */
+// TODO: Put this in php_v8js_context
+std::map<char *, v8::Handle<v8::Object> > modules_loaded;
+
+V8JS_METHOD(require)
+{
+	//v8::Persistent<v8::Value> module_name_value_v8 = v8::Persistent<v8::Value>::New(args[0]->ToObject());
+	v8::String::Utf8Value module_name_v8(args[0]);
+
+	// Make sure to duplicate the string to ensure it is not freed by V8's garbage collector
+	char *module_name = strdup(ToCString(module_name_v8));
+
+	if (modules_loaded.count(module_name) > 0) {
+printf("Using cached module\n");
+		return modules_loaded[module_name];
+	}
+
+	zval module_code;
+	zval *module_name_zend;
+
+	MAKE_STD_ZVAL(module_name_zend);
+	ZVAL_STRING(module_name_zend, module_name, 1);
+
+	zval* params[] = { module_name_zend };
+
+	// Get the module loader from extension context
+	v8::Handle<v8::External> data = v8::Handle<v8::External>::Cast(args.Data());
+	php_v8js_ctx *c = static_cast<php_v8js_ctx*>(data->Value());
+
+	if (c->module_loader == NULL) {
+		printf("Module loader not defined!\n");
+		exit(-1);
+	}
+
+	if (SUCCESS == call_user_function(EG(function_table), NULL, c->module_loader, &module_code, 1, params TSRMLS_CC)) {
+		printf("CODE = %s\n", Z_STRVAL(module_code));
+	} else {
+		printf("Module loader callback failed\n");
+		exit(-1);
+	}
+
+	// Create a template for the global object and set the built-in global functions
+	v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
+	global->Set(v8::String::New("print"), v8::FunctionTemplate::New(V8JS_MN(print)), v8::ReadOnly);
+	global->Set(v8::String::New("require"), v8::FunctionTemplate::New(V8JS_MN(require), v8::External::New(c)), v8::ReadOnly);
+
+	// Add the exports object
+	v8::Handle<v8::ObjectTemplate> exports_template = v8::ObjectTemplate::New();
+	v8::Handle<v8::Object> exports = exports_template->NewInstance();
+	global->Set(v8::String::New("exports"), exports);
+
+	// Each module gets its own context so different modules do not affect each other
+	v8::Persistent<v8::Context> context = v8::Context::New(NULL, global);
+
+	// Catch JS exceptions
+	v8::TryCatch try_catch;
+
+	// Enter the module context
+	v8::Context::Scope scope(context);
+
+	v8::HandleScope handle_scope;
+
+	// Set script identifier
+	v8::Local<v8::String> sname = V8JS_SYM("require");
+
+	// TODO: Load module code here
+	v8::Local<v8::String> source = v8::String::New(Z_STRVAL(module_code));
+
+	// Create and compile script
+	v8::Local<v8::Script> script = v8::Script::New(source, sname);
+
+	// The script will be empty if there are compile errors
+	if (script.IsEmpty()) {
+		//php_v8js_throw_exception(&try_catch TSRMLS_CC);
+		printf("Compile error\n");
+		exit(-1);
+		return V8JS_NULL;
+	}
+
+	// Run script
+	//c->in_execution++;
+	v8::Local<v8::Value> result = script->Run();
+	//c->in_execution--;
+
+	// Script possibly terminated, return immediately
+	if (!try_catch.CanContinue()) {
+		// TODO: Throw PHP exception here?
+		return V8JS_NULL;
+	}
+
+	// Handle runtime JS exceptions
+	if (try_catch.HasCaught()) {
+
+		// Rethrow the exception back to JS
+		try_catch.ReThrow();
+		return V8JS_NULL;
+	}
+
+	// Cache the module
+	modules_loaded[module_name] = handle_scope.Close(exports);
+
+	return modules_loaded[module_name];
+}
+
+void php_v8js_register_methods(v8::Handle<v8::ObjectTemplate> global, php_v8js_ctx *c) /* {{{ */
 {
 	global->Set(V8JS_SYM("exit"), v8::FunctionTemplate::New(V8JS_MN(exit)), v8::ReadOnly);
 	global->Set(V8JS_SYM("sleep"), v8::FunctionTemplate::New(V8JS_MN(sleep)), v8::ReadOnly);
 	global->Set(V8JS_SYM("print"), v8::FunctionTemplate::New(V8JS_MN(print)), v8::ReadOnly);
 	global->Set(V8JS_SYM("var_dump"), v8::FunctionTemplate::New(V8JS_MN(var_dump)), v8::ReadOnly);
+
+	global->Set(V8JS_SYM("require"), v8::FunctionTemplate::New(V8JS_MN(require), v8::External::New(c)), v8::ReadOnly);
 }
 /* }}} */