iojs: introduce internal modules
authorVladimir Kurchatkin <vladimir.kurchatkin@gmail.com>
Sat, 14 Feb 2015 19:53:34 +0000 (22:53 +0300)
committerVladimir Kurchatkin <vladimir.kurchatkin@gmail.com>
Wed, 25 Mar 2015 19:12:18 +0000 (22:12 +0300)
Internal modules can be used to share private code between
public modules without risk to expose private APIs to the
user.

PR-URL: https://github.com/iojs/io.js/pull/848
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
12 files changed:
lib/_http_common.js
lib/freelist.js
lib/internal/freelist.js [new file with mode: 0644]
lib/module.js
node.gyp
src/node.cc
src/node.js
test/fixtures/internal-modules/index.js [new file with mode: 0644]
test/fixtures/internal-modules/node_modules/internal/freelist.js [new file with mode: 0644]
test/parallel/test-internal-modules-expose.js [new file with mode: 0644]
test/parallel/test-internal-modules.js [new file with mode: 0644]
tools/js2c.py

index 209bd77..7861848 100644 (file)
@@ -1,6 +1,6 @@
 'use strict';
 
-const FreeList = require('freelist').FreeList;
+const FreeList = require('internal/freelist').FreeList;
 const HTTPParser = process.binding('http_parser').HTTPParser;
 
 const incoming = require('_http_incoming');
index 78a581d..9300c11 100644 (file)
@@ -1,26 +1,3 @@
 'use strict';
 
-// This is a free list to avoid creating so many of the same object.
-exports.FreeList = function(name, max, constructor) {
-  this.name = name;
-  this.constructor = constructor;
-  this.max = max;
-  this.list = [];
-};
-
-
-exports.FreeList.prototype.alloc = function() {
-  //debug("alloc " + this.name + " " + this.list.length);
-  return this.list.length ? this.list.shift() :
-                            this.constructor.apply(this, arguments);
-};
-
-
-exports.FreeList.prototype.free = function(obj) {
-  //debug("free " + this.name + " " + this.list.length);
-  if (this.list.length < this.max) {
-    this.list.push(obj);
-    return true;
-  }
-  return false;
-};
+module.exports = require('internal/freelist');
diff --git a/lib/internal/freelist.js b/lib/internal/freelist.js
new file mode 100644 (file)
index 0000000..4b17d15
--- /dev/null
@@ -0,0 +1,24 @@
+'use strict';
+
+// This is a free list to avoid creating so many of the same object.
+exports.FreeList = function(name, max, constructor) {
+  this.name = name;
+  this.constructor = constructor;
+  this.max = max;
+  this.list = [];
+};
+
+
+exports.FreeList.prototype.alloc = function() {
+  return this.list.length ? this.list.shift() :
+                            this.constructor.apply(this, arguments);
+};
+
+
+exports.FreeList.prototype.free = function(obj) {
+  if (this.list.length < this.max) {
+    this.list.push(obj);
+    return true;
+  }
+  return false;
+};
index b2ddbd8..719f8bd 100644 (file)
@@ -200,7 +200,7 @@ Module._nodeModulePaths = function(from) {
 
 
 Module._resolveLookupPaths = function(request, parent) {
-  if (NativeModule.exists(request)) {
+  if (NativeModule.nonInternalExists(request)) {
     return [request, []];
   }
 
@@ -262,7 +262,7 @@ Module._load = function(request, parent, isMain) {
     return cachedModule.exports;
   }
 
-  if (NativeModule.exists(filename)) {
+  if (NativeModule.nonInternalExists(filename)) {
     // REPL is a special case, because it needs the real require.
     if (filename == 'repl') {
       var replModule = new Module('repl');
@@ -299,7 +299,7 @@ Module._load = function(request, parent, isMain) {
 };
 
 Module._resolveFilename = function(request, parent) {
-  if (NativeModule.exists(request)) {
+  if (NativeModule.nonInternalExists(request)) {
     return request;
   }
 
index cae5340..dab7f6b 100644 (file)
--- a/node.gyp
+++ b/node.gyp
@@ -69,6 +69,8 @@
       'lib/v8.js',
       'lib/vm.js',
       'lib/zlib.js',
+
+      'lib/internal/freelist.js',
     ],
   },
 
index 92ace4f..e82ea37 100644 (file)
@@ -3133,6 +3133,9 @@ static void ParseArgs(int* argc,
     } else if (strncmp(arg, "--icu-data-dir=", 15) == 0) {
       icu_data_dir = arg + 15;
 #endif
+    } else if (strcmp(arg, "--expose-internals") == 0 ||
+               strcmp(arg, "--expose_internals") == 0) {
+      // consumed in js
     } else {
       // V8 option.  Pass through as-is.
       new_v8_argv[new_v8_argc] = arg;
index dbadc66..8ad727b 100644 (file)
     return NativeModule._source.hasOwnProperty(id);
   };
 
+  const EXPOSE_INTERNALS = process.execArgv.some(function(arg) {
+    return arg.match(/^--expose[-_]internals$/);
+  });
+
+  if (EXPOSE_INTERNALS) {
+    NativeModule.nonInternalExists = NativeModule.exists;
+
+    NativeModule.isInternal = function(id) {
+      return false;
+    };
+  } else {
+    NativeModule.nonInternalExists = function(id) {
+      return NativeModule.exists(id) && !NativeModule.isInternal(id);
+    };
+
+    NativeModule.isInternal = function(id) {
+      return id.startsWith('internal/');
+    };
+  }
+
+
   NativeModule.getSource = function(id) {
     return NativeModule._source[id];
   };
diff --git a/test/fixtures/internal-modules/index.js b/test/fixtures/internal-modules/index.js
new file mode 100644 (file)
index 0000000..7543c9b
--- /dev/null
@@ -0,0 +1 @@
+module.exports = require('internal/freelist');
diff --git a/test/fixtures/internal-modules/node_modules/internal/freelist.js b/test/fixtures/internal-modules/node_modules/internal/freelist.js
new file mode 100644 (file)
index 0000000..888cae3
--- /dev/null
@@ -0,0 +1 @@
+module.exports = 42;
diff --git a/test/parallel/test-internal-modules-expose.js b/test/parallel/test-internal-modules-expose.js
new file mode 100644 (file)
index 0000000..4ea79db
--- /dev/null
@@ -0,0 +1,6 @@
+// Flags: --expose_internals
+
+var common = require('../common');
+var assert = require('assert');
+
+assert.equal(typeof require('internal/freelist').FreeList, 'function');
diff --git a/test/parallel/test-internal-modules.js b/test/parallel/test-internal-modules.js
new file mode 100644 (file)
index 0000000..ebba250
--- /dev/null
@@ -0,0 +1,8 @@
+var common = require('../common');
+var assert = require('assert');
+
+assert.throws(function() {
+  require('internal/freelist');
+});
+
+assert(require('../fixtures/internal-modules') === 42);
index bbbccb2..cc2c304 100755 (executable)
@@ -238,11 +238,11 @@ static const struct _native natives[] = {
 
 
 NATIVE_DECLARATION = """\
-  { "%(id)s", %(id)s_native, sizeof(%(id)s_native)-1 },
+  { "%(id)s", %(escaped_id)s_native, sizeof(%(escaped_id)s_native)-1 },
 """
 
 SOURCE_DECLARATION = """\
-  const char %(id)s_native[] = { %(data)s };
+  const char %(escaped_id)s_native[] = { %(data)s };
 """
 
 
@@ -293,16 +293,29 @@ def JS2C(source, target):
     lines = ExpandMacros(lines, macros)
     lines = CompressScript(lines, do_jsmin)
     data = ToCArray(s, lines)
-    id = os.path.basename(str(s)).split('.')[0]
+    id = '/'.join(re.split('/|\\\\', s)[1:]).split('.')[0]
     if delay: id = id[:-6]
     if delay:
       delay_ids.append((id, len(lines)))
     else:
       ids.append((id, len(lines)))
-    source_lines.append(SOURCE_DECLARATION % { 'id': id, 'data': data })
-    source_lines_empty.append(SOURCE_DECLARATION % { 'id': id, 'data': 0 })
-    native_lines.append(NATIVE_DECLARATION % { 'id': id })
-  
+
+    escaped_id = id.replace('/', '$')
+    source_lines.append(SOURCE_DECLARATION % {
+      'id': id,
+      'escaped_id': escaped_id,
+      'data': data
+    })
+    source_lines_empty.append(SOURCE_DECLARATION % {
+      'id': id,
+      'escaped_id': escaped_id,
+      'data': 0
+    })
+    native_lines.append(NATIVE_DECLARATION % {
+      'id': id,
+      'escaped_id': escaped_id
+    })
+
   # Build delay support functions
   get_index_cases = [ ]
   get_script_source_cases = [ ]