summaryrefslogtreecommitdiff
path: root/deps/npm/node_modules/genfun/lib
diff options
context:
space:
mode:
Diffstat (limited to 'deps/npm/node_modules/genfun/lib')
-rw-r--r--deps/npm/node_modules/genfun/lib/genfun.js296
-rw-r--r--deps/npm/node_modules/genfun/lib/method.js86
-rw-r--r--deps/npm/node_modules/genfun/lib/role.js17
-rw-r--r--deps/npm/node_modules/genfun/lib/util.js37
4 files changed, 436 insertions, 0 deletions
diff --git a/deps/npm/node_modules/genfun/lib/genfun.js b/deps/npm/node_modules/genfun/lib/genfun.js
new file mode 100644
index 0000000000..c6ba01ca54
--- /dev/null
+++ b/deps/npm/node_modules/genfun/lib/genfun.js
@@ -0,0 +1,296 @@
+'use strict'
+
+const Method = require('./method')
+const Role = require('./role')
+const util = require('./util')
+
+const kCache = Symbol('cache')
+const kDefaultMethod = Symbol('defaultMethod')
+const kMethods = Symbol('methods')
+const kNoNext = Symbol('noNext')
+
+module.exports = function genfun (opts) {
+ function gf () {
+ if (!gf[kMethods].length && gf[kDefaultMethod]) {
+ return gf[kDefaultMethod].func.apply(this, arguments)
+ } else {
+ return gf.applyGenfun(this, arguments)
+ }
+ }
+ Object.setPrototypeOf(gf, Genfun.prototype)
+ gf[kMethods] = []
+ gf[kCache] = {key: [], methods: [], state: STATES.UNINITIALIZED}
+ if (opts && typeof opts === 'function') {
+ gf.add(opts)
+ } else if (opts && opts.default) {
+ gf.add(opts.default)
+ }
+ if (opts && opts.name) {
+ Object.defineProperty(gf, 'name', {
+ value: opts.name
+ })
+ }
+ if (opts && opts.noNextMethod) {
+ gf[kNoNext] = true
+ }
+ return gf
+}
+
+class Genfun extends Function {}
+Genfun.prototype.isGenfun = true
+
+const STATES = {
+ UNINITIALIZED: 0,
+ MONOMORPHIC: 1,
+ POLYMORPHIC: 2,
+ MEGAMORPHIC: 3
+}
+
+const MAX_CACHE_SIZE = 32
+
+/**
+ * Defines a method on a generic function.
+ *
+ * @function
+ * @param {Array-like} selector - Selector array for dispatching the method.
+ * @param {Function} methodFunction - Function to execute when the method
+ * successfully dispatches.
+ */
+Genfun.prototype.add = function addMethod (selector, func) {
+ if (!func && typeof selector === 'function') {
+ func = selector
+ selector = []
+ }
+ selector = [].slice.call(selector)
+ for (var i = 0; i < selector.length; i++) {
+ if (!selector.hasOwnProperty(i)) {
+ selector[i] = Object.prototype
+ }
+ }
+ this[kCache] = {key: [], methods: [], state: STATES.UNINITIALIZED}
+ let method = new Method(this, selector, func)
+ if (selector.length) {
+ this[kMethods].push(method)
+ } else {
+ this[kDefaultMethod] = method
+ }
+ return this
+}
+
+/**
+ * Removes a previously-defined method on `genfun` that matches
+ * `selector` exactly.
+ *
+ * @function
+ * @param {Genfun} genfun - Genfun to remove a method from.
+ * @param {Array-like} selector - Objects to match on when finding a
+ * method to remove.
+ */
+Genfun.prototype.rm = function removeMethod () {
+ throw new Error('not yet implemented')
+}
+
+/**
+ * Returns true if there are methods that apply to the given arguments on
+ * `genfun`. Additionally, makes sure the cache is warmed up for the given
+ * arguments.
+ *
+ */
+Genfun.prototype.hasMethod = function hasMethod () {
+ const methods = this.getApplicableMethods(arguments)
+ return !!(methods && methods.length)
+}
+
+/**
+ * This generic function is called when `genfun` has been called and no
+ * applicable method was found. The default method throws an `Error`.
+ *
+ * @function
+ * @param {Genfun} genfun - Generic function instance that was called.
+ * @param {*} newthis - value of `this` the genfun was called with.
+ * @param {Array} callArgs - Arguments the genfun was called with.
+ */
+module.exports.noApplicableMethod = module.exports()
+module.exports.noApplicableMethod.add([], (gf, thisArg, args) => {
+ let msg =
+ 'No applicable method found when called with arguments of types: (' +
+ [].map.call(args, (arg) => {
+ return (/\[object ([a-zA-Z0-9]+)\]/)
+ .exec(({}).toString.call(arg))[1]
+ }).join(', ') + ')'
+ let err = new Error(msg)
+ err.genfun = gf
+ err.thisArg = thisArg
+ err.args = args
+ throw err
+})
+
+/*
+ * Internal
+ */
+Genfun.prototype.applyGenfun = function applyGenfun (newThis, args) {
+ let applicableMethods = this.getApplicableMethods(args)
+ if (applicableMethods.length === 1 || this[kNoNext]) {
+ return applicableMethods[0].func.apply(newThis, args)
+ } else if (applicableMethods.length > 1) {
+ let idx = 0
+ const nextMethod = function nextMethod () {
+ if (arguments.length) {
+ // Replace args if passed in explicitly
+ args = arguments
+ Array.prototype.push.call(args, nextMethod)
+ }
+ const next = applicableMethods[idx++]
+ if (idx >= applicableMethods.length) {
+ Array.prototype.pop.call(args)
+ }
+ return next.func.apply(newThis, args)
+ }
+ Array.prototype.push.call(args, nextMethod)
+ return nextMethod()
+ } else {
+ return module.exports.noApplicableMethod(this, newThis, args)
+ }
+}
+
+Genfun.prototype.getApplicableMethods = function getApplicableMethods (args) {
+ if (!args.length || !this[kMethods].length) {
+ return this[kDefaultMethod] ? [this[kDefaultMethod]] : []
+ }
+ let applicableMethods
+ let maybeMethods = cachedMethods(this, args)
+ if (maybeMethods) {
+ applicableMethods = maybeMethods
+ } else {
+ applicableMethods = computeApplicableMethods(this, args)
+ cacheArgs(this, args, applicableMethods)
+ }
+ return applicableMethods
+}
+
+function cacheArgs (genfun, args, methods) {
+ if (genfun[kCache].state === STATES.MEGAMORPHIC) { return }
+ var key = []
+ var proto
+ for (var i = 0; i < args.length; i++) {
+ proto = cacheableProto(genfun, args[i])
+ if (proto) {
+ key[i] = proto
+ } else {
+ return null
+ }
+ }
+ genfun[kCache].key.unshift(key)
+ genfun[kCache].methods.unshift(methods)
+ if (genfun[kCache].key.length === 1) {
+ genfun[kCache].state = STATES.MONOMORPHIC
+ } else if (genfun[kCache].key.length < MAX_CACHE_SIZE) {
+ genfun[kCache].state = STATES.POLYMORPHIC
+ } else {
+ genfun[kCache].state = STATES.MEGAMORPHIC
+ }
+}
+
+function cacheableProto (genfun, arg) {
+ var dispatchable = util.dispatchableObject(arg)
+ if (Object.hasOwnProperty.call(dispatchable, Role.roleKeyName)) {
+ for (var j = 0; j < dispatchable[Role.roleKeyName].length; j++) {
+ var role = dispatchable[Role.roleKeyName][j]
+ if (role.method.genfun === genfun) {
+ return null
+ }
+ }
+ }
+ return Object.getPrototypeOf(dispatchable)
+}
+
+function cachedMethods (genfun, args) {
+ if (genfun[kCache].state === STATES.UNINITIALIZED ||
+ genfun[kCache].state === STATES.MEGAMORPHIC) {
+ return null
+ }
+ var protos = []
+ var proto
+ for (var i = 0; i < args.length; i++) {
+ proto = cacheableProto(genfun, args[i])
+ if (proto) {
+ protos[i] = proto
+ } else {
+ return
+ }
+ }
+ for (i = 0; i < genfun[kCache].key.length; i++) {
+ if (matchCachedMethods(genfun[kCache].key[i], protos)) {
+ return genfun[kCache].methods[i]
+ }
+ }
+}
+
+function matchCachedMethods (key, protos) {
+ if (key.length !== protos.length) { return false }
+ for (var i = 0; i < key.length; i++) {
+ if (key[i] !== protos[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+function computeApplicableMethods (genfun, args) {
+ args = [].slice.call(args)
+ let discoveredMethods = []
+ function findAndRankRoles (object, hierarchyPosition, index) {
+ var roles = Object.hasOwnProperty.call(object, Role.roleKeyName)
+ ? object[Role.roleKeyName]
+ : []
+ roles.forEach(role => {
+ if (role.method.genfun === genfun && index === role.position) {
+ if (discoveredMethods.indexOf(role.method) < 0) {
+ Method.clearRank(role.method)
+ discoveredMethods.push(role.method)
+ }
+ Method.setRankHierarchyPosition(role.method, index, hierarchyPosition)
+ }
+ })
+ // When a discovered method would receive more arguments than
+ // were specialized, we pretend all extra arguments have a role
+ // on Object.prototype.
+ if (util.isObjectProto(object)) {
+ discoveredMethods.forEach(method => {
+ if (method.minimalSelector <= index) {
+ Method.setRankHierarchyPosition(method, index, hierarchyPosition)
+ }
+ })
+ }
+ }
+ args.forEach((arg, index) => {
+ getPrecedenceList(util.dispatchableObject(arg))
+ .forEach((obj, hierarchyPosition) => {
+ findAndRankRoles(obj, hierarchyPosition, index)
+ })
+ })
+ let applicableMethods = discoveredMethods.filter(method => {
+ return (args.length === method._rank.length &&
+ Method.isFullySpecified(method))
+ })
+ applicableMethods.sort((a, b) => Method.score(a) - Method.score(b))
+ if (genfun[kDefaultMethod]) {
+ applicableMethods.push(genfun[kDefaultMethod])
+ }
+ return applicableMethods
+}
+
+/*
+ * Helper function for getting an array representing the entire
+ * inheritance/precedence chain for an object by navigating its
+ * prototype pointers.
+ */
+function getPrecedenceList (obj) {
+ var precedenceList = []
+ var nextObj = obj
+ while (nextObj) {
+ precedenceList.push(nextObj)
+ nextObj = Object.getPrototypeOf(nextObj)
+ }
+ return precedenceList
+}
diff --git a/deps/npm/node_modules/genfun/lib/method.js b/deps/npm/node_modules/genfun/lib/method.js
new file mode 100644
index 0000000000..5a9d9f788e
--- /dev/null
+++ b/deps/npm/node_modules/genfun/lib/method.js
@@ -0,0 +1,86 @@
+'use strict'
+
+/*
+ * Method
+ *
+ * Methods are added, conceptually, to Genfuns, not to objects
+ * themselves, although the Genfun object does not have any pointers to
+ * method objects.
+ *
+ * The _rank vector is an internal datastructure used during dispatch
+ * to figure out whether a method is applicable, and if so, how to
+ * order multiple discovered methods.
+ *
+ * Right now, the score method on Method does not take into account any
+ * ordering, and all arguments to a method are ranked equally for the
+ * sake of ordering.
+ *
+ */
+const Role = require('./role')
+const util = require('./util')
+
+module.exports = Method
+function Method (genfun, selector, func) {
+ var method = this
+ method.genfun = genfun
+ method.func = func
+ method._rank = []
+ method.minimalSelector = 0
+
+ const tmpSelector = selector.length ? selector : [Object.prototype]
+ for (var object, i = tmpSelector.length - 1; i >= 0; i--) {
+ object = Object.hasOwnProperty.call(tmpSelector, i)
+ ? tmpSelector[i]
+ : Object.prototype
+ object = util.dispatchableObject(object)
+ if (
+ typeof object === 'function' &&
+ !object.isGenfun
+ ) {
+ object = object.prototype
+ }
+ if (i > 0 &&
+ !method.minimalSelector &&
+ util.isObjectProto(object)) {
+ continue
+ } else {
+ method.minimalSelector++
+ if (!Object.hasOwnProperty.call(object, Role.roleKeyName)) {
+ if (Object.defineProperty) {
+ // Object.defineProperty is JS 1.8.0+
+ Object.defineProperty(
+ object, Role.roleKeyName, {value: [], enumerable: false})
+ } else {
+ object[Role.roleKeyName] = []
+ }
+ }
+ // XXX HACK - no method replacement now, so we just shove
+ // it in a place where it'll always show up first. This
+ // would probably seriously break method combination if we
+ // had it.
+ object[Role.roleKeyName].unshift(new Role(method, i))
+ }
+ }
+}
+
+Method.setRankHierarchyPosition = (method, index, hierarchyPosition) => {
+ method._rank[index] = hierarchyPosition
+}
+
+Method.clearRank = method => {
+ method._rank = []
+}
+
+Method.isFullySpecified = method => {
+ for (var i = 0; i < method.minimalSelector; i++) {
+ if (!method._rank.hasOwnProperty(i)) {
+ return false
+ }
+ }
+ return true
+}
+
+Method.score = method => {
+ // TODO - this makes all items in the list equal
+ return method._rank.reduce((a, b) => a + b, 0)
+}
diff --git a/deps/npm/node_modules/genfun/lib/role.js b/deps/npm/node_modules/genfun/lib/role.js
new file mode 100644
index 0000000000..69e35c2e58
--- /dev/null
+++ b/deps/npm/node_modules/genfun/lib/role.js
@@ -0,0 +1,17 @@
+'use strict'
+
+/*
+ * Role
+ *
+ * A Role encapsulates a particular object's 'role' in a method's
+ * dispatch. They are added directly to the selector for a method, and thus
+ * do not prevent the objects a method was defined on from being garbage
+ * collected.
+ */
+module.exports = Role
+function Role (method, position) {
+ this.method = method
+ this.position = position
+}
+
+Role.roleKeyName = Symbol('roles')
diff --git a/deps/npm/node_modules/genfun/lib/util.js b/deps/npm/node_modules/genfun/lib/util.js
new file mode 100644
index 0000000000..23770629d3
--- /dev/null
+++ b/deps/npm/node_modules/genfun/lib/util.js
@@ -0,0 +1,37 @@
+'use strict'
+
+module.exports.isObjectProto = isObjectProto
+function isObjectProto (obj) {
+ return obj === Object.prototype
+}
+
+const _null = {}
+const _undefined = {}
+const Bool = Boolean
+const Num = Number
+const Str = String
+const boolCache = {
+ true: new Bool(true),
+ false: new Bool(false)
+}
+const numCache = {}
+const strCache = {}
+
+/*
+ * Returns a useful dispatch object for value using a process similar to
+ * the ToObject operation specified in http://es5.github.com/#x9.9
+ */
+module.exports.dispatchableObject = dispatchableObject
+function dispatchableObject (value) {
+ // To shut up jshint, which doesn't let me turn off this warning.
+ const Obj = Object
+ if (value === null) { return _null }
+ if (value === undefined) { return _undefined }
+ switch (typeof value) {
+ case 'object': return value
+ case 'boolean': return boolCache[value]
+ case 'number': return numCache[value] || (numCache[value] = new Num(value))
+ case 'string': return strCache[value] || (strCache[value] = new Str(value))
+ default: return new Obj(value)
+ }
+}