/** * Combined redistributable GNU ease.js file. * * For the original, uncombined and unminifed source, please visit * . * * @licstart The following is the entire license notice for the JavaScript * code in this file. * * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc. * * GNU ease.js is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License (GNU GPL) as published * by the Free Software Foundation, either version 3 of the License, or (at * your option) any later version. This library is distributed WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU GPL for more details. * * As additional permission under GNU GPL version 3 section 7, you may * distribute non-source (e.g., minimized or compacted) forms of this * library without the copy of the GNU GPL normally required by section 4, * provided you include this license notice and a URL through which * recipients can access the Corresponding Source. * * @licend The following is the entire license notice for the JavaScript * code in this file. */ /** * ease.js namespace * * All modules will be available via this namespace. In CommonJS format, they * were accessed via the require() function. For example: * * var util = require( 'easejs' ).Class; * * In this file, the above would be written as: * * var util = easejs.Class; * * @type {Object} */ var easejs = {}; ( function( ns_exports, __cwd ) { /** * CommonJS module exports * * Since this file contains all of the modules, this will be populated with * every module right off the bat. * * @type {Object.} */ var module = {}; /** * Returns the requested module * * The require() function is likely unavailable client-side (within a web * browser). Therefore, we mock one. If it is available, this overwrites it. * Our modules are all preloaded in the exports object. * * @param {string} module_id id of the module to load * * return tag intentionally omitted; too many potential return types and * setting return type of {*} will throw warnings for those attempting to * treat the return value as a function */ var require = function( module_id ) { // anything that is not an absolute require path will be prefixed // with __cwd, which is set by the combined module; this allows // including relative paths (but note that this also means that // modules that perform ad-hoc conditional requires after another // module has been processed may not work properly; we don't do // this, though) var id_norm = ( module_id.substr( 0, 1 ) === '/' ) ? module_id : __cwd + '/' + module_id; // strip `../`, poorly strip `./` (for example, it would also strip // `foo./`, but we know that this won't ever be the case with our // files), and strip leading `/` var id_clean = id_norm.replace( /([^\/]+\/\.\.\/|\.\/|^\/)/g, '' ); // attempt to retrieve the module var mod = module[ id_clean ]; if ( mod === undefined ) { throw "[ease.js] Undefined module: " + id_clean; } return mod.exports; }; /** util/symbol/FallbackSymbol **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = 'util/symbol'; /** * Forward-compatible subset of ES6 Symbol for pre-ES6 environments * * Copyright (C) 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This is *not* intended to be a complete implementation; it merely * performs what is needed for ease.js. In particular, this pre-ES6 * implementation will simply generate a random string to be used as a key; * the caller is expected to add the key to the destination object as * non-enumerable, if supported by the environment. */ // ensures that, so long as these methods have not been overwritten by the // time ease.js is loaded, we will maintain a proper reference var _random = Math.random, _floor = Math.floor; // prefix used for all generated symbol strings (this string is highly // unlikely to exist in practice); it will produce a string containing a // non-printable ASCII character that is *not* the null byte var _root = ' ' + String.fromCharCode( _floor( _random() * 10 ) % 31 + 1 ) + '$'; /** * Generate a pseudo-random string (with a common prefix) to be used as an * object key * * The returned key is unique so long as Math.{random,floor} are reliable. * This will be true so long as (1) the runtime provides a reliable * implementation and (2) Math.{floor,random} have not been overwritten at * the time that this module is loaded. This module stores an internal * reference to this methods, so malicious code loaded after this module * will not be able to compromise the return value. * * Note that the returned string is not wholly random: a common prefix is * used to ensure that collisions with other keys on objects is highly * unlikely; you should not rely on this behavior, though, as it is an * implementation detail that may change in the future. * * @return {string} pseudo-random string with common prefix */ function FallbackSymbol() { if ( !( this instanceof FallbackSymbol ) ) { return new FallbackSymbol(); } this.___$$id$$ = ( _root + _floor( _random() * 1e8 ) ); } FallbackSymbol.prototype = { /** * Return random identifier * * This is convenient, as it allows us to both treat the symbol as an * object of type FallbackSymbol and use the symbol as a key (since * doing so will automatically call this method). * * @return {string} random identifier */ toString: function() { return this.___$$id$$; } }; module.exports = FallbackSymbol; } )( module['util/symbol/FallbackSymbol'] = {}, '.' ); /** util/Global **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = 'util'; /** * Global scope handling * * Copyright (C) 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ // retrieve global scope; works with ES5 strict mode (0,eval)( 'var _the_global=this' ); // prototype to allow us to augment the global scope for our own purposes // without polluting the global scope function _G() {} _G.prototype = _the_global; /** * Provides access to and augmentation of global variables * * This provides a static method to consistently provide access to the * object representing the global scope, regardless of environment. Through * instantiation, its API permits augmenting a local object whose prototype * is the global scope, providing alternatives to variables that do not * exist. */ function Global() { // allows omitting `new` keyword, consistent with ease.js style if ( !( this instanceof Global ) ) { return new Global(); } // do not pollute the global scope (previously, _the_global was used as // the prototype for a new object to take advantage of native overrides, // but unfortunately IE<=8 did not support this and always returned // undefined values from the prototype). this._alt = {}; } /** * Provides consistent access to the global scope through all ECMAScript * versions, for any root variable name, and works with ES5 strict mode. * * As an example, Node.js exposes the variable `root` to represent global * scope, but browsers expose `window`. Further, ES5 strict mode will * provide an error when checking whether `typeof SomeGlobalVar === * 'undefined'`. * * @return {Object} global object */ Global.expose = function() { return _the_global; }; Global.prototype = { /** * Provide a value for the provided global variable name if it is not * defined * * A function returning the value to assign to NAME should be provided, * ensuring that the alternative is never even evaluated unless it is * needed. * * The global scope will not be polluted with this alternative; * consequently, you must access the value using the `get` method. * * @param {string} name global variable name * @param {function()} f function returning value to assign * * @return {Global} self */ provideAlt: function( name, f ) { if ( ( _the_global[ name ] !== undefined ) || ( this._alt[ name ] !== undefined ) ) { return; } this._alt[ name ] = f(); return this; }, /** * Retrieve global value or provided alternative * * This will take into account values provided via `provideAlt`; if no * alternative was provided, the request will be deleagated to the * global variable NAME, which may or may not be undefined. * * No error will be thrown if NAME is not globally defined. * * @param {string} name global variable name * * @return {*} value associated with global variable NAME or * its provided alternative */ get: function( name ) { return ( this._alt[ name ] !== undefined ) ? this._alt[ name ] : _the_global[ name ]; } }; module.exports = Global; } )( module['util/Global'] = {}, '.' ); /** util/Symbol **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = 'util'; /** * Forward-compatible subset of ES6 Symbol * * Copyright (C) 2010, 2011, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This is *not* intended to be a complete implementation; it merely * performs what is needed for ease.js, preferring the benefits of the ES6 * Symbol implementation while falling back to sane ES5 and ES3 options. */ // to be used if there is no global Symbol available var FallbackSymbol = require( './symbol/FallbackSymbol' ); var _root = require( './Global' ).expose(); module.exports = _root.Symbol || FallbackSymbol; } )( module['util/Symbol'] = {}, '.' ); /** prop_parser **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * Property keyword parser module * * Copyright (C) 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * Known (permitted) keywords * @type {Object.} */ var _keywords = { 'public': 1, 'protected': 1<<1, 'private': 1<<2, 'static': 1<<3, 'abstract': 1<<4, 'const': 1<<5, 'virtual': 1<<6, 'override': 1<<7, 'proxy': 1<<8, 'weak': 1<<9 }; /** * Keyword masks for conveniently checking the keyword bitfield * @type {Object.} */ var _kmasks = { amods: _keywords[ 'public' ] | _keywords[ 'protected' ] | _keywords[ 'private' ], 'virtual': _keywords[ 'abstract' ] | _keywords[ 'virtual' ] }; // expose magic values exports.kvals = _keywords; exports.kmasks = _kmasks; /** * Parses property keywords * * @param {string} prop property string, which may contain keywords * * @return {{name: string, keywords: Object.}} */ exports.parseKeywords = function ( prop ) { var name = prop, keywords = [], bitwords = 0x00, keyword_obj = {}; prop = ''+( prop ); // the keywords are all words, except for the last, which is the // property name if ( ( keywords = prop.split( /\s+/ ) ).length !== 1 ) { name = keywords.pop(); var i = keywords.length; while ( i-- ) { var keyword = keywords[ i ], kval = _keywords[ keyword ]; // ensure the keyword is recognized if ( !kval ) { throw Error( "Unexpected keyword for '" + name + "': " + keyword ); } // ease-of-access keyword_obj[ keyword ] = true; // permits quick and concise checks bitwords |= kval; } } // members with an underscore prefix are implicitly private, unless an // access modifier is explicitly provided; double-underscore is ingored, // as they denote special members that do not become part of the // prototype and are reserved by ease.js if ( ( name.match( /^_[^_]/ ) && !( bitwords & _kmasks.amods ) ) ) { keyword_obj[ 'private' ] = true; bitwords |= _keywords[ 'private' ]; } return { name: name, keywords: keyword_obj, bitwords: bitwords }; } } )( module['prop_parser'] = {}, '.' ); /** util **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * Contains utilities functions shared by modules * * Copyright (C) 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ var propParseKeywords = require( './prop_parser' ).parseKeywords; /** * Whether we can actually define properties, or we need to fall back * * This check actually attempts to set a property and fails if there's an error. * This is needed because IE8 has a broken implementation, yet still defines * Object.defineProperty for use with DOM elements. Just another day in the life * of a web developer. * * This test is only performed once, when the module is first loaded. Don't * expect a performance hit from it. * * @type {boolean} */ var can_define_prop = ( function() { if ( typeof Object.defineProperty === 'function' ) { try { // perform test, primarily for IE8 Object.defineProperty( {}, 'x', {} ); return true; } catch ( e ) {} } return false; } )(); exports.Global = require( './util/Global' ); /** * Freezes an object if freezing is supported * * @param {Object} obj object to freeze * * @return {Object} object passed to function */ exports.freeze = ( typeof Object.freeze === 'function' ) ? Object.freeze : function( obj ) { return; } ; /** * Gets/sets whether the system needs to fall back to defining properties in a * normal manner when use of Object.defineProperty() is requested * * This will be set by default if the JS engine does not support the * Object.defineProperty method from ECMAScript 5. * * @param {boolean=} val value, if used as setter * * @return {boolean|Object} current value if getter, self if setter */ exports.definePropertyFallback = function( val ) { if ( val === undefined ) { return !can_define_prop; } can_define_prop = !val; exports.defineSecureProp = getDefineSecureProp(); return exports; }; /** * Attempts to define a non-enumerable, non-writable and non-configurable * property on the given object * * If the operation is unsupported, a normal property will be set. * * @param {Object} obj object to set property on * @param {string} prop name of property to set * @param {*} value value to set * * @return {undefined} */ exports.defineSecureProp = getDefineSecureProp(); /** * Clones an object * * @param {*} data object to clone * @param {boolean=} deep perform deep clone (defaults to shallow) * * @return {*} cloned object * * Closure Compiler ignores typeof checks and is thusly confused: * @suppress {checkTypes} */ exports.clone = function clone( data, deep ) { deep = !!deep; if ( data instanceof Array ) { if ( !deep ) { // return a copy of the array return data.slice( 0 ); } // if we're performing a deep clone, we have to loop through each of the // elements of the array and clone them var ret = []; for ( var i = 0, len = data.length; i < len; i++ ) { // clone this element ret.push( clone( data[ i ], deep ) ); } return ret; } else if ( typeof data === 'function' ) { // It is pointless to clone a function. Even if we did clone those that // support toSource(), they'd still do the same damn thing. return data; } // explicitly testing with instanceof will ensure we're actually testing an // object, not something that may be misinterpreted as one (e.g. null) else if ( data instanceof Object ) { var newobj = {}, hasOwn = Object.prototype.hasOwnProperty; // copy data to the new object for ( var prop in data ) { if ( hasOwn.call( data, prop ) ) { newobj[ prop ] = ( deep ) ? clone( data[ prop ] ) : data[ prop ] ; } } return newobj; } // primitive type; cloning unnecessary return data; }; /** * Copies properties from one object to another * * This method is designed to support very basic object extensions. The * destination argument is first to allow extending an object without using the * full-blown class system. * * If a deep copy is not performed, all values will be copied by reference. * * @param {Object} dest destination object * @param {Object} src source object * @param {boolean} deep perform deep copy (slower) * * @return {Object} dest */ exports.copyTo = function( dest, src, deep ) { deep = !!deep; var get, set, data; // sanity check if ( !( dest instanceof Object ) || !( src instanceof Object ) ) { throw TypeError( "Must provide both source and destination objects" ); } // slower; supports getters/setters if ( can_define_prop ) { for ( var prop in src ) { data = Object.getOwnPropertyDescriptor( src, prop ); if ( data.get || data.set ) { // Define the property the slower way (only needed for // getters/setters). We don't have to worry about cloning in // this case, since getters/setters are methods. Object.defineProperty( dest, prop, data ); } else { // normal copy; cloned if deep, otherwise by reference dest[ prop ] = ( deep ) ? exports.clone( src[ prop ], true ) : src[ prop ] ; } } } // quick (keep if statement out of the loop) else { for ( var prop in src ) { // normal copy; cloned if deep, otherwise by reference dest[ prop ] = ( deep ) ? exports.clone( src[ prop ], true ) : src[ prop ] ; } } // return dest for convenience (and to feel useful about ourselves) return dest; }; /** * Throw an exception * * Yes, this function has purpose; see where it's used. * * @param {Error} e exception to throw */ function _throw( e ) { throw e; } /** * Parses object properties to determine how they should be interpreted in an * Object Oriented manner * * @param {!Object} data properties with names as the key * * @param {!{each,property,method,getset,keywordParser}} options * parser options and callbacks * * @return undefined */ exports.propParse = function( data, options, context ) { // todo: profile; function calls are more expensive than if statements, so // it's probably a better idea not to use fvoid var fvoid = function() {}, callbackEach = options.each || undefined, callbackProp = options.property || fvoid, callbackMethod = options.method || fvoid, callbackGetSet = options.getset || fvoid, keywordParser = options.keywordParser || propParseKeywords, throwf = options._throw || _throw, hasOwn = Object.prototype.hasOwnProperty, parse_data = {}, name = '', keywords = {}, value = null, getter = false, setter = false; // for each of the given properties, determine what type of property we're // dealing with (in the classic OO sense) for ( var prop in data ) { // ignore properties of instance prototypes if ( !( hasOwn.call( data, prop ) ) ) { continue; } // retrieve getters/setters, if supported if ( can_define_prop ) { var prop_desc = Object.getOwnPropertyDescriptor( data, prop ); getter = prop_desc.get; setter = prop_desc.set; } // do not attempt to retrieve the value if a getter is defined (as that // would then call the getter) value = ( typeof getter === 'function' ) ? undefined : data[ prop ]; parse_data = keywordParser( prop ) || {}; name = parse_data.name || prop; keywords = parse_data.keywords || {}; // note the exception for abstract overrides if ( options.assumeAbstract || ( keywords[ 'abstract' ] && !( keywords[ 'override' ] ) ) ) { // may not be set if assumeAbstract is given keywords[ 'abstract' ] = true; if ( !( value instanceof Array ) ) { throwf( TypeError( "Missing parameter list for abstract method: " + name ) ); } verifyAbstractNames( throwf, name, value ); value = exports.createAbstractMethod.apply( this, value ); } // if an 'each' callback was provided, pass the data before parsing it if ( callbackEach ) { callbackEach.call( context, name, value, keywords ); } // getter/setter if ( getter || setter ) { callbackGetSet.call( context, name, getter, setter, keywords ); } // method else if ( ( typeof value === 'function' ) || ( keywords[ 'proxy' ] ) ) { callbackMethod.call( context, name, value, exports.isAbstractMethod( value ), keywords ); } // simple property else { callbackProp.call( context, name, value, keywords ); } } }; /** * Only permit valid names for parameter list * * In the future, we may add additional functionality, so it's important to * restrict this as much as possible for the time being. * * @param {function(Error)} throwf function to call with error * * @param {string} name name of abstract member (for error) * @param {Object} params parameter list to check * * @return {undefined} */ function verifyAbstractNames( throwf, name, params ) { var i = params.length; while ( i-- ) { if ( params[ i ].match( /^[a-z_][a-z0-9_]*$/i ) === null ) { throwf( SyntaxError( "Member " + name + " contains invalid parameter '" + params[ i ] + "'" ) ); } } } /** * Creates an abstract method * * Abstract methods must be implemented by a subclass and cannot be called * directly. If a class contains a single abstract method, the class itself is * considered to be abstract and cannot be instantiated. It may only be * extended. * * @param {...string} def function definition that concrete * implementations must follow * * @return {function()} */ exports.createAbstractMethod = function( def ) { var dfn = [], i = arguments.length; while ( i-- ) dfn[ i ] = arguments[ i ]; var method = function() { throw new Error( "Cannot call abstract method" ); }; exports.defineSecureProp( method, 'abstractFlag', true ); exports.defineSecureProp( method, 'definition', dfn ); exports.defineSecureProp( method, '__length', arguments.length ); return method; }; /** * Determines if the given function is an abstract method * * @param {function()} func function to inspect * * @return {boolean} true if function is an abstract method, otherwise false * * @suppress {checkTypes} */ exports.isAbstractMethod = function( func ) { return ( ( typeof func === 'function') && ( func.abstractFlag === true ) ) ? true : false ; }; /** * Shrinks an array, removing undefined elements * * Pushes all items onto a new array, removing undefined elements. This ensures * that the length of the array represents correctly the number of elements in * the array. * * @param {Array} items array to shrink * * @return {Array} shrunken array */ exports.arrayShrink = function( items ) { // copy the methods into a new array by pushing them onto it, to ensure // the length property of the array will work properly var arr_new = []; for ( var i = 0, len = items.length; i < len; i++ ) { var item = items[ i ]; if ( item === undefined ) { continue; } arr_new.push( item ); } return arr_new; }; /** * Uses Object.getOwnPropertyDescriptor if available, otherwise provides our own * implementation to fall back on */ exports.getOwnPropertyDescriptor = ( can_define_prop && Object.getOwnPropertyDescriptor ) || /** * If the environment does not support retrieving property descriptors * (ES5), then the following will be true: * - get/set will always be undefined * - writable, enumerable and configurable will always be true * - value will be the value of the requested property on the given object * * @param {!Object} obj object to check property on * @param {string} prop property to retrieve descriptor for * * @return {Object|undefined} descriptor for requested property, if found */ function( obj, prop ) { if ( !Object.prototype.hasOwnProperty.call( obj, prop ) ) { return undefined; } // fallback response return { get: undefined, set: undefined, writable: true, enumerable: true, configurable: true, value: obj[ prop ] }; }; /** * Returns prototype of object, or undefined if unsupported */ exports.getPrototypeOf = Object.getPrototypeOf || function() { return undefined; }; /** * Travels down the prototype chain of the given object in search of the * requested property and returns its descriptor * * This operates as Object.getOwnPropertyDescriptor(), except that it traverses * the prototype chain. For environments that do not support __proto__, it will * not traverse the prototype chain and essentially serve as an alias for * getOwnPropertyDescriptor(). * * This method has the option to ignore the base prototype. This is useful to, * for example, not catch properties like Object.prototype.toString() when * searching for 'toString' on an object. * * @param {Object} obj object to check property on * @param {string} prop property to retrieve descriptor for * @param {boolean} nobase whether to ignore the base prototype * * @return {Object} descriptor for requested property or undefined if not found */ exports.getPropertyDescriptor = function( obj, prop, nobase ) { // false by default nobase = !!nobase; // note that this uses util's function, not Object's var desc = exports.getOwnPropertyDescriptor( obj, prop ), next = exports.getPrototypeOf( obj ); // if we didn't find a descriptor and a prototype is available, recurse down // the prototype chain, ensuring that the next prototype has a prototype if // the base is to be excluded if ( !desc && next && ( !nobase || exports.getPrototypeOf( next ) ) ) { return exports.getPropertyDescriptor( next, prop, nobase ); } // return the descriptor or undefined if no prototype is available return desc; }; /** * Indicates whether or not the getPropertyDescriptor method is capable of * traversing the prototype chain */ exports.defineSecureProp( exports.getPropertyDescriptor, 'canTraverse', ( Object.getPrototypeOf ) ? true : false ); /** * Appropriately returns defineSecureProp implementation to avoid check on * each invocation * * @return {function( Object, string, * )} */ function getDefineSecureProp() { // falls back to simply defining a normal property var fallback = function( obj, prop, value ) { obj[ prop ] = value; }; if ( !can_define_prop ) { return fallback; } else { // uses ECMAScript 5's Object.defineProperty() method return function( obj, prop, value ) { Object.defineProperty( obj, prop, { value: value, enumerable: false, writable: false, configurable: false } ); }; } } } )( module['util'] = {}, '.' ); /** warn/Warning **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = 'warn'; /** * Warning prototype * * Copyright (C) 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * Permits wrapping an exception as a warning * * Warnings are handled differently by the system, depending on the warning * level that has been set. * * @param {Error} e exception (error) to wrap * * @return {Warning} new warning instance * * @constructor */ function Warning( e ) { // allow instantiation without use of 'new' keyword if ( !( this instanceof Warning ) ) { return new Warning( e ); } // ensure we're wrapping an exception if ( !( e instanceof Error ) ) { throw TypeError( "Must provide exception to wrap" ); } Error.prototype.constructor.call( this, e.message ); // copy over the message for convenience this.message = e.message; this.name = 'Warning'; this._error = e; this.stack = e.stack && e.stack.replace( /^.*?\n+/, this.name + ': ' + this.message + "\n" ); }; // ensures the closest compatibility...just be careful not to modify Warning's // prototype Warning.prototype = Error(); Warning.prototype.constructor = Warning; Warning.prototype.name = 'Warning'; /** * Return the error wrapped by the warning * * @return {Error} wrapped error */ Warning.prototype.getError = function() { return this._error; }; module.exports = Warning; } )( module['warn/Warning'] = {}, '.' ); /** warn/DismissiveHandler **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = 'warn'; /** * Dismissive warning handler * * Copyright (C) 2010, 2011, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * Warning handler that dismisses (ignores) all warnings * * This is useful in a production environment. */ function DismissiveHandler() { if ( !( this instanceof DismissiveHandler ) ) { return new DismissiveHandler(); } } DismissiveHandler.prototype = { /** * Handle a warning * * @param {Warning} warning warning to handle * @return {undefined} */ handle: function( warning ) { // intentionally do nothing } } module.exports = DismissiveHandler; } )( module['warn/DismissiveHandler'] = {}, '.' ); /** warn/LogHandler **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = 'warn'; /** * Logging warning handler * * Copyright (C) 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * Warning handler that logs all warnings to a console * * @param {Object} console console with a warn or log method */ function LogHandler( console ) { if ( !( this instanceof LogHandler ) ) { return new LogHandler( console ); } this._console = console || {}; } LogHandler.prototype = { /** * Handle a warning * * Will attempt to log using console.warn(), falling back to * console.log() if necessary and aborting entirely if neither is * available. * * This is useful as a default option to bring problems to the * developer's attention without affecting the control flow of the * software. * * @param {Warning} warning warning to handle * @return {undefined} */ handle: function( warning ) { var dest = this._console.warn || this._console.log; dest && dest.call( this._console, 'Warning: ' + warning.message ); } } module.exports = LogHandler; } )( module['warn/LogHandler'] = {}, '.' ); /** warn/ThrowHandler **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = 'warn'; /** * Throwing warning handler * * Copyright (C) 2010, 2011, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * Warning handler that throws all warnings as exceptions */ function ThrowHandler() { if ( !( this instanceof ThrowHandler ) ) { return new ThrowHandler(); } } ThrowHandler.prototype = { /** * Handle a warning * * Throws the error associated with the warning. * * This handler is useful for development and will ensure that problems * are brought to the attention of the developer. * * @param {Warning} warning warning to handle * @return {undefined} */ handle: function( warning ) { throw warning.getError(); } } module.exports = ThrowHandler; } )( module['warn/ThrowHandler'] = {}, '.' ); /** warn **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * ease.js warning system * * Copyright (C) 2011, 2012, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ module.exports = { Warning: require( './warn/Warning' ), DismissiveHandler: require( './warn/DismissiveHandler' ), LogHandler: require( './warn/LogHandler' ), ThrowHandler: require( './warn/ThrowHandler' ) }; } )( module['warn'] = {}, '.' ); /** ClassBuilder **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * Handles building of classes * * Copyright (C) 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * TODO: This module is currently being tested /indirectly/ by the class * tests. This is because of a refactoring. All of this logic used to * be part of the class module. Test this module directly, but keep * the existing class tests in tact for a higher-level test. */ var util = require( './util' ), Warning = require( './warn' ).Warning, Symbol = require( './util/Symbol' ), hasOwn = Object.prototype.hasOwnProperty, /** * IE contains a nasty enumeration "bug" (poor implementation) that makes * toString unenumerable. This means that, if you do obj.toString = foo, * toString will NOT show up in `for` or hasOwnProperty(). This is a problem. * * This test will determine if this poor implementation exists. */ enum_bug = ( Object.prototype.propertyIsEnumerable.call( { toString: function() {} }, 'toString' ) === false ) ? true : false, /** * Hash of reserved members * * These methods cannot be defined in the class. They are for internal use * only. We must check both properties and methods to ensure that neither is * defined. * * @type {Object.} */ reserved_members = { '__initProps': true, 'constructor': true }, /** * Hash of methods that must be public * * Notice that this is a list of /methods/, not members, because this check * is performed only for methods. This is for performance reasons. We do not * have a situation where we will want to check for properties as well. * * @type {Object.} */ public_methods = { '__construct': true, '__mixin': true, 'toString': true, '__toString': true }, /** * Symbol used to encapsulate internal data * * Note that this is intentionally generated *outside* the ClassBuilder * instance; this ensures that it is properly encapsulated and will not * be exposed on the Classbuilder instance (which would defeat the * purpose). */ _priv = Symbol() ; /** * Initializes class builder with given member builder * * The 'new' keyword is not required when instantiating this constructor. * * @param {Object} member_builder member builder * * @param {VisibilityObjectFactory} visibility_factory visibility object * generator * * @constructor */ module.exports = exports = function ClassBuilder( warn_handler, member_builder, visibility_factory ) { // allow ommitting the 'new' keyword if ( !( this instanceof exports ) ) { // module.exports for Closure Compiler return new module.exports( warn_handler, member_builder, visibility_factory ); } /** * Determines how warnings should be handled * @type {WarningHandler} */ this._warnHandler = warn_handler; /** * Used for building class members * @type {Object} */ this._memberBuilder = member_builder; /** * Generates visibility object * @type {VisibilityObjectFactory} */ this._visFactory = visibility_factory; /** * Class id counter, to be increment on each new definition * @type {number} */ this._classId = 0; /** * Instance id counter, to be incremented on each new instance * @type {number} */ this._instanceId = 0; /** * A flag to let the system know that we are currently attempting to access * a static property from within a method. This means that the caller should * be given access to additional levels of visibility. * * @type {boolean} */ this._spropInternal = false; }; /** * Default class implementation * * @return undefined */ exports.ClassBase = function Class() {}; // the base class has the class identifier 0 util.defineSecureProp( exports.ClassBase, '__cid', 0 ); /** * Default static property method * * This simply returns undefined, signifying that the property was not found. * * @param {string} prop requested property * * @return {undefined} */ exports.ClassBase.$ = function( prop, val ) { if ( val !== undefined ) { throw ReferenceError( "Cannot set value of undeclared static property '" + prop + "'" ); } return undefined; }; /** * Returns a hash of the reserved members * * The returned object is a copy of the original. It cannot be used to modify * the internal list of reserved members. * * @return {Object.} reserved members */ exports.getReservedMembers = function() { // return a copy of the reserved members return util.clone( reserved_members, true ); }; /** * Returns a hash of the forced-public methods * * The returned object is a copy of the original. It cannot be used to modify * the internal list of reserved members. * * @return {Object.} forced-public methods */ exports.getForcedPublicMethods = function() { return util.clone( public_methods, true ); }; /** * Returns reference to metadata for the requested class * * Since a reference is returned (rather than a copy), the returned object can * be modified to alter the metadata. * * @param {Function|Object} cls class from which to retrieve metadata * * @return {__class_meta} or null if unavailable */ exports.getMeta = function( cls ) { return ( cls[ _priv ] || {} ).meta || null; } /** * Allow OBJ to assume an identity as a class * * This is useful to use objects in situations where classes are expected, * as it eliminates the need for handling of special cases. * * This is intended for internal use---there are no guarantees as to what * methods ease.js may expect that a class-like object incorporate. That * guarantee may exist in the future, but until then, stay away. * * @param {Object} obj object to masquerade as an ease.js class * * @return {Object} OBJ */ exports.masquerade = function( obj ) { // XXX: this is duplicated; abstract util.defineSecureProp( obj, _priv, {} ); createMeta( obj, exports.ClassBase ); return obj; }; /** * Determines if the class is an instance of the given type * * The given type can be a class, interface, trait or any other type of object. * It may be used in place of the 'instanceof' operator and contains additional * enhancements that the operator is unable to provide due to prototypal * restrictions. * * @param {Object} type expected type * @param {Object} instance instance to check * * @return {boolean} true if instance is an instance of type, otherwise false */ exports.isInstanceOf = function( type, instance ) { var meta, implemented, i; if ( !( type && instance ) ) { return false; } // defer check to type, falling back to a more primitive check; this // also allows extending ease.js' type system return !!( type.__isInstanceOf || _instChk )( type, instance ); } /** * Wrapper around ECMAScript instanceof check * * This will not throw an error if TYPE is not a function. * * Note that a try/catch is used instead of checking first to see if TYPE is * a function; this is due to the implementation of, notably, IE, which * allows instanceof to be used on some DOM objects with typeof `object'. * These same objects have typeof `function' in other browsers. * * @param {*} type constructor to check against * @param {Object} instance instance to examine * * @return {boolean} whether INSTANCE is an instance of TYPE */ function _instChk( type, instance ) { try { // check prototype chain (will throw an error if type is not a // constructor) if ( instance instanceof type ) { return true; } } catch ( e ) {} return false; } /** * Mimics class inheritance * * This method will mimic inheritance by setting up the prototype with the * provided base class (or, by default, Class) and copying the additional * properties atop of it. * * The class to inherit from (the first argument) is optional. If omitted, the * first argument will be considered to be the properties list. * * @param {Function|Object} _ parent or definition object * @param {Object=} __ definition object if parent was provided * * @return {Function} extended class */ exports.prototype.build = function extend( _, __ ) { var build = this; var a = arguments, an = a.length, props = ( ( an > 0 ) ? a[ an - 1 ] : 0 ) || {}, base = ( ( an > 1 ) ? a[ an - 2 ] : 0 ) || exports.ClassBase, prototype = this._getBase( base ), cname = '', autoa = false, prop_init = this._memberBuilder.initMembers(), members = this._memberBuilder.initMembers( prototype ), static_members = { methods: this._memberBuilder.initMembers(), props: this._memberBuilder.initMembers() }, // constructor may be different than base pmeta = exports.getMeta( prototype.constructor ) || {}, abstract_methods = util.clone( pmeta.abstractMethods ) || { __length: 0 }, virtual_members = util.clone( pmeta.virtualMembers ) || {} ; // prevent extending final classes (TODO: abstract this check) if ( base.___$$final$$ === true ) { throw Error( "Cannot extend final class " + ( base[ _priv ].meta.name || '(anonymous)' ) ); } // grab the name, if one was provided if ( cname = props.__name ) { // we no longer need it delete props.__name; } // gobble up auto-abstract flag if present if ( ( autoa = props.___$$auto$abstract$$ ) !== undefined ) { delete props.___$$auto$abstract$$; } // IE has problems with toString() if ( enum_bug ) { if ( props.toString !== Object.prototype.toString ) { props.__toString = props.toString; } } // increment class identifier this._classId++; // if we are inheriting from a prototype, we must make sure that all // properties initialized by the ctor are implicitly public; otherwise, // proxying will fail to take place // TODO: see Class.isA TODO if ( ( prototype[ _priv ] || {} ).vis === undefined ) { this._discoverProtoProps( prototype, prop_init ); } // build the various class components (XXX: this is temporary; needs // refactoring) try { this.buildMembers( props, this._classId, base, prop_init, { all: members, 'abstract': abstract_methods, 'static': static_members, 'virtual': virtual_members }, function( inst ) { return new_class.___$$svis$$; } ); } catch ( e ) { // intercept warnings /only/ if ( e instanceof Warning ) { this._warnHandler.handle( e ); } else { throw e; } } // reference to the parent prototype (for more experienced users) prototype.___$$parent$$ = base.prototype; // set up the new class var new_class = this.createCtor( cname, abstract_methods, members ); // closure to hold static initialization to be used later by subtypes this.initStaticVisibilityObj( new_class ); var _self = this; var staticInit = function( ctor, inheriting ) { _self.attachStatic( ctor, static_members, base, inheriting ); } staticInit( new_class, false ); this._attachPropInit( prototype, prop_init, members, new_class, this._classId ); new_class.prototype = prototype; new_class.prototype.constructor = new_class; new_class.___$$props$$ = prop_init; new_class.___$$methods$$ = members; new_class.___$$sinit$$ = staticInit; attachFlags( new_class, props ); validateAbstract( new_class, cname, abstract_methods, autoa ); // We reduce the overall cost of this definition by defining it on the // prototype rather than during instantiation. While this does increase the // amount of time it takes to access the property through the prototype // chain, it takes much more time to define the property in this manner. // Therefore, we can save a substantial amount of time by defining it on the // prototype rather than on each new instance via __initProps(). util.defineSecureProp( prototype, '__self', new_class.___$$svis$$ ); // create internal metadata for the new class var meta = createMeta( new_class, base, pmeta ); meta.abstractMethods = abstract_methods; meta.virtualMembers = virtual_members; meta.name = cname; attachAbstract( new_class, abstract_methods ); attachId( new_class, this._classId ); // returns a new instance of the class without invoking the constructor // (intended for use in prototype chains) new_class.asPrototype = function() { new_class[ _priv ].extending = true; var inst = new new_class(); new_class[ _priv ].extending = false; return inst; }; return new_class; }; exports.prototype._getBase = function( base ) { var type = ( typeof base ); switch ( type ) { // constructor (we could also check to ensure that the return value of // the constructor is an object, but that is not our concern) case 'function': return ( base[ _priv ] ) ? base.asPrototype() : new base(); // we can use objects as the prototype directly case 'object': return base; } // scalars throw TypeError( 'Must extend from Class, constructor or object' ); }; /** * Discovers public properties on the given object and create an associated * property * * This allows inheriting from a prototype that uses properties by ensuring * that we properly proxy to that property. Otherwise, assigning the value * on the private visibilit object would mask the underlying value rather * than modifying it, leading to an inconsistent and incorrect state. * * This assumes that the object has already been initialized with all the * properties. This may not be the case if the prototype constructor does * not do so, in which case there is nothing we can do. * * This does not recurse on the prototype chian. * * For a more detailed description of this issue, see the interoperability * test case for classes. * * @param {Object} obj object from which to gather properties * @param {Object} prop_init destination property object * * @return {undefined} */ exports.prototype._discoverProtoProps = function( obj, prop_init ) { var hasOwn = Object.hasOwnProperty, pub = prop_init[ 'public' ]; for ( var field in obj ) { var value = obj[ field ]; // we are not interested in the objtype chain, nor are we // interested in functions (which are methods and need not be // proxied) if ( !( hasOwn.call( obj, field ) ) || typeof value === 'function' ) { continue; } this._memberBuilder.buildProp( prop_init, null, field, value, {} ); } }; exports.prototype.buildMembers = function buildMembers( props, class_id, base, prop_init, memberdest, staticInstLookup ) { var context = { _cb: this, // arguments prop_init: prop_init, class_id: class_id, base: base, staticInstLookup: staticInstLookup, defs: {}, // holds member builder state state: {}, // TODO: there does not seem to be tests for these guys; perhaps // this can be rectified with the reflection implementation members: memberdest.all, abstract_methods: memberdest['abstract'], static_members: memberdest['static'], virtual_members: memberdest['virtual'] }; // default member handlers for parser var handlers = { each: _parseEach, property: _parseProp, getset: _parseGetSet, method: _parseMethod }; // a custom parser may be provided to hook the below property parser; // this can be done to save time on post-processing, or alter the // default behavior of the parser if ( props.___$$parser$$ ) { // this isn't something that we actually want to parse var parser = props.___$$parser$$; delete props.___$$parser$$; // TODO: this is recreated every call! var hjoin = function( name, orig ) { handlers[ name ] = function() { var args = [], i = arguments.length; while ( i-- ) args[ i ] = arguments[ i ]; // invoke the custom handler with the original handler as // its last argument (which the custom handler may choose // not to invoke at all) args.push( orig ); parser[ name ].apply( context, args ); }; }; // this avoids a performance penalty unless the above property is // set parser.each && hjoin( 'each', handlers.each ); parser.property && hjoin( 'property', handlers.property ); parser.getset && hjoin( 'getset', handlers.getset ); parser.method && hjoin( 'method', handlers.method ); } // parse members and process accumulated member state util.propParse( props, handlers, context ); this._memberBuilder.end( context.state ); } function _parseEach( name, value, keywords ) { var defs = this.defs; // disallow use of our internal __initProps() method if ( reserved_members[ name ] === true ) { throw Error( name + " is reserved" ); } // if a member was defined multiple times in the same class // declaration, throw an error (unless the `weak' keyword is // provided, which exists to accomodate this situation) if ( hasOwn.call( defs, name ) && !( keywords['weak'] || defs[ name ].weak ) ) { throw Error( "Cannot redefine method '" + name + "' in same declaration" ); } // keep track of the definitions (only during class declaration) // to catch duplicates defs[ name ] = keywords; } function _parseProp( name, value, keywords ) { var dest = ( keywordStatic( keywords ) ) ? this.static_members.props : this.prop_init; // build a new property, passing in the other members to compare // against for preventing nonsensical overrides this._cb._memberBuilder.buildProp( dest, null, name, value, keywords, this.base ); } function _parseGetSet( name, get, set, keywords ) { var dest = ( keywordStatic( keywords ) ) ? this.static_members.methods : this.members, is_static = keywordStatic( keywords ), instLookup = ( ( is_static ) ? this.staticInstLookup : exports.getMethodInstance ); this._cb._memberBuilder.buildGetterSetter( dest, null, name, get, set, keywords, instLookup, this.class_id, this.base ); } function _parseMethod( name, func, is_abstract, keywords ) { var is_static = keywordStatic( keywords ), dest = ( is_static ) ? this.static_members.methods : this.members, instLookup = ( is_static ) ? this.staticInstLookup : exports.getMethodInstance ; // constructor check if ( public_methods[ name ] === true ) { if ( keywords[ 'protected' ] || keywords[ 'private' ] ) { throw TypeError( name + " must be public" ); } } var used = this._cb._memberBuilder.buildMethod( dest, null, name, func, keywords, instLookup, this.class_id, this.base, this.state ); // do nothing more if we didn't end up using this definition // (this may be the case, for example, with weak members) if ( !used ) { return; } // note the concrete method check; this ensures that weak // abstract methods will not count if a concrete method of the // smae name has already been seen if ( is_abstract ) { this.abstract_methods[ name ] = true; this.abstract_methods.__length++; } else if ( ( hasOwn.call( this.abstract_methods, name ) ) && ( is_abstract === false ) ) { // if this was a concrete method, then it should no longer // be marked as abstract delete this.abstract_methods[ name ]; this.abstract_methods.__length--; } if ( keywords['virtual'] ) { this.virtual_members[ name ] = true; } } /** * Validates abstract class requirements * * We permit an `auto' flag for internal use only that will cause the * abstract flag to be automatically set if the class should be marked as * abstract, instead of throwing an error; this should be used sparingly and * never exposed via a public API (for explicit use), as it goes against the * self-documentation philosophy. * * @param {function()} ctor class * @param {string} cname class name * @param {{__length}} abstract_methods object containing abstract methods * @param {boolean} auto automatically flag as abstract * * @return {undefined} */ function validateAbstract( ctor, cname, abstract_methods, auto ) { if ( ctor.___$$abstract$$ ) { if ( !auto && ( abstract_methods.__length === 0 ) ) { throw TypeError( "Class " + ( cname || "(anonymous)" ) + " was declared as " + "abstract, but contains no abstract members" ); } } else if ( abstract_methods.__length > 0 ) { if ( auto ) { ctor.___$$abstract$$ = true; return; } throw TypeError( "Class " + ( cname || "(anonymous)" ) + " contains abstract " + "members and must therefore be declared abstract" ); } } /** * Creates the constructor for a new class * * This constructor will call the __constructor method for concrete classes * and throw an exception for abstract classes (to prevent instantiation). * * @param {string} cname class name (may be empty) * @param {Array.} abstract_methods list of abstract methods * @param {Object} members class members * * @return {Function} constructor */ exports.prototype.createCtor = function( cname, abstract_methods, members ) { var new_class; if ( abstract_methods.__length === 0 ) { new_class = this.createConcreteCtor( cname, members ); } else { new_class = this.createAbstractCtor( cname ); } util.defineSecureProp( new_class, _priv, {} ); return new_class; } /** * Creates the constructor for a new concrete class * * This constructor will call the __constructor method of the class, if * available. * * @param {string} cname class name (may be empty) * @param {Object} members class members * * @return {function()} constructor */ exports.prototype.createConcreteCtor = function( cname, members ) { var args = null, _self = this; /** * Constructor function to be returned * * The name is set to ClassInstance because some debuggers (e.g. v8) will * show the name of this function for constructor instances rather than * invoking the toString() method * * @constructor * * Suppressing due to complaints for using __initProps * @suppress {checkTypes} */ function ClassInstance() { if ( !( this instanceof ClassInstance ) ) { // store arguments to be passed to constructor and // instantiate new object args = arguments; return new ClassInstance(); } initInstance( this ); this.__initProps(); // If we're extending, we don't actually want to invoke any class // construction logic. The above is sufficient to use this class in a // prototype, so stop here. if ( ClassInstance[ _priv ].extending ) { return; } // generate and store unique instance id attachInstanceId( this, ++_self._instanceId ); // FIXME: this is a bit of a kluge for determining whether the ctor // should be invoked before a child prector... var haspre = ( typeof this.___$$ctor$pre$$ === 'function' ); if ( haspre && ClassInstance.prototype.hasOwnProperty( '___$$ctor$pre$$' ) ) { // FIXME: we're exposing _priv to something that can be // malicously set by the user this.___$$ctor$pre$$( _priv ); haspre = false; } // call the constructor, if one was provided if ( typeof this.__construct === 'function' ) { // note that since 'this' refers to the new class (even // subtypes), and since we're using apply with 'this', the // constructor will be applied to subtypes without a problem this.__construct.apply( this, ( args || arguments ) ); } // FIXME: see above if ( haspre ) { this.___$$ctor$pre$$( _priv ); } if ( typeof this.___$$ctor$post$$ === 'function' ) { this.___$$ctor$post$$( _priv ); } args = null; // attach any instance properties/methods (done after // constructor to ensure they are not overridden) attachInstanceOf( this ); // Provide a more intuitive string representation of the class // instance. If a toString() method was already supplied for us, // use that one instead. if ( !( hasOwn.call( members[ 'public' ], 'toString' ) ) ) { // use __toString if available (see enum_bug), otherwise use // our own defaults this.toString = members[ 'public' ].__toString || ( ( cname ) ? function() { return '#<' + cname + '>'; } : function() { return '#'; } ) ; } }; // provide a more intuitive string representation ClassInstance.toString = ( cname ) ? function() { return cname; } : function() { return '(Class)'; } ; return ClassInstance; } /** * Creates the constructor for a new abstract class * * Calling this constructor will cause an exception to be thrown, as abstract * classes cannot be instantiated. * * @param {string} cname class name (may be empty) * * @return {function()} constructor */ exports.prototype.createAbstractCtor = function( cname ) { var _self = this; var __abstract_self = function() { if ( !__abstract_self[ _priv ].extending ) { throw Error( "Abstract class " + ( cname || '(anonymous)' ) + " cannot be instantiated" ); } }; __abstract_self.toString = ( cname ) ? function() { return cname; } : function() { return '(AbstractClass)'; } ; return __abstract_self; } /** * Attaches __initProps() method to the class prototype * * The __initProps() method will initialize class properties for that instance, * ensuring that their data is not shared with other instances (this is not a * problem with primitive data types). * * The method will also initialize any parent properties (recursive) to ensure * that subtypes do not have a referencing issue, and subtype properties take * precedence over those of the parent. * * @param {Object} prototype prototype to attach method to * @param {Object} properties properties to initialize * * @param {{public: Object, protected: Object, private: Object}} members * * @param {function()} ctor class * @param {number} cid class id * * @return {undefined} */ exports.prototype._attachPropInit = function( prototype, properties, members, ctor, cid ) { var _self = this; util.defineSecureProp( prototype, '__initProps', function( inherit ) { // defaults to false inherit = !!inherit; var iid = this.__iid, parent = prototype.___$$parent$$, vis = this[ _priv ].vis; // first initialize the parent's properties, so that ours will overwrite // them var parent_init = parent && parent.__initProps; if ( typeof parent_init === 'function' ) { // call the parent prop_init, letting it know that it's been // inherited so that it does not initialize private members or // perform other unnecessary tasks parent_init.call( this, true ); } // this will return our property proxy, if supported by our environment, // otherwise just a normal object with everything merged in var inst_props = _self._visFactory.createPropProxy( this, vis, properties[ 'public' ] ); // Copies all public and protected members into inst_props and stores // private in a separate object, which adds inst_props to its prototype // chain and is returned. This is stored in a property referenced by the // class id, so that the private members can be swapped on each method // request, depending on calling context. var vis = vis[ cid ] = _self._visFactory.setup( inst_props, properties, members ); // provide a means to access the actual instance (rather than the // property/visibility object) internally (this will translate to // this.__inst from within a method), but only if we're on our final // object (not a parent) if ( !inherit ) { util.defineSecureProp( vis, '__inst', this ); } }); } /** * Determines if the given keywords should result in a static member * * A member will be considered static if the static or const keywords are given. * * @param {Object} keywords keywords to scan * * @return {boolean} true if to be static, otherwise false */ function keywordStatic( keywords ) { return ( keywords[ 'static' ] || keywords[ 'const' ] ) ? true : false ; } /** * Creates and populates the static visibility object * * @param {Function} ctor class * * @return {undefined} */ exports.prototype.initStaticVisibilityObj = function( ctor ) { var _self = this; /** * the object will simply be another layer in the prototype chain to * prevent protected/private members from being mixed in with the public * * @constructor */ var sobj = function() {}; sobj.prototype = ctor; var sobji = new sobj(); // override __self on the instance's visibility object, giving internal // methods access to the restricted static methods ctor.___$$svis$$ = sobji; // Override the class-level accessor method to allow the system to know we // are within a method. An internal flag is necessary, rather than using an // argument or binding, because those two options are exploitable. An // internal flag cannot be modified by conventional means. sobji.$ = function() { _self._spropInternal = true; var val = ctor.$.apply( ctor, arguments ); _self._spropInternal = false; return val; }; } /** * Attaches static members to a constructor (class) * * Static methods will be assigned to the constructor itself. Properties, on the * other hand, will be assigned to ctor.$. The reason for this is because JS * engines pre-ES5 support no means of sharing references to primitives. Static * properties of subtypes should share references to the static properties of * their parents. * * @param {function()} ctor class * @param {Object} members static members * @param {function()} base base class inheriting from * @param {boolean} inheriting true if inheriting static members, * otherwise false (setting own static * members) * * @return {undefined} */ exports.prototype.attachStatic = function( ctor, members, base, inheriting ) { var methods = members.methods, props = members.props, _self = this ; // "Inherit" the parent's static methods by running the parent's static // initialization method. It is important that we do this before anything, // because this will recursively inherit all members in order, permitting // overrides. var baseinit = base.___$$sinit$$; if ( baseinit ) { baseinit( ctor, true ); } // initialize static property if not yet defined if ( !inheriting ) { ctor.___$$sprops$$ = props; // provide a method to access static properties util.defineSecureProp( ctor, '$', function( prop, val ) { // we use hasOwnProperty to ensure that undefined values will not // cause us to continue checking the parent, thereby potentially // failing to set perfectly legal values var found = false, // Determine if we were invoked in the context of a class. If // so, use that. Otherwise, use ourself. context = ( this.___$$sprops$$ ) ? this : ctor, // We are in a subtype if the context does not match the // constructor. This works because, when invoked for the first // time, this method is not bound to the constructor. In such a // case, we default the context to the constructor and pass that // down the line to each recursive call. Therefore, recursive // calls to subtypes will have a context mismatch. in_subtype = ( context !== ctor ) ; // Attempt to locate the property. First, we check public. If not // available and we are internal (within a method), we can move on // to check other levels of visibility. `found` will contain the // visibility level the property was found in, or false. found = hasOwn.call( props[ 'public' ], prop ) && 'public'; if ( !found && _self._spropInternal ) { // Check for protected/private. We only check for private // properties if we are not currently checking the properties of // a subtype. This works because the context is passed to each // recursive call. found = hasOwn.call( props[ 'protected' ], prop ) && 'protected' || !in_subtype && hasOwn.call( props[ 'private' ], prop ) && 'private' ; } // if we don't own the property, let the parent(s) handle it if ( found === false ) { // TODO: This check is simple, but quick. It may be worth // setting a flag on the class during definition to specify if // it's extending from a non-class base. return ( base.__cid && base.$ || exports.ClassBase.$ ).apply( context, arguments ); } var prop_item = props[ found ][ prop ]; // if a value was provided, this method should be treated as a // setter rather than a getter (we *must* test using // arguments.length to ensure that setting to undefined works) if ( arguments.length > 1 ) { // if const, disallow modification if ( prop_item[ 1 ][ 'const' ] ) { throw TypeError( "Cannot modify constant property '" + prop + "'" ); } prop_item[ 0 ] = val; return context; } else { // return the value return prop_item[ 0 ]; } } ); } // copy over public static methods util.copyTo( ctor, methods[ 'public' ], true ); util.copyTo( ctor.___$$svis$$, methods[ 'protected' ], true ); // private methods should not be inherited by subtypes if ( !inheriting ) { util.copyTo( ctor.___$$svis$$, methods[ 'private' ], true ); } } /** * Initializes class metadata for the given class * * DYNMETA is used only when CPARENT's metadata are flagged as "lazy", * meaning that the data are not available at the time of its definition, * but are available now as DYNMETA. * * @param {Function} func class to initialize metadata for * @param {Function} cparent class parent * @param {?Object} dynmeta dynamic metadata * * @return {undefined} * * Suppressed due to warnings for use of __cid * @suppress {checkTypes} */ function createMeta( func, cparent, dynmeta ) { var id = func.__cid, parent_meta = ( cparent[ _priv ] ? exports.getMeta( cparent ) : undefined ); // copy the parent prototype's metadata if it exists (inherit metadata) if ( parent_meta ) { return func[ _priv ].meta = util.clone( // "lazy" metadata are unavailable at the time of definition parent_meta._lazy ? dynmeta : parent_meta, true ); } // create empty return func[ _priv ].meta = { implemented: [] }; } /** * Attaches an instance identifier to a class instance * * @param {Object} instance class instance * @param {number} iid instance id * * @return {undefined} */ function attachInstanceId( instance, iid ) { util.defineSecureProp( instance, '__iid', iid ); } /** * Initializes class instance * * This process will create the instance visibility object that will contain * private and protected members. The class instance is part of the prototype * chain. This will be passed to all methods when invoked, permitting them to * access the private and protected members while keeping them encapsulated. * * For each instance, there is always a base. The base will contain a proxy to * the public members on the instance itself. The base will also contain all * protected members. * * Atop the base object is a private member object, with the base as its * prototype. There exists a private member object for the instance itself and * one for each supertype. This is stored by the class id (cid) as the key. This * permits the private member object associated with the class of the method * call to be bound to that method. For example, if a parent method is called, * that call must be invoked in the context of the parent, so the private * members of the parent must be made available. * * The resulting structure looks something like this: * class_instance = { iid: { cid: {} } } * * @param {Object} instance instance to initialize * * @return {undefined} */ function initInstance( instance ) { /** @constructor */ var prot = function() {}; prot.prototype = instance; // initialize our *own* private metadata store; do not use the // prototype's util.defineSecureProp( instance, _priv, {} ); // add the visibility objects to the data object for this class instance instance[ _priv ].vis = new prot(); } /** * Attaches partially applied isInstanceOf() method to class instance * * @param {Object} instance class instance to attach method to * * @return {undefined} */ function attachInstanceOf( instance ) { var method = function( type ) { return module.exports.isInstanceOf( type, instance ); }; // TODO: To improve performance (defineSecureProp can be costly), simply // define a normal prop and freeze the class afterward. The class shouldn't // have any mutable methods. util.defineSecureProp( instance, 'isInstanceOf', method ); util.defineSecureProp( instance, 'isA', method ); } /** * Returns the instance object associated with the given method * * The instance object contains the protected members. This object can be passed * as the context when calling a method in order to give that method access to * those members. * * One level above the instance object on the prototype chain is the object * containing the private members. This is swappable, depending on the class id * associated with the provided method call. This allows methods that were not * overridden by the subtype to continue to use the private members of the * supertype. * * @param {function()} inst instance that the method is being called from * @param {number} cid class id * * @return {Object|null} instance object if found, otherwise null * * @suppress {checkTypes} */ exports.getMethodInstance = function( inst, cid ) { if ( inst === undefined ) { return null; } var iid = inst.__iid, priv = inst[ _priv ], data; return ( iid && priv && ( data = priv.vis ) ) ? data[ cid ] : null ; } /** * Attaches isAbstract() method to the class * * @param {Function} func function (class) to attach method to * @param {Array} methods abstract method names * * @return {undefined} */ function attachAbstract( func, methods ) { var is_abstract = ( methods.__length > 0 ) ? true: false; /** * Returns whether the class contains abstract methods (and is therefore * abstract) * * @return {boolean} true if class is abstract, otherwise false */ util.defineSecureProp( func, 'isAbstract', function() { return is_abstract; }); } /** * Attaches the unique id to the class and its prototype * * The unique identifier is used internally to match a class and its instances * with the class metadata. Exposing the id breaks encapsulation to a degree, * but is a lesser evil when compared to exposing all metadata. * * @param {function()} ctor constructor (class) to attach method to * @param {number} id id to assign * * @return {undefined} */ function attachId( ctor, id ) { util.defineSecureProp( ctor, '__cid', id ); util.defineSecureProp( ctor.prototype, '__cid', id ); } /** * Sets class flags * * @param {Function} ctor class to flag * @param {Object} props class properties * * @return {undefined} */ function attachFlags( ctor, props ) { ctor.___$$final$$ = !!( props.___$$final$$ ); ctor.___$$abstract$$ = !!( props.___$$abstract$$ ); // The properties are no longer needed. Set to undefined rather than delete // (v8 performance) props.___$$final$$ = props.___$$abstract$$ = undefined; } } )( module['ClassBuilder'] = {}, '.' ); /** MethodWrapperFactory **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * Builds method wrappers * * Copyright (C) 2011, 2012, 2013 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * Initializes factory to wrap methods * * @param {function(Function,Function,number)} factory function that will * perform the actual * wrapping * * @constructor */ module.exports = exports = function MethodWrapperFactory( factory ) { // permit omission of the 'new' keyword for instantiation if ( !( this instanceof exports ) ) { // module.exports for Closure Compiler return new module.exports( factory ); } this._factory = factory; }; /** * Wraps the provided method * * The returned function is determined by the factory function provided when the * MethodWrapperFactory was instantiated. * * @param {function()} method method to wrap * @param {function()} super_method super method, if overriding * @param {number} cid class id that method is associated with * @param {function()} getInst function to determine instance and return * associated visibility object * @param {string=} name name of method * @param {Object=} keywords method keywords */ exports.prototype.wrapMethod = function( method, super_method, cid, getInst, name, keywords ) { return this._factory( method, super_method, cid, getInst, name, keywords ); }; } )( module['MethodWrapperFactory'] = {}, '.' ); /** MethodWrappers **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * Default method wrapper functions * * Copyright (C) 2011, 2012, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * Method wrappers for standard (non-fallback) * @type {Object} */ exports.standard = { wrapOverride: function( method, super_method, cid, getInst ) { var retf = function() { // we need some sort of context in order to set __super; it may // be undefined per strict mode requirements depending on how // the method was invoked var context = getInst( this, cid ) || this || {}, retval = undefined ; // the _super property will contain the parent method (store the // previous value to ensure that calls to multiple overrides will // be supported) var psup = context.__super; context.__super = super_method; retval = method.apply( context, arguments ); // prevent sneaky bastards from breaking encapsulation by stealing // method references and ensure that __super is properly restored // for nested/multiple override invocations context.__super = psup; // if the value returned from the method was the context that we // passed in, return the actual instance (to ensure we do not break // encapsulation) if ( retval === context ) { return this; } return retval; }; // `super` is reserved and, in ES3, this causes problems with the // dot-notation; while `foo.super` will work fine in modern (ES5) // browsers, we need to maintain our ES3 compatibility retf['super'] = super_method; return retf; }, wrapNew: function( method, super_method, cid, getInst ) { return function() { var context = getInst( this, cid ) || this, retval = undefined ; // invoke the method retval = method.apply( context, arguments ); // if the value returned from the method was the context that we // passed in, return the actual instance (to ensure we do not break // encapsulation) if ( retval === context ) { return this; } return retval; }; }, wrapProxy: function( proxy_to, _, cid, getInst, name, keywords ) { // it is important that we store only a boolean value as to whether or // not this method is static *outside* of the returned closure, so as // not to keep an unnecessary reference to the keywords object var is_static = keywords && keywords[ 'static' ]; var ret = function() { var context = getInst( this, cid ) || this, retval = undefined, dest = ( ( is_static ) ? context.$( proxy_to ) : context[ proxy_to ] ) ; // rather than allowing a cryptic error to be thrown, attempt to // detect when the proxy call will fail and provide a useful error // message if ( !( ( dest !== null ) && ( typeof dest === 'object' ) && ( typeof dest[ name ] === 'function' ) ) ) { throw TypeError( "Unable to proxy " + name + "() call to '" + proxy_to + "'; '" + proxy_to + "' is undefined or '" + name + "' is not a function." ); } retval = dest[ name ].apply( dest, arguments ); // if the object we are proxying to returns itself, then instead // return a reference to *ourself* (so as not to break encapsulation // and to provide a more consistent and sensible API) return ( retval === dest ) ? this : retval; }; // ensures that proxies can be used to provide concrete // implementations of abstract methods with param requirements (we // have no idea what we'll be proxying to at runtime, so we need to // just power through it; see test case for more info) ret.__length = NaN; return ret; } }; } )( module['MethodWrappers'] = {}, '.' ); /** MemberBuilder **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * Handles building members (properties, methods) * * Copyright (C) 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This prototype could have easily been refactored into a number of others * (e.g. one for each type of member), but that refactoring has been * deferred until necessary to ensure ease.js maintains a relatively small * footprint. Ultimately, however, such a decision is a micro-optimization * and shouldn't harm the design and maintainability of the software. * * TODO: Implementation is inconsistent between various members. For * example, methods use ___$$keywords$$, whereas properties use [ val, * keywords ]. Decide on a common format. */ var util = require( './util' ), visibility = [ 'public', 'protected', 'private' ] ; /** * Responsible for building class members * * @param {Function} wrap_method method wrapper * @param {Function} wrap_override method override wrapper * @param {Function} wrap_proxy method proxy wrapper * @param {MemberBuilderValidator} validate member validator * * @constructor */ module.exports = function MemberBuilder( wrap_method, wrap_override, wrap_proxy, validate ) { // permit omitting 'new' keyword if ( !( this instanceof module.exports ) ) { return new module.exports( wrap_method, wrap_override, wrap_proxy, validate ); } this._wrapMethod = wrap_method; this._wrapOverride = wrap_override; this._wrapProxy = wrap_proxy; this._validate = validate; }; // we're throwing everything into the prototype exports = module.exports.prototype; /** * Initializes member object * * The member object contains members for each level of visibility (public, * protected and private). * * @param {Object} mpublic default public members * @param {Object} mprotected default protected members * @param {Object} mprivate default private members * * @return {__visobj} */ exports.initMembers = function( mpublic, mprotected, mprivate ) { return { 'public': mpublic || {}, 'protected': mprotected || {}, 'private': mprivate || {} }; }; /** * Copies a method to the appropriate member prototype, depending on * visibility, and assigns necessary metadata from keywords * * The provided ``member run'' state object is required and will be * initialized automatically if it has not been already. For the first * member of a run, the object should be empty. * * @param {__visobj} members * @param {!Object} meta metadata container * @param {string} name property name * @param {*} value property value * * @param {!Object.} keywords parsed keywords * * @param {Function} instCallback function to call in order to retrieve * object to bind 'this' keyword to * * @param {number} cid class id * @param {Object=} base optional base object to scan * * @param {Object} state member run state object * * @return {undefined} */ exports.buildMethod = function( members, meta, name, value, keywords, instCallback, cid, base, state ) { // these defaults will be used whenever a keyword set is unavailable, // which should only ever be the case if we're inheriting from a // prototype rather than an ease.js class/etc var kdefaults = this._methodKeywordDefaults; // TODO: We can improve performance by not scanning each one individually // every time this method is called var prev_data = scanMembers( members, name, base ), prev = ( prev_data ) ? prev_data.member : null, prev_keywords = ( prev && ( prev.___$$keywords$$ || kdefaults ) ), dest = getMemberVisibility( members, keywords, name ); ; // ensure that the declaration is valid (keywords make sense, argument // length, etc) this._validate.validateMethod( name, value, keywords, prev_data, prev_keywords, state ); // we might be overriding an existing method if ( keywords[ 'proxy' ] && !( prev && keywords.weak ) ) { // TODO: Note that this is not compatible with method hiding, due to its // positioning (see hideMethod() below); address once method hiding is // implemented (the validators currently handle everything else) dest[ name ] = this._createProxy( value, instCallback, cid, name, keywords ); } else if ( prev ) { if ( keywords.weak && !( prev_keywords[ 'abstract' ] ) ) { // another member of the same name has been found; discard the // weak declaration return false; } else if ( keywords[ 'override' ] || prev_keywords[ 'abstract' ] ) { // if we have the `abstract' keyword at this point, then we are // an abstract override var override = ( keywords[ 'abstract' ] ) ? aoverride( name ) : prev; // override the method dest[ name ] = this._overrideMethod( override, value, instCallback, cid ); } else { // by default, perform method hiding, even if the keyword was not // provided (the keyword simply suppresses the warning) dest[ name ] = hideMethod( prev, value, instCallback, cid ); } } else if ( keywords[ 'abstract' ] || keywords[ 'private' ] ) { // we do not want to wrap abstract methods, since they are not // callable; further, we do not need to wrap private methods, since // they are only ever accessible when we are already within a // private context (see test case for more information) dest[ name ] = value; } else { // we are not overriding the method, so simply copy it over, wrapping it // to ensure privileged calls will work properly dest[ name ] = this._overrideMethod( null, value, instCallback, cid ); } // store keywords for later reference (needed for pre-ES5 fallback) dest[ name ].___$$keywords$$ = keywords; return true; }; /** * Default keywords to apply to methods inherited from a prototype. * @type {Object} */ exports._methodKeywordDefaults = { 'virtual': true }; /** * Creates an abstract override super method proxy to NAME * * This is a fairly abstract concept that is disastrously confusing without * having been put into the proper context: This function is intended to be * used as a super method for a method override in the case of abstract * overrides. It only makes sense to be used, at least at this time, with * mixins. * * When called, the bound context (`this') will be the private member object * of the caller, which should contain a reference to the protected member * object of the supertype to proxy to. It is further assumed that the * protected member object (pmo) defines NAME such that it proxies to a * mixin; this means that invoking it could result in an infinite loop. We * therefore skip directly to the super-super method, which will be the * method we are interested in proxying to. * * There is one additional consideration: If this super method is proxying * from a mixin instance into a class, then it is important that we bind the * calling context to the pmo instaed of our own context; otherwise, we'll * be executing within the context of the trait, without access to the * members of the supertype that we are proxying to! The pmo will be used by * the ease.js method wrapper to look up the proper private member object, * so it is not a problem that the pmo is being passed in. * * That's a lot of text for such a small amount of code. * * @param {string} name name of method to proxy to * * @return {Function} abstract override super method proxy */ function aoverride( name ) { return function() { return this.___$$super$$.prototype[ name ] .apply( this.___$$pmo$$, arguments ); }; } /** * Copies a property to the appropriate member prototype, depending on * visibility, and assigns necessary metadata from keywords * * @param {__visobj} members * @param {!Object} meta metadata container * @param {string} name property name * @param {*} value property value * * @param {!Object.} keywords parsed keywords * * @param {Object=} base optional base object to scan * * @return {undefined} */ exports.buildProp = function( members, meta, name, value, keywords, base ) { // TODO: We can improve performance by not scanning each one individually // every time this method is called var prev_data = scanMembers( members, name, base ), prev = ( prev_data ) ? prev_data.member : null, prev_keywords = ( prev ) ? prev[ 1 ] : null; this._validate.validateProperty( name, value, keywords, prev_data, prev_keywords ); getMemberVisibility( members, keywords, name )[ name ] = [ value, keywords ]; }; /** * Copies a getter/setter to the appropriate member prototype, depending on * visibility, and assigns necessary metadata from keywords * * TODO: This should essentially mirror buildMethod with regards to overrides, * proxies, etc. * * @param {!__visobj} members * @param {!Object} meta metadata container * @param {string} name getter name * @param {*} get getter value * @param {*} set setter value * * @param {!Object.} keywords parsed keywords * * @param {Function} instCallback function to call in order to retrieve * object to bind 'this' keyword to * * @param {number} cid class id * @param {Object=} base optional base object to scan * * @return {undefined} * * Closure Compiler is improperly throwing warnings on Object.defineProperty(): * @suppress {checkTypes} */ exports.buildGetterSetter = function( members, meta, name, get, set, keywords, instCallback, cid, base ) { var prev_data = scanMembers( members, name, base ), prev_keywords = ( ( prev_data && prev_data.get ) ? prev_data.get.___$$keywords$$ : null ) ; this._validate.validateGetterSetter( name, {}, keywords, prev_data, prev_keywords ); if ( get ) { get = this._overrideMethod( null, get, instCallback, cid ); // ensure we store the keywords *after* the override, otherwise they // will be assigned to the wrapped function (the getter) get.___$$keywords$$ = keywords; } Object.defineProperty( getMemberVisibility( members, keywords, name ), name, { get: get, set: ( set ) ? this._overrideMethod( null, set, instCallback, cid ) : set, enumerable: true, configurable: false } ); }; /** * Returns member prototype to use for the requested visibility * * Will throw an exception if multiple access modifiers were used. * * @param {__visobj} members * * @param {!Object.} keywords parsed keywords * @param {string} name member name * * @return {Object} reference to visibility of members argument to use */ function getMemberVisibility( members, keywords, name ) { // there's cleaner ways of doing this, but consider it loop unrolling for // performance if ( keywords[ 'private' ] ) { ( keywords[ 'public' ] || keywords[ 'protected' ] ) && viserr( name ); return members[ 'private' ]; } else if ( keywords[ 'protected' ] ) { ( keywords[ 'public' ] || keywords[ 'private' ] ) && viserr( name ); return members[ 'protected' ]; } else { // public keyword is the default, so explicitly specifying it is only // for clarity ( keywords[ 'private' ] || keywords[ 'protected' ] ) && viserr( name ); return members[ 'public' ]; } } function viserr( name ) { throw TypeError( "Only one access modifier may be used for definition of '" + name + "'" ); } /** * Scan each level of visibility for the requested member * * @param {__visobj} members * * @param {string} name member to locate * @param {Object=} base optional base object to scan * * @return {{get,set,member}|null} */ function scanMembers( members, name, base ) { var i = visibility.length, member = null; // locate requested member by scanning each level of visibility while ( i-- ) { var visobj = members[ visibility[ i ] ]; // In order to support getters/setters, we must go off of the // descriptor. We must also ignore base properties (last argument), such // as Object.prototype.toString(). However, we must still traverse the // prototype chain. if ( member = util.getPropertyDescriptor( visobj, name, true ) ) { return { get: member.get, set: member.set, member: member.value }; } } // if a second comparison object was given, try again using it instead of // the original members object if ( base !== undefined ) { var base_methods = base.___$$methods$$, base_props = base.___$$props$$; // we must recurse on *all* the visibility objects of the base's // supertype; attempt to find the class associated with its // supertype, if any var base2 = ( ( base.prototype || {} ).___$$parent$$ || {} ) .constructor; // scan the base's methods and properties, if they are available return ( base_methods && scanMembers( base_methods, name, base2 ) ) || ( base_props && scanMembers( base_props, name, base2 ) ) || null ; } // nothing was found return null; } /** * Hide a method with a "new" method */ function hideMethod( super_method, new_method, instCallback, cid ) { // TODO: This function is currently unimplemented. It exists at present to // provide a placeholder and ensure that the override keyword is required to // override a parent method. // // We should never get to this point if the default validation rule set is // used to prevent omission of the 'override' keyword. throw Error( 'Method hiding not yet implemented (we should never get here; bug).' ); } /** * Create a method that proxies to the method of another object * * @param {string} proxy_to name of property (of instance) to proxy to * * @param {Function} instCallback function to call in order to retrieve * object to bind 'this' keyword to * * @param {number} cid class id * @param {string} mname name of method to invoke on destination object * @param {Object} keywords method keywords * * @return {Function} proxy method */ exports._createProxy = function( proxy_to, instCallback, cid, mname, keywords ) { return this._wrapProxy.wrapMethod( proxy_to, null, cid, instCallback, mname, keywords ); }; /** * Generates a method override function * * The override function simply wraps the method so that its invocation will * pass a __super property. This property may be used to invoke the overridden * method. * * @param {function()} super_method method to override * @param {function()} new_method method to override with * * @param {Function} instCallback function to call in order to retrieve * object to bind 'this' keyword to * * @param {number} cid class id * * @return {function()} override method */ exports._overrideMethod = function( super_method, new_method, instCallback, cid ) { instCallback = instCallback || function() {}; // return a function that permits referencing the super method via the // __super property var override = null; // are we overriding? override = ( ( super_method ) ? this._wrapOverride : this._wrapMethod ).wrapMethod( new_method, super_method, cid, instCallback ); // This is a trick to work around the fact that we cannot set the length // property of a function. Instead, we define our own property - __length. // This will store the expected number of arguments from the super method. // This way, when a method is being overridden, we can check to ensure its // compatibility with its super method. util.defineSecureProp( override, '__length', ( new_method.__length || new_method.length ) ); return override; } /** * Return the visibility level as a numeric value, where 0 is public and 2 is * private * * @param {Object} keywords keywords to scan for visibility level * * @return {number} visibility level as a numeric value */ exports._getVisibilityValue = function( keywords ) { if ( keywords[ 'protected' ] ) { return 1; } else if ( keywords[ 'private' ] ) { return 2; } else { // default is public return 0; } } /** * End member run and perform post-processing on state data * * A ``member run'' should consist of the members required for a particular * object (class/interface/etc). This action will perform validation * post-processing if a validator is available. * * @param {Object} state member run state * * @return {undefined} */ exports.end = function( state ) { this._validate && this._validate.end( state ); }; } )( module['MemberBuilder'] = {}, '.' ); /** MemberBuilderValidator **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * Validation rules for members * * Copyright (C) 2011, 2012, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ module.exports = exports = function MemberBuilderValidator( warn_handler ) { // permit omitting 'new' keyword if ( !( this instanceof module.exports ) ) { return new module.exports( warn_handler ); } this._warningHandler = warn_handler || function() {}; }; /** * Initialize validation state if not already done * * @param {Object} state validation state * * @return {Object} provided state object STATE */ exports.prototype._initState = function( state ) { if ( state.__vready ) return state; state.warn = {}; state.__vready = true; return state; }; /** * Perform post-processing on and invalidate validation state * * All queued warnings will be triggered. * * @param {Object} state validation state * * @return {undefined} */ exports.prototype.end = function( state ) { // trigger warnings for ( var f in state.warn ) { var warns = state.warn[ f ]; for ( var id in warns ) { this._warningHandler( warns[ id ] ); } } state.__vready = false; }; /** * Enqueue warning within validation state * * @param {Object} state validation state * @param {string} member member name * @param {string} id warning identifier * @param {Warning} warn warning * * @return {undefined} */ function _addWarn( state, member, id, warn ) { ( state.warn[ member ] = state.warn[ member ] || {} )[ id ] = warn; } /** * Remove warning from validation state * * @param {Object} state validation state * @param {string} member member name * @param {string} id warning identifier * * @return {undefined} */ function _clearWarn( state, member, id, warn ) { delete ( state.warn[ member ] || {} )[ id ]; } /** * Validates a method declaration, ensuring that keywords are valid, * overrides make sense, etc. * * Throws exception on validation failure. Warnings are stored in the state * object for later processing. The state object will be initialized if it * has not been already; for the initial validation, the state object should * be empty. * * @param {string} name method name * @param {*} value method value * * @param {Object.} keywords parsed keywords * * @param {Object} prev_data data of member being overridden * @param {Object} prev_keywords keywords of member being overridden * * @param {Object} state pre-initialized state object * * @return {undefined} */ exports.prototype.validateMethod = function( name, value, keywords, prev_data, prev_keywords, state ) { this._initState( state ); var prev = ( prev_data ) ? prev_data.member : null; if ( keywords[ 'abstract' ] ) { // do not permit private abstract methods (doesn't make sense, since // they cannot be inherited/overridden) if ( keywords[ 'private' ] ) { throw TypeError( "Method '" + name + "' cannot be both private and abstract" ); } } // const doesn't make sense for methods; they're always immutable if ( keywords[ 'const' ] ) { throw TypeError( "Cannot declare method '" + name + "' as constant; keyword is " + "redundant" ); } // virtual static does not make sense, as static methods cannot be // overridden if ( keywords[ 'virtual' ] && ( keywords[ 'static' ] ) ) { throw TypeError( "Cannot declare static method '" + name + "' as virtual" ); } // do not allow overriding getters/setters if ( prev_data && ( prev_data.get || prev_data.set ) ) { throw TypeError( "Cannot override getter/setter '" + name + "' with method" ); } if ( keywords[ 'proxy' ] ) { // proxies are expected to provide the name of the destination object if ( typeof value !== 'string' ) { throw TypeError( "Cannot declare proxy method '" + name + "'; string value " + "expected" ); } else if ( keywords[ 'abstract' ] ) { // proxies are always concrete throw TypeError( "Proxy method '" + name + "' cannot be abstract" ); } } // search for any previous instances of this member if ( prev ) { // perform this check first, as it will make more sense than those that // follow, should this condition be satisfied if ( prev_keywords[ 'private' ] ) { throw TypeError( "Private member name '" + name + "' conflicts with supertype" ); } // disallow overriding properties with methods if ( !( typeof prev === 'function' ) ) { throw TypeError( "Cannot override property '" + name + "' with method" ); } // disallow overriding non-virtual methods if ( keywords[ 'override' ] && !( prev_keywords[ 'virtual' ] ) ) { if ( !( keywords[ 'abstract' ] ) ) { throw TypeError( "Cannot override non-virtual method '" + name + "'" ); } // at this point, we have `abstract override' if ( !( prev_keywords[ 'abstract' ] ) ) { // TODO: test me throw TypeError( "Cannot perform abstract override on non-abstract " + "method '" + name + "'" ); } } // do not allow overriding concrete methods with abstract unless the // abstract method is weak if ( keywords[ 'abstract' ] && !( keywords.weak ) && !( prev_keywords[ 'abstract' ] ) ) { throw TypeError( "Cannot override concrete method '" + name + "' with " + "abstract method" ); } var lenprev = ( prev.__length === undefined ) ? prev.length : prev.__length; var lennow = ( value.__length === undefined ) ? value.length : value.__length; if ( keywords[ 'proxy' ] ) { // otherwise we'd be checking against the length of a string. lennow = NaN; } if ( keywords.weak && !( prev_keywords[ 'abstract' ] ) ) { // weak abstract declaration found after its concrete // definition; check in reverse order var tmp = lenprev; lenprev = lennow; lennow = tmp; } // ensure parameter list is at least the length of its supertype if ( lennow < lenprev ) { throw TypeError( "Declaration of method '" + name + "' must be compatible " + "with that of its supertype" ); } // do not permit visibility deescalation if ( this._getVisibilityValue( prev_keywords ) < this._getVisibilityValue( keywords ) ) { throw TypeError( "Cannot de-escalate visibility of method '" + name + "'" ); } // Disallow overriding method without override keyword (unless // parent method is abstract). In the future, this will provide a // warning to default to method hiding. Note the check for a if ( !( keywords[ 'override' ] || prev_keywords[ 'abstract' ] || keywords.weak ) ) { throw TypeError( "Attempting to override method '" + name + "' without 'override' keyword" ); } // prevent non-override warning if ( keywords.weak && prev_keywords[ 'override' ] ) { _clearWarn( state, name, 'no' ); } } else if ( keywords[ 'override' ] ) { // using the override keyword without a super method may indicate a bug, // but it shouldn't stop the class definition (it doesn't adversely // affect the functionality of the class, unless of course the method // attempts to reference a supertype) _addWarn( state, name, 'no', Error( "Method '" + name + "' using 'override' keyword without super method" ) ); } }; /** * Validates a property declaration, ensuring that keywords are valid, overrides * make sense, etc. * * Throws exception on validation failure * * @param {string} name method name * @param {*} value method value * * @param {Object.} keywords parsed keywords * * @param {Object} prev_data data of member being overridden * @param {Object} prev_keywords keywords of member being overridden * * @return {undefined} */ exports.prototype.validateProperty = function( name, value, keywords, prev_data, prev_keywords ) { var prev = ( prev_data ) ? prev_data.member : null; // do not permit visibility de-escalation if ( prev ) { // perform this check first, as it will make more sense than those that // follow, should this condition be satisfied if ( prev_keywords[ 'private' ] ) { throw TypeError( "Private member name '" + name + "' conflicts with supertype" ); } // disallow overriding methods with properties if ( typeof prev === 'function' ) { throw new TypeError( "Cannot override method '" + name + "' with property" ); } if ( this._getVisibilityValue( prev_keywords ) < this._getVisibilityValue( keywords ) ) { throw TypeError( "Cannot de-escalate visibility of property '" + name + "'" ); } } // do not allow overriding getters/setters if ( prev_data && ( prev_data.get || prev_data.set ) ) { throw TypeError( "Cannot override getter/setter '" + name + "' with property" ); } // abstract properties do not make sense if ( keywords[ 'abstract' ] ) { throw TypeError( "Property '" + name + "' cannot be declared as abstract" ); } // constants are static if ( keywords[ 'static' ] && keywords[ 'const' ] ) { throw TypeError( "Static keyword cannot be used with const for property '" + name + "'" ); } // properties are inherently virtual if ( keywords['virtual'] ) { throw TypeError( "Cannot declare property '" + name + "' as virtual" ); } }; /** * Performs common validations on getters/setters * * If a problem is found, an exception will be thrown. * * @param {string} name getter/setter name * @param {Object.} keywords parsed keywords * * @return {undefined} */ exports.prototype.validateGetterSetter = function( name, value, keywords, prev_data, prev_keywords ) { var prev = ( prev_data ) ? prev_data.member : null, prev_gs = ( ( prev_data && ( prev_data.get || prev_data.set ) ) ? true : false ) ; // abstract getters/setters are not yet supported if ( keywords[ 'abstract' ] ) { throw TypeError( "Cannot declare getter/setter '" + name + "' as abstract" ); } // for const getters/setters, omit the setter if ( keywords[ 'const' ] ) { throw TypeError( "Cannot declare const getter/setter '" + name + "'" ); } // virtual static does not make sense, as static methods cannot be // overridden if ( keywords[ 'virtual' ] && ( keywords[ 'static' ] ) ) { throw TypeError( "Cannot declare static method '" + name + "' as virtual" ); } if ( prev || prev_gs ) { // perform this check first, as it will make more sense than those that // follow, should this condition be satisfied if ( prev_keywords && prev_keywords[ 'private' ] ) { throw TypeError( "Private member name '" + name + "' conflicts with supertype" ); } // To speed up the system we'll simply check for a getter/setter, rather // than checking separately for methods/properties. This is at the // expense of more detailed error messages. They'll live. if ( !( prev_gs ) ) { throw TypeError( "Cannot override method or property '" + name + "' with getter/setter" ); } if ( !( prev_keywords && prev_keywords[ 'virtual' ] ) ) { throw TypeError( "Cannot override non-virtual getter/setter '" + name + "'" ); } if ( !( keywords[ 'override' ] ) ) { throw TypeError( "Attempting to override getter/setter '" + name + "' without 'override' keyword" ); } // do not permit visibility de-escalation if ( this._getVisibilityValue( prev_keywords || {} ) < this._getVisibilityValue( keywords ) ) { throw TypeError( "Cannot de-escalate visibility of getter/setter '" + name + "'" ); } } else if ( keywords[ 'override' ] ) { // using the override keyword without a super method may indicate a bug // in the user's code this._warningHandler( Error( "Getter/setter '" + name + "' using 'override' keyword without super getter/setter" ) ); } } /** * Return the visibility level as a numeric value, where 0 is public and 2 is * private * * @param {Object} keywords keywords to scan for visibility level * * @return {number} visibility level as a numeric value */ exports.prototype._getVisibilityValue = function( keywords ) { if ( keywords[ 'protected' ] ) { return 1; } else if ( keywords[ 'private' ] ) { return 2; } else { // default is public return 0; } } } )( module['MemberBuilderValidator'] = {}, '.' ); /** interface **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * Contains interface module * * Copyright (C) 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ var util = require( './util' ), MethodWrapperFactory = require( './MethodWrapperFactory' ), wrappers = require( './MethodWrappers' ).standard, member_builder = require( './MemberBuilder' )( MethodWrapperFactory( wrappers.wrapNew ), MethodWrapperFactory( wrappers.wrapOverride ), MethodWrapperFactory( wrappers.wrapProxy ), require( './MemberBuilderValidator' )() ), ClassBuilder = require( './ClassBuilder' ); /** * This module may be invoked in order to provide a more natural looking * interface definition * * Only new interfaces may be created using this method. They cannot be * extended. To extend an existing interface, call its extend() method, or use * the extend() method of this module. * * @param {string|Object} namedef optional name or definition * @param {Object=} def interface definition if first arg is name * * @return {Function|Object} new interface or staging object */ module.exports = function( namedef, def ) { var type = ( typeof namedef ), result = null ; switch ( type ) { // anonymous interface case 'object': result = createAnonymousInterface.apply( null, arguments ); break; // named class case 'string': result = createNamedInterface.apply( null, arguments ); break; default: // we don't know what to do! throw TypeError( "Expecting anonymous interface definition or named " + "interface definition" ); } return result; }; /** * Creates an interface * * @return {Function} extended interface */ module.exports.extend = function() { return extend.apply( this, arguments ); }; /** * Determines whether the provided object is an interface created through * ease.js * * @param {Object} obj object to test * * @return {boolean} true if interface (created through ease.js), otherwise * false */ module.exports.isInterface = function( obj ) { obj = obj || {}; return ( obj.prototype instanceof Interface ) ? true : false ; }; /** * Default interface implementation * * @return {undefined} */ function Interface() {} /** * Creates a new anonymous Interface from the given interface definition * * @param {Object} def interface definition * * @return {Function} new anonymous interface */ function createAnonymousInterface( def ) { // ensure we have the proper number of arguments (if they passed in // too many, it may signify that they don't know what they're doing, // and likely they're not getting the result they're looking for) if ( arguments.length > 1 ) { throw Error( "Expecting one argument for Interface definition; " + arguments.length + " given." ); } return extend( def ); } /** * Creates a new named interface from the given interface definition * * @param {string} name interface name * @param {Object} def interface definition * * @return {Function} new named interface */ function createNamedInterface( name, def ) { // if too many arguments were provided, it's likely that they're // expecting some result that they're not going to get if ( arguments.length > 2 ) { throw Error( "Expecting two arguments for definition of named Interface '" + name + "'; " + arguments.length + " given." ); } // the definition must be an object if ( typeof def !== 'object' ) { throw TypeError( "Unexpected value for definition of named Interface '" + name + "'; object expected" ); } // add the name to the definition def.__name = name; return extend( def ); } /** * Augment an exception with interface name and then throw * * @param {string} iname interface name or empty string * @param {Error} e exception to augment */ function _ithrow( iname, e ) { // alter the message to include our name e.message = "Failed to define interface " + ( ( iname ) ? iname : '(anonymous)' ) + ": " + e.message ; throw e; } var extend = ( function( extending ) { return function extend() { // ensure we'll be permitted to instantiate interfaces for the base extending = true; var a = arguments, an = a.length, props = ( ( an > 0 ) ? a[ an - 1 ] : 0 ) || {}, base = ( ( an > 1 ) ? a[ an - 2 ] : 0 ) || Interface, prototype = new base(), iname = '', // holds validation state vstate = {}, members = member_builder.initMembers( prototype, prototype, prototype ) ; // grab the name, if one was provided if ( iname = props.__name ) { // we no longer need it delete props.__name; } // sanity check inheritCheck( prototype ); var new_interface = createInterface( iname ); util.propParse( props, { assumeAbstract: true, // override default exceptions from parser errors _throw: function( e ) { _ithrow( iname, e ); }, property: function() { // should never get to this point because of assumeAbstract _ithrow( iname, TypeError( "Unexpected internal error" ) ); }, getset: function() { // should never get to this point because of assumeAbstract _ithrow( iname, TypeError( "Unexpected internal error" ) ); }, method: function( name, value, is_abstract, keywords ) { // all members must be public if ( keywords[ 'protected' ] || keywords[ 'private' ] ) { _ithrow( iname, TypeError( "Member " + name + " must be public" ) ); } member_builder.buildMethod( members, null, name, value, keywords, null, 0, {}, vstate ); } } ); attachExtend( new_interface ); attachStringMethod( new_interface, iname ); attachCompat( new_interface ); attachInstanceOf( new_interface ); new_interface.prototype = prototype; new_interface.constructor = new_interface; // freeze the interface (preventing additions), if supported util.freeze( new_interface ); // we're done; let's not allow interfaces to be instantiated anymore extending = false; return new_interface; }; /** * Creates a new interface constructor function * * @param {string=} iname interface name * * @return {function()} */ function createInterface( iname ) { return function() { // allows us to extend the interface without throwing an exception // (since the prototype requires an instance) if ( !extending ) { // only called if someone tries to create a new instance of an // interface throw Error( "Interface " + ( ( iname ) ? ( iname + ' ' ) : '' ) + " cannot be instantiated" ); } }; } } )( false ); /** * Assures that the parent object is a valid object to inherit from * * This method allows inheriting from any object (note that it will likely cause * errors if not an interface), but will place restrictions on objects like * Classes that do not make sense to inherit from. This will provide a more * friendly error, with suggestions on how to resolve the issue, rather than a * cryptic error resulting from inheritance problems. * * This method will throw an exception if there is a violation. * * @param {Object} prototype prototype to check for inheritance flaws * * @return {undefined} */ function inheritCheck( prototype ) { // if we're inheriting from another interface, then we're good if ( !( prototype instanceof Interface ) ) { throw new TypeError( "Interfaces may only extend other interfaces" ); } } /** * Attaches extend method to the given function (interface) * * @param {Function} func function (interface) to attach method to * * @return {undefined} */ function attachExtend( func ) { /** * Shorthand for extending interfaces * * This method can be invoked on the object, rather than having to call * Interface.extend( this ). * * @param {Object} props properties to add to extended interface * * @return {Object} extended interface */ util.defineSecureProp( func, 'extend', function( props ) { return extend( this, props ); }); } /** * Provides more sane/useful output when interface is converted to a string * * @param {Object} func interface * @param {string=} iname interface name * * @return {undefined} */ function attachStringMethod( func, iname ) { func.toString = ( iname ) ? function() { return '[object Interface <' + iname + '>]'; } : function() { return '[object Interface]'; } ; } /** * Attaches a method to assert whether a given object is compatible with the * interface * * @param {Function} iface interface to attach method to * * @return {undefined} */ function attachCompat( iface ) { util.defineSecureProp( iface, 'isCompatible', function( obj ) { return isCompat( iface, obj ); } ); } /** * Determines if the given object is compatible with the given interface. * * An object is compatible if it defines all methods required by the * interface, with at least the required number of parameters. * * Processing time is linear with respect to the number of members of the * provided interface. * * To get the actual reasons in the event of a compatibility failure, use * analyzeCompat instead. * * @param {Interface} iface interface that must be adhered to * @param {Object} obj object to check compatibility against * * @return {boolean} true if compatible, otherwise false */ function isCompat( iface, obj ) { // yes, this processes the entire interface, but it is hopefully small // anyway and the process is fast enough that doing otherwise may be // micro-optimizing return analyzeCompat( iface, obj ).length === 0; } /** * Analyzes the given object to determine if there exists any compatibility * issues with respect to the given interface * * Will provide an array of the names of incompatible members. A method is * incompatible if it is not defined or if it does not define at least the * required number of parameters. * * Processing time is linear with respect to the number of members of the * provided interface. * * @param {Interface} iface interface that must be adhered to * @param {Object} obj object to check compatibility against * * @return {Array.>} compatibility reasons */ function analyzeCompat( iface, obj ) { var missing = []; util.propParse( iface.prototype, { method: function( name, func, is_abstract, keywords ) { if ( typeof obj[ name ] !== 'function' ) { missing.push( [ name, 'missing' ] ); } else if ( obj[ name ].length < func.__length ) { // missing parameter(s); note that we check __length on the // interface method (our internal length) but not on the // object (since it may be a vanilla object) missing.push( [ name, 'incompatible' ] ); } } } ); return missing; } /** * Attaches instance check method * * This method is invoked when checking the type of a class against an * interface. * * @param {Interface} iface interface that must be adhered to * * @return {undefined} */ function attachInstanceOf( iface ) { util.defineSecureProp( iface, '__isInstanceOf', function( type, obj ) { return _isInstanceOf( type, obj ); } ); } /** * Determine if INSTANCE implements the interface TYPE * * @param {Interface} type interface to check against * @param {Object} instance instance to examine * * @return {boolean} whether TYPE is implemented by INSTANCE */ function _isInstanceOf( type, instance ) { // we are interested in the class's metadata, not the instance's var proto = instance.constructor; // if no metadata are available, then our remaining checks cannot be // performed var meta; if ( !instance.__cid || !( meta = ClassBuilder.getMeta( proto ) ) ) { return isCompat( type, instance ); } var implemented = meta.implemented, i = implemented.length; // check implemented interfaces et. al. (other systems may make use of // this meta-attribute to provide references to types) while ( i-- ) { if ( implemented[ i ] === type ) { return true; } } return false; } module.exports.isInstanceOf = _isInstanceOf; } )( module['interface'] = {}, '.' ); /** VisibilityObjectFactory **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * Contains visibility object factory * * Copyright (C) 2011, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * XXX: tightly coupled */ var util = require( './util' ); /** * Initializes visibility object factory * * The visibility object is the "magic" behind ease.js. This factory creates the * object that holds the varying levels of visibility, which are swapped out and * inherited depending on circumstance. * * @constructor */ module.exports = exports = function VisibilityObjectFactory() { // permit omitting 'new' keyword if ( !( this instanceof exports ) ) { // module.exports instead of exports because Closure Compiler seems to // be confused return new module.exports(); } }; /** * Sets up properties * * This includes all members (including private). Private members will be set up * in a separate object, so that they can be easily removed from the mix. That * object will include the destination object in the prototype, so that the * access should be transparent. This object is returned. * * Properties are expected in the following format. Note that keywords are * ignored: * { public: { prop: [ value, { keyword: true } ] } } * * @param {Object} dest destination object * @param {Object} properties properties to copy * @param {Object=} methods methods to copy * * @return {Object} object containing private members and dest as prototype */ exports.prototype.setup = function setup( dest, properties, methods ) { // create the private layer atop of the destination object var obj = this._createPrivateLayer( dest, properties ); // initialize each of the properties for this instance to // ensure we're not sharing references to prototype values this._doSetup( dest, properties[ 'public' ] ); // Do the same for protected, but only if they do not exist already in // public. The reason for this is because the property object is laid /atop/ // of the public members, meaning that a parent's protected members will // take precedence over a subtype's overriding /public/ members. Uh oh. this._doSetup( dest, properties[ 'protected' ], methods[ 'protected' ], true ); // then add the private parts this._doSetup( obj, properties[ 'private' ], methods[ 'private' ] ); return obj; }; /** * Add an extra layer atop the destination object, which will contain the * private members * * The object provided will be used as the prototype for the new private layer, * so the provided object will be accessible on the prototype chain. * * Subtypes may override this method to alter the functionality of the private * visibility object (e.g. to prevent it from being created). * * @param {Object} atop_of object to add private layer atop of * @param {Object} properties properties * * @return {Object} private layer with given object as prototype */ exports.prototype._createPrivateLayer = function( atop_of, properties ) { /** @constructor */ var obj_ctor = function() {}; obj_ctor.prototype = atop_of; // we'll be returning an instance, so that the prototype takes effect var obj = new obj_ctor(); // All protected properties need to be proxied from the private object // (which will be passed as the context) to the object containing protected // values. Otherwise, the protected property values would be set on the // private object, making them inaccessible to subtypes. this.createPropProxy( atop_of, obj, properties[ 'protected' ] ); return obj; }; /** * Set up destination object by copying over properties and methods * * The prot_priv parameter can be used to ignore both explicitly and * implicitly public methods. * * @param {Object} dest destination object * @param {Object} properties properties to copy * @param {Object} methods methods to copy * @param {boolean} prot_priv do not set unless protected or private * * @return {undefined} */ exports.prototype._doSetup = function( dest, properties, methods, prot_priv ) { var hasOwn = Array.prototype.hasOwnProperty; // copy over the methods if ( methods !== undefined ) { for ( var method_name in methods ) { if ( hasOwn.call( methods, method_name ) ) { var pre = dest[ method_name ], kw = pre && pre.___$$keywords$$; // If requested, do not copy the method over if it already // exists in the destination object. Don't use hasOwn here; // unnecessary overhead and we want to traverse any prototype // chains. We do not check the public object directly, for // example, because we need a solution that will work if a proxy // is unsupported by the engine. // // Also note that we need to allow overriding if it exists in // the protected object (we can override protected with // protected). This is the *last* check to ensure a performance // hit is incured *only* if we're overriding protected with // protected. if ( !prot_priv || ( pre === undefined ) || ( kw[ 'private' ] || kw[ 'protected' ] ) ) { dest[ method_name ] = methods[ method_name ]; } } } } // initialize private/protected properties and store in instance data for ( var prop in properties ) { if ( hasOwn.call( properties, prop ) ) { dest[ prop ] = util.clone( properties[ prop ][ 0 ] ); } } } /** * Creates a proxy for all given properties to the given base * * The proxy uses getters/setters to forward all calls to the base. The * destination object will be used as the proxy. All properties within props * will be used proxied. * * To summarize: for each property in props, all gets and sets will be forwarded * to base. * * Please note that this does not use the JS proxy implementation. That will be * done in the future for engines that support it. * * @param {Object} base object to proxy to * @param {Object} dest object to treat as proxy (set getters/setters on) * @param {Object} props properties to proxy * * @return {Object} returns dest */ exports.prototype.createPropProxy = function( base, dest, props ) { var hasOwn = Object.prototype.hasOwnProperty; for ( var prop in props ) { if ( !( hasOwn.call( props, prop ) ) ) { continue; } ( function( prop ) { // just in case it's already defined, so we don't throw an error dest[ prop ] = undefined; // public properties, when set internally, must forward to the // actual variable Object.defineProperty( dest, prop, { set: function( val ) { base[ prop ] = val; }, get: function() { return base[ prop ]; }, enumerable: true } ); } ).call( null, prop ); } return dest; }; } )( module['VisibilityObjectFactory'] = {}, '.' ); /** FallbackVisibilityObjectFactory **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * Contains fallback visibility object factory * * Copyright (C) 2010, 2011, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * Initializes fallback visibility object factory * * Unlike the standard visibility object, fallback does not create various * layers. This is for the simple fact that setting a value on one of the layers * is not visible to layers beneath it (its prototypes). Fallback is necessary * if proxy support or emulation (via ES5 getters/setters) is unavailable. */ module.exports = exports = function FallbackVisibilityObjectFactory() { // permit omitting 'new' keyword if ( !( this instanceof exports ) ) { // module.exports for Closure Compiler return new module.exports(); } }; /** * "Inherit" from VisibilityObjectFactory */ exports.prototype = require( './VisibilityObjectFactory' )(); /** * Do not create private visibility layer * * We're likely falling back because we cannot properly support the private * visibility layer. Therefore, it will be omitted. * * @param {Object} atop_of will be returned, unmodified * @param {Object} properties ignored * * @return {Object} provided object with no additional layer */ exports.prototype._createPrivateLayer = function( atop_of, properties ) { return atop_of; }; /** * Does not create property proxy * * The fallback implementation is used because proxies are not supported and * cannot be emulated with getters/setters. * * @param {Object} base will be returned, unmodified * @param {Object} dest ignored * @param {Object} props ignored * * @return {Object} given base */ exports.prototype.createPropProxy = function( base, dest, props ) { return base; }; } )( module['FallbackVisibilityObjectFactory'] = {}, '.' ); /** VisibilityObjectFactoryFactory **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * Contains factory for visibility object factory * * Copyright (C) 2011, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * XXX: Figure out how to resolve Closure Compiler's warning about shared * type information */ // XXX: Tightly coupled var util = require( './util' ), VisibilityObjectFactory = require( './VisibilityObjectFactory' ), FallbackVisibilityObjectFactory = require( './FallbackVisibilityObjectFactory' ) ; /** * Responsible for instantiating the VisibilityObjectFactory appropriate for the * runtime environment * * This prototype determines what class should be instantiated. If we are within * an ECMAScript 5 environment, we can take full advantage of the standard * visibility object implementation. Otherwise, we are unable to emulate proxies * and must fall back on a less sophisticated implementation that sacrifices * visibility support. */ exports.fromEnvironment = function() { // if falling back, return fallback, otherwise standard return ( util.definePropertyFallback() ) ? FallbackVisibilityObjectFactory() : VisibilityObjectFactory() ; }; } )( module['VisibilityObjectFactoryFactory'] = {}, '.' ); /** class **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * Contains basic inheritance mechanism * * Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * Console to use for logging * * This reference allows an alternative console to be used. Must contain * warn() or log() methods. * * TODO: This needs to be moved into a facade, once more refactoring can be * done; it was moved out of warn during its refactoring. * * @type {Object} */ var _console = ( typeof console !== 'undefined' ) ? console : undefined; var util = require( './util' ), ClassBuilder = require( './ClassBuilder' ), Interface = require( './interface' ), warn = require( './warn' ), Warning = warn.Warning, log_handler = warn.LogHandler( _console ), MethodWrapperFactory = require( './MethodWrapperFactory' ), wrappers = require( './MethodWrappers' ).standard, class_builder = ClassBuilder( log_handler, require( './MemberBuilder' )( MethodWrapperFactory( wrappers.wrapNew ), MethodWrapperFactory( wrappers.wrapOverride ), MethodWrapperFactory( wrappers.wrapProxy ), require( './MemberBuilderValidator' )( function( warning ) { log_handler.handle( Warning( warning ) ); } ) ), require( './VisibilityObjectFactoryFactory' ) .fromEnvironment() ) ; var _nullf = function() { return null; } /** * This module may be invoked in order to provide a more natural looking class * definition mechanism * * This may not be used to extend existing classes. To extend an existing class, * use the class's extend() method. If unavailable (or extending a non-ease.js * class/object), use the module's extend() method. * * @param {string|Object} namedef optional name or definition * @param {Object=} def class definition if first argument is name * * @return {Function|Object} new class or staging object */ module.exports = function( namedef, def ) { var type = ( typeof namedef ), result = null, args = [], i = arguments.length ; // passing arguments object prohibits optimizations in v8 while ( i-- ) args[ i ] = arguments[ i ]; switch ( type ) { // anonymous class case 'object': result = createAnonymousClass.apply( null, args ); break; // named class case 'string': result = createNamedClass.apply( null, args ); break; default: // we don't know what to do! throw TypeError( "Expecting anonymous class definition or named class definition" ); } return result; }; /** * Creates a class, inheriting either from the provided base class or the * default base class * * @param {Function|Object} baseordfn parent or definition object * @param {Object=} dfn definition object if parent provided * * @return {Function} extended class */ module.exports.extend = extend; /** * Implements an interface or set of interfaces * * @param {...Function} interfaces interfaces to implement * * @return {Object} intermediate interface object */ module.exports.implement = function( interfaces ) { // implement on empty base return createImplement( null, Array.prototype.slice.call( arguments ) ); }; /** * Mix a trait into a class * * The ultimate intent of this depends on the ultimate `extend' call---if it * extends another class, then the traits will be mixed into that class; * otherwise, the traits will be mixed into the base class. In either case, * a final `extend' call is necessary to complete the definition. An attempt * to instantiate the return value before invoking `extend' will result in * an exception. * * @param {Array.} traits traits to mix in * * @return {Function} staging object for class definition */ module.exports.use = function( traits ) { var args = [], i = arguments.length; while( i-- ) args[ i ] = arguments[ i ]; // consume traits onto an empty base return createUse( _nullf, args ); }; var _dummyclass = { prototype: {} }; var _dummyinst = { constructor: { prototype: {} } }; /** * Determines whether the provided object is a class created through ease.js * * TODO: delegate to ClassBuilder * * @param {Object} obj object to test * * @return {boolean} true if class (created through ease.js), otherwise false */ module.exports.isClass = function( obj ) { obj = obj || _dummyclass; var meta = ClassBuilder.getMeta( obj ); // TODO: we're checking a random field on the meta object; do something // proper return ( ( ( meta !== null ) && meta.implemented ) || ( obj.prototype instanceof ClassBuilder.ClassBase ) ) ? true : false ; }; /** * Determines whether the provided object is an instance of a class created * through ease.js * * TODO: delegate to ClassBuilder * * @param {Object} obj object to test * * @return {boolean} true if instance of class (created through ease.js), * otherwise false */ module.exports.isClassInstance = function( obj ) { obj = obj || _dummyinst; // if the constructor is a class, then we must be an instance! return module.exports.isClass( obj.constructor ); }; /** * Determines if the class is an instance of the given type * * The given type can be a class, interface, trait or any other type of object. * It may be used in place of the 'instanceof' operator and contains additional * enhancements that the operator is unable to provide due to prototypal * restrictions. * * @param {Object} type expected type * @param {Object} instance instance to check * * @return {boolean} true if instance is an instance of type, otherwise false */ module.exports.isInstanceOf = ClassBuilder.isInstanceOf; /** * Alias for isInstanceOf() * * May read better in certain situations (e.g. Cat.isA( Mammal )) and more * accurately conveys the act of inheritance, implementing interfaces and * traits, etc. */ module.exports.isA = module.exports.isInstanceOf; /** * Creates a new anonymous Class from the given class definition * * @param {Object} def class definition * * @return {Function} new anonymous class */ function createAnonymousClass( def ) { // ensure we have the proper number of arguments (if they passed in // too many, it may signify that they don't know what they're doing, // and likely they're not getting the result they're looking for) if ( arguments.length > 1 ) { throw Error( "Expecting one argument for anonymous Class definition; " + arguments.length + " given." ); } return extend( def ); } /** * Creates a new named Class from the given class definition * * @param {string} name class name * @param {Object} def class definition * * @return {Function|Object} new named class or staging object if definition * was not provided */ function createNamedClass( name, def ) { // if too many arguments were provided, it's likely that they're // expecting some result that they're not going to get if ( arguments.length > 2 ) { throw Error( "Expecting at most two arguments for definition of named Class '" + name + "'; " + arguments.length + " given." ); } // if no definition was given, return a staging object, to apply the name to // the class once it is actually created if ( def === undefined ) { return createStaging( name ); } // the definition must be an object else if ( typeof def !== 'object' ) { throw TypeError( "Unexpected value for definition of named Class '" + name + "'; object expected" ); } // add the name to the definition def.__name = name; return extend( def ); } /** * Creates a staging object to stage a class name * * The class name will be applied to the class generated by operations performed * on the staging object. This allows applying names to classes that need to be * extended or need to implement interfaces. * * @param {string} cname desired class name * * @return {Object} object staging the given class name */ function createStaging( cname ) { return { extend: function() { var args = [], i = arguments.length; while ( i-- ) args[ i ] = arguments[ i ]; // extend() takes a maximum of two arguments. If only one // argument is provided, then it is to be the class definition. // Otherwise, the first argument is the supertype and the second // argument is the class definition. Either way you look at it, // the class definition is always the final argument. // // We want to add the name to the definition. args[ args.length - 1 ].__name = cname; return extend.apply( null, args ); }, implement: function() { var args = [], i = arguments.length; while ( i-- ) args[ i ] = arguments[ i ]; // implement on empty base, providing the class name to be used once // extended return createImplement( null, args, cname ); }, use: function() { var args = [], i = arguments.length; while ( i-- ) args[ i ] = arguments[ i ]; return createUse( _nullf, args ); } }; } /** * Creates an intermediate object to permit implementing interfaces * * This object defers processing until extend() is called. This intermediate * object ensures that a usable class is not generated until after extend() is * called, as it does not make sense to create a class without any * body/definition. * * @param {Object} base base class to implement atop of, or null * @param {Array} ifaces interfaces to implement * @param {string=} cname optional class name once extended * * @return {Object} intermediate implementation object */ function createImplement( base, ifaces, cname ) { // Defer processing until after extend(). This also ensures that implement() // returns nothing usable. var partial = { extend: function() { var an = arguments.length, def = arguments[ an - 1 ], ext_base = ( an > 1 ) ? arguments[ an - 2 ] : null ; // if any arguments remain, then they likely misunderstood what this // method does if ( an > 2 ) { throw Error( "Expecting no more than two arguments for extend()" ); } // if a base was already provided for extending, don't allow them to // give us yet another one (doesn't make sense) if ( base && ext_base ) { throw Error( "Cannot override parent " + base.toString() + " with " + ext_base.toString() + " via extend()" ); } // if a name was provided, use it if ( cname ) { def.__name = cname; } // If a base was provided when createImplement() was called, use // that. Otherwise, use the extend() base passed to this function. // If neither of those are available, extend from an empty class. ifaces.push( base || ext_base || extend( {} ) ); return extend.call( null, implement.apply( this, ifaces ), def ); }, // TODO: this is a naive implementation that works, but could be // much more performant (it creates a subtype before mixing in) use: function() { var traits = [], i = arguments.length; // passing arguments object prohibits optimizations in v8 while ( i-- ) traits[ i ] = arguments[ i ]; return createUse( function() { return partial.__createBase(); }, traits ); }, // allows overriding default behavior __createBase: function() { return partial.extend( {} ); } }; return partial; } /** * Create a staging object representing an eventual mixin * * This staging objects prepares a class definition for trait mixin. In * particular, the returned staging object has the following features: * - invoking it will, if mixing into an existing (non-base) class without * subclassing, immediately complete the mixin and instantiate the * generated class; * - calling `use' has the effect of chaining mixins, stacking them atop * of one-another; and * - invoking `extend' will immediately complete the mixin, resulting in a * subtype of the base. * * Mixins are performed lazily---the actual mixin will not take place until * the final `extend' call, which may be implicit by invoking the staging * object (performing instantiation). * * The third argument determines whether or not a final `extend' call must * be explicit: in this case, any instantiation attempts will result in an * exception being thrown. * * This staging object may be used as a base for extending. Note, however, * that its metadata are unavailable at the time of definition---its * contents are marked as "lazy" and must be processed using the mixin's * eventual metadata. * * @param {function()} basef returns base from which to lazily * extend * @param {Array.} traits traits to mix in * @param {boolean} nonbase extending from a non-base class * (setting will permit instantiation * with implicit extend) * * @return {Function} staging object for mixin */ function createUse( basef, traits, nonbase ) { // invoking the partially applied class will immediately complete its // definition and instantiate it with the provided constructor arguments var partial = function() { return partialClass() .apply( null, arguments ); }; var partialClass = function() { // this argument will be set only in the case where an existing // (non-base) class is extended, meaning that an explict Class or // AbstractClass was not provided if ( !( nonbase ) ) { throw TypeError( "Cannot instantiate incomplete class definition; did " + "you forget to call `extend'?" ); } return createMixedClass( basef(), traits ); }; // otherwise, its definition is deferred until additional context is // given during the extend operation partial.extend = function() { var an = arguments.length, dfn = arguments[ an - 1 ], ext_base = ( an > 1 ) ? arguments[ an - 2 ] : null, base = basef(); // extend the mixed class, which ensures that all super references // are properly resolved return extend.call( null, createMixedClass( ( base || ext_base ), traits ), dfn ); }; // syntatic sugar to avoid the aruduous and seemingly pointless `extend' // call simply to mix in another trait partial.use = function() { var args = [], i = arguments.length; while ( i-- ) args[ i ] = arguments[ i ]; return createUse( function() { return partial.__createBase(); }, args, nonbase ); }; // allows overriding default behavior partial.__createBase = function() { return partial.extend( {} ); }; partial.asPrototype = function() { return partialClass().asPrototype(); }; partial.__isInstanceOf = Interface.isInstanceOf; // allow the system to recognize this object as a viable base for // extending, but mark the metadata as lazy: since we defer all // processing for mixins, we cannot yet know all metadata // TODO: `_lazy' is a kluge ClassBuilder.masquerade( partial ); ClassBuilder.getMeta( partial )._lazy = true; return partial; } function createMixedClass( base, traits ) { // generated definition for our [abstract] class that will mix in each // of the provided traits; it will automatically be marked as abstract // if needed var dfn = { ___$$auto$abstract$$: true }; // this object is used as a class-specific context for storing trait // data; it will be encapsulated within a ctor closure and will not be // attached to any class var tc = []; // "mix" each trait into the class definition object for ( var i = 0, n = traits.length; i < n; i++ ) { traits[ i ].__mixin( dfn, tc, ( base || ClassBuilder.ClassBase ) ); } // create the mixed class from the above generated definition var C = extend.call( null, base, dfn ), meta = ClassBuilder.getMeta( C ); // add each trait to the list of implemented types so that the // class is considered to be of type T in traits var impl = meta.implemented; for ( var i = 0, n = traits.length; i < n; i++ ) { impl.push( traits[ i ] ); traits[ i ].__mixinImpl( impl ); } return C; } /** * Mimics class inheritance * * This method will mimic inheritance by setting up the prototype with the * provided base class (or, by default, Class) and copying the additional * properties atop of it. * * The class to inherit from (the first argument) is optional. If omitted, the * first argument will be considered to be the properties list. * * @param {Function|Object} _ parent or definition object * @param {Object=} __ definition object if parent was provided * * @return {Function} extended class */ function extend( _, __ ) { var args = [], i = arguments.length; // passing arguments object prohibits optimizations in v8 while ( i-- ) args[ i ] = arguments[ i ]; // set up the new class var new_class = class_builder.build.apply( class_builder, args ); // set up some additional convenience props setupProps( new_class ); // lock down the new class (if supported) to ensure that we can't add // members at runtime util.freeze( new_class ); return new_class; } /** * Implements interface(s) into an object * * This will copy all of the abstract methods from the interface and merge it * into the given object. * * @param {Object} baseobj base object * @param {...Function} interfaces interfaces to implement into dest * * @return {Object} destination object with interfaces implemented */ var implement = function( baseobj, interfaces ) { var an = arguments.length, dest = {}, base = arguments[ an - 1 ], arg = null, implemented = [], make_abstract = false ; // add each of the interfaces for ( var i = 0; i < ( an - 1 ); i++ ) { arg = arguments[ i ]; // copy all interface methods to the class (does not yet deep copy) util.propParse( arg.prototype, { method: function( name, func, is_abstract, keywords ) { dest[ 'abstract ' + name ] = func.definition; make_abstract = true; } } ); implemented.push( arg ); } // xxx: temporary if ( make_abstract ) { dest.___$$abstract$$ = true; } // create a new class with the implemented abstract methods var class_new = module.exports.extend( base, dest ); ClassBuilder.getMeta( class_new ).implemented = implemented; return class_new; } /** * Sets up common properties for the provided function (class) * * @param {function()} func function (class) to set up * * @return {undefined} */ function setupProps( func ) { attachExtend( func ); attachImplement( func ); attachUse( func ); } /** * Attaches extend method to the given function (class) * * @param {Function} func function (class) to attach method to * * @return {undefined} */ function attachExtend( func ) { /** * Shorthand for extending classes * * This method can be invoked on the object, rather than having to call * Class.extend( this ). * * @param {Object} props properties to add to extended class * * @return {Object} extended class */ util.defineSecureProp( func, 'extend', function( props ) { return extend( this, props ); }); } /** * Attaches implement method to the given function (class) * * Please see the implement() export of this module for more information. * * @param {function()} func function (class) to attach method to * * @return {undefined} */ function attachImplement( func ) { util.defineSecureProp( func, 'implement', function() { var args = [], i = arguments.length; while( i-- ) args[ i ] = arguments[ i ]; return createImplement( func, args ); }); } /** * Attaches use method to the given function (class) * * Please see the `use' export of this module for more information. * * @param {function()} func function (class) to attach method to * * @return {undefined} */ function attachUse( func ) { util.defineSecureProp( func, 'use', function() { var args = [], i = arguments.length; while( i-- ) args[ i ] = arguments[ i ]; return createUse( function() { return func; }, args, true ); } ); } } )( module['class'] = {}, '.' ); /** class_abstract **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * Wrapper permitting the definition of abstract classes * * This doesn't actually introduce any new functionality. Rather, it sets a * flag to allow abstract methods within a class, forcing users to clearly * state that a class is abstract. * * Copyright (C) 2010, 2011, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ var Class = require( './class' ); /** * Creates an abstract class * * @return {Function} abstract class */ module.exports = exports = function() { markAbstract( arguments[ arguments.length - 1 ] ); // forward everything to Class var result = Class.apply( this, arguments ); // if we're using the temporary object, then override its methods to permit // abstract classes if ( !Class.isClass( result ) ) { abstractOverride( result ); } return result; }; /** * Creates an abstract class from a class extend operation * * @return {Function} abstract class */ exports.extend = function() { markAbstract( arguments[ arguments.length - 1 ] ); return Class.extend.apply( this, arguments ); }; /** * Mixes in a trait * * @return {Object} staged abstract class */ exports.use = function() { return abstractOverride( Class.use.apply( this, arguments ) ); }; /** * Creates an abstract class implementing the given members * * Simply wraps the class module's implement() method. * * @return {Object} staged abstract class */ exports.implement = function() { return abstractOverride( Class.implement.apply( this, arguments ) ); }; /** * Causes a definition to be flagged as abstract * * @param {*} dfn suspected definition object * * @return {undefined} */ function markAbstract( dfn ) { if ( typeof dfn === 'object' ) { // mark as abstract dfn.___$$abstract$$ = true; } } /** * Overrides object members to permit abstract classes * * @param {Object} obj object to override * * @return {Object} obj */ function abstractOverride( obj ) { var extend = obj.extend, impl = obj.implement, use = obj.use; // wrap and apply the abstract flag, only if the method is defined (it // may not be under all circumstances, e.g. after an implement()) impl && ( obj.implement = function() { return abstractOverride( impl.apply( this, arguments ) ); } ); var mixin = false; use && ( obj.use = function() { return abstractOverride( use.apply( this, arguments ) ); } ); // wrap extend, applying the abstract flag obj.extend = function() { markAbstract( arguments[ arguments.length - 1 ] ); return extend.apply( this, arguments ); }; // used by mixins; we need to mark the intermediate subtype as abstract, // but ensure we don't throw any errors if no abstract members are mixed // in (since thay may be mixed in later on) obj.__createBase = function() { return extend( { ___$$auto$abstract$$: true } ); }; return obj; } } )( module['class_abstract'] = {}, '.' ); /** Trait **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * Provides system for code reuse via traits * * Copyright (C) 2014, 2015 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ var AbstractClass = require( './class_abstract' ), ClassBuilder = require( './ClassBuilder' ), Interface = require( './interface' ); function _fvoid() {}; /** * Trait constructor / base object * * The interpretation of the argument list varies by number. Further, * various trait methods may be used as an alternative to invoking this * constructor. * * @return {Function} trait */ function Trait() { switch ( arguments.length ) { case 0: throw Error( "Missing trait name or definition" ); case 1: return ( typeof arguments[ 0 ] === 'string' ) ? _createStaging.apply( this, arguments ) : Trait.extend.apply( this, arguments ); case 2: return createNamedTrait.apply( this, arguments ); } throw Error( "Expecting at most two arguments for definition of named " + "Trait " + name + "'; " + arguments.length + " given" ); }; /** * Create a named trait * * @param {string} name trait name * @param {Object} def trait definition * * @return {Function} named trait */ function createNamedTrait( name, dfn ) { if ( typeof name !== 'string' ) { throw Error( "First argument of named class definition must be a string" ); } dfn.__name = name; return Trait.extend( dfn ); } function _createStaging( name ) { return { extend: function( dfn ) { return createNamedTrait( name, dfn ); }, implement: function() { return createImplement( arguments, name ); } }; } Trait.extend = function( /* ... */ ) { var an = arguments.length, dfn = arguments[ an - 1 ], has_ext_base = ( an > 1 ), ext_base = ( has_ext_base ) ? arguments[ 0 ] : null; if ( an > 2 ) { throw Error( "Unexpected number of arguments to Trait.extend" ); } if ( has_ext_base ) { var basetype = typeof ext_base; if ( ( ext_base === null ) || !( ( basetype === 'object' ) || ( basetype === 'function' ) ) ) { throw TypeError( "Trait cannot extend base of type '" + basetype + "'" ); } // prevent extending final classes (TODO: abstract this check; see // also ClassBuilder) if ( ext_base.___$$final$$ === true ) { throw TypeError( "Trait cannot extend final class" ); } // TODO: this is intended to be temporary; see Trait/ClassExtendTest if ( module.exports.isTrait( ext_base ) ) { throw TypeError( "Traits cannot extend other traits" ); } } // we may have been passed some additional metadata var meta = ( this || {} ).__$$meta || {}; // store any provided name, since we'll be clobbering it (the definition // object will be used to define the hidden abstract class) var name = dfn.__name || '(Trait)', type = _getTraitType( dfn ), isparam = ( type === 'param' ); // augment the parser to handle our own oddities dfn.___$$parser$$ = { each: _parseMember, property: _parseProps, getset: _parseGetSet }; // automatically mark ourselves as abstract if an abstract method is // provided dfn.___$$auto$abstract$$ = true; // give the abstract trait class a distinctive name for debugging dfn.__name = '#AbstractTrait#'; // if __mixin was provided,in the definition, then we should crate a // paramaterized trait var Trait = ( isparam ) ? function ParameterTraitType() { // duplicate ars in a way that v8 can optimize var args = [], i = arguments.length; while ( i-- ) args[ i ] = arguments[ i ]; var AT = function ArgumentTrait() { throw Error( "Cannot re-configure argument trait" ); }; // TODO: mess! AT.___$$mixinargs = args; AT.__trait = 'arg'; AT.__acls = Trait.__acls; AT.__ccls = Trait.__ccls; AT.toString = Trait.toString; AT.__mixinImpl = Trait.__mixinImpl; AT.__isInstanceOf = Trait.__isInstanceOf; // mix in the argument trait instead of the original AT.__mixin = function( dfn, tc, base ) { mixin( AT, dfn, tc, base ); }; return AT; } : function TraitType() { throw Error( "Cannot instantiate non-parameterized trait" ); }; // implement interfaces if indicated var base = AbstractClass; if ( meta.ifaces ) { base = base.implement.apply( null, meta.ifaces ); } // and here we can see that traits are quite literally abstract classes var tclass = base.extend( dfn ); Trait.__trait = type; Trait.__acls = tclass; Trait.__ccls = null; Trait.__extbase = ext_base; Trait.toString = function() { return ''+name; }; // we're not a param trait, but we want consistent data Trait.___$$mixinargs = []; // invoked to trigger mixin Trait.__mixin = function( dfn, tc, base ) { mixin( Trait, dfn, tc, base ); }; // mixes in implemented types Trait.__mixinImpl = function( dest_meta ) { mixinImpl( tclass, dest_meta ); }; // TODO: this and the above should use util.defineSecureProp Trait.__isInstanceOf = Interface.isInstanceOf; return Trait; }; /** * Validate whether mixin is permitted * * If a mixee (the trait being mixed in) extends some type S, then a * contract has been created mandating that that trait may only be mixed * into something of type S; a `TypeError` will be thrown if this contract * is violated. * * @param {Class} base mixor (target of mixin) * @param {Trait} T mixee (trait being mixed in) * * @return {undefined} * * @throws {TypeError} on type contract violation */ function _validateMixin( base, T ) { if ( !T.__extbase ) { return; } // TODO: isSubtypeOf if ( !( ( T.__extbase === base ) || ClassBuilder.isInstanceOf( T.__extbase, base.asPrototype() ) ) ) { throw TypeError( "Cannot mix trait " + T.toString() + " into " + base.toString() + "; mixor must be of type " + T.__extbase.toString() ); } } /** * Retrieve a string representation of the trait type * * A trait is parameterized if it has a __mixin method; otherwise, it is * standard. * * @param {Object} dfn trait definition object * @return {string} trait type */ function _getTraitType( dfn ) { return ( typeof dfn.__mixin === 'function' ) ? 'param' : 'std'; } /** * Verifies trait member restrictions * * @param {string} name property name * @param {*} value property value * @param {Object} keywords property keywords * @param {Function} h original handler that we replaced * * @return {undefined} */ function _parseMember( name, value, keywords, h ) { // traits are not permitted to define constructors if ( name === '__construct' ) { throw Error( "Traits may not define __construct" ); } // will be supported in future versions if ( keywords['static'] ) { throw Error( "Cannot define member `" + name + "'; static trait " + "members are currently unsupported" ); } // apply original handler h.apply( this, arguments ); } /** * Throws error if non-internal property is found within PROPS * * For details and rationale, see the Trait/PropertyTest case. * * @param {string} name property name * @param {*} value property value * @param {Object} keywords property keywords * @param {Function} h original handler that we replaced * * @return {undefined} */ function _parseProps( name, value, keywords, h ) { // ignore internal properties if ( name.substr( 0, 3 ) === '___' ) { return; } if ( !( keywords['private'] ) ) { throw Error( "Cannot define property `" + name + "'; only private " + "properties are permitted within Trait definitions" ); } // apply original handler h.apply( this, arguments ); } /** * Immediately throws an exception, as getters/setters are unsupported * * This is a temporary restriction; they will be supported in future * releases. * * @param {string} name property name * @param {*} value property value * @param {Object} keywords property keywords * @param {Function} h original handler that we replaced * * @return {undefined} */ function _parseGetSet( name, value, keywords, h ) { throw Error( "Cannot define property `" + name + "'; getters/setters are " + "currently unsupported" ); } /** * Implement one or more interfaces * * Implementing an interface into a trait has the same effect as it does * within classes in that it will automatically define abstract methods * unless a concrete method is provided. Further, the class that the trait * is mixed into will act as though it implemented the interfaces. * * @param {...Function} interfaces interfaces to implement * * @return {Object} staged trait object */ Trait.implement = function() { return createImplement( arguments ); }; /** * Create a staging object from which a trait implementing a set of * interfaces may be defined * * @param {...Function} interfaces interfaces to implement * @param {string=} name optional trait name * * @return {Object} staged trait object */ function createImplement( ifaces, name ) { return { extend: function( dfn ) { if ( name ) { dfn.__name = name; } // pass our interface metadata as the invocation context return Trait.extend.call( { __$$meta: { ifaces: ifaces } }, dfn ); } }; } /** * Determines if the provided value references a trait * * @param {*} trait value to check * @return {boolean} whether the provided value references a trait */ Trait.isTrait = function( trait ) { return !!( trait || {} ).__trait; }; /** * Determines if the provided value references a parameterized trait * * @param {*} trait value to check * @return {boolean} whether the provided value references a param trait */ Trait.isParameterTrait = function( trait ) { return !!( ( trait || {} ).__trait === 'param' ); }; /** * Determines if the provided value references an argument trait * * An argument trait is a configured parameter trait. * * @param {*} trait value to check * @return {boolean} whether the provided value references an arg trait */ Trait.isArgumentTrait = function( trait ) { return !!( ( trait || {} ).__trait === 'arg' ); }; /** * Create a concrete class from the abstract trait class * * This class is the one that will be instantiated by classes that mix in * the trait. * * @param {AbstractClass} acls abstract trait class * * @return {Class} concrete trait class for instantiation */ function createConcrete( acls ) { // start by providing a concrete implementation for our dummy method and // a constructor that accepts the protected member object of the // containing class var dfn = { // protected member object (we define this as protected so that the // parent ACLS has access to it (!), which is not prohibited since // JS does not provide a strict typing mechanism...this is a kluge) // and target supertype---that is, what __super calls should // referene 'protected ___$$pmo$$': null, 'protected ___$$super$$': null, __construct: function( base, pmo ) { this.___$$super$$ = base; this.___$$pmo$$ = pmo; }, // mainly for debugging; should really never see this. __name: '#ConcreteTrait#' }; // every abstract method should be overridden with a proxy to the // protected member object that will be passed in via the ctor var amethods = ClassBuilder.getMeta( acls ).abstractMethods; for ( var f in amethods ) { // TODO: would be nice if this check could be for '___'; need to // replace amethods.__length with something else, then if ( !( Object.hasOwnProperty.call( amethods, f ) ) || ( f.substr( 0, 2 ) === '__' ) ) { continue; } // we know that if it's not public, then it must be protected var vis = ( acls.___$$methods$$['public'][ f ] !== undefined ) ? 'public' : 'protected'; // setting the correct visibility modified is important to prevent // visibility de-escalation errors if a protected concrete method is // provided dfn[ vis + ' proxy ' + f ] = '___$$pmo$$'; } // virtual methods need to be handled with care to ensure that we invoke // any overrides createVirtProxy( acls, dfn ); return acls.extend( dfn ); } /** * Create virtual method proxies for all virtual members * * Virtual methods are a bit of hassle with traits: we are in a situation * where we do not know at the time that the trait is created whether or not * the virtual method has been overridden, since the class that the trait is * mixed into may do the overriding. Therefore, we must check if an override * has occured *when the method is invoked*; there is room for optimization * there (by making such a determination at the time of mixin), but we'll * leave that for later. * * @param {AbstractClass} acls abstract trait class * @param {Object} dfn destination definition object * * @return {undefined} */ function createVirtProxy( acls, dfn ) { var vmembers = ClassBuilder.getMeta( acls ).virtualMembers; // f = `field' for ( var f in vmembers ) { var vis = ( acls.___$$methods$$['public'][ f ] !== undefined ) ? 'public' : 'protected'; var srcmethod = acls.___$$methods$$[ vis ][ f ], plen = srcmethod.__length; // this is the aforementioned proxy method; see the docblock for // more information dfn[ vis + ' virtual override ' + f ] = ( function( f ) { var retf = function() { var pmo = this.___$$pmo$$, o = pmo[ f ]; // proxy to virtual override from the class we are mixed // into, if found; otherwise, proxy to our supertype return ( o ) ? o.apply( pmo, arguments ) : this.__super.apply( this, arguments ); }; retf.__length = plen; return retf; } )( f ); // this guy bypasses the above virtual override check, which is // necessary in certain cases to prevent infinte recursion dfn[ vis + ' virtual __$$' + f ] = ( function( method ) { var retf = function() { return method.apply( this, arguments ); }; retf.__length = plen; return retf; } )( srcmethod ); } } /** * Mix trait into the given definition * * The original object DFN is modified; it is not cloned. TC should be * initialized to an empty array; it is used to store context data for * mixing in traits and will be encapsulated within a ctor closure (and thus * will remain in memory). * * @param {Trait} trait trait to mix in * @param {Object} dfn definition object to merge into * @param {Array} tc trait class context * @param {Class} base target supertype * * @return {Object} dfn */ function mixin( trait, dfn, tc, base ) { _validateMixin( base, trait ); // the abstract class hidden within the trait var acls = trait.__acls; // retrieve the private member name that will contain this trait object var iname = addTraitInst( trait, dfn, tc, base ); // TODO: this should not be necessary for dfn metadata dfn[ 'weak virtual ___$$ctor$pre$$' ] = _fvoid; dfn[ 'weak virtual ___$$ctor$post$$' ] = _fvoid; // TODO: this is a kluge; generalize and move // this ensures __construct is called before __mixin when mixing into // the base class if ( base === ClassBuilder.ClassBase ) { dfn[ 'virtual override ___$$ctor$post$$' ] = _tctorApply; dfn[ 'virtual override ___$$ctor$pre$$' ] = _fvoid; } else { dfn[ 'virtual override ___$$ctor$post$$' ] = _fvoid; dfn[ 'virtual override ___$$ctor$pre$$' ] = _tctorApply; } // recursively mix in trait's underlying abstract class (ensuring that // anything that the trait inherits from is also properly mixed in) mixinCls( acls, dfn, iname ); return dfn; } /** * Recursively mix in class methods * * If CLS extends another class, its methods will be recursively processed * to ensure that the entire prototype chain is properly proxied. * * For an explanation of the iname parameter, see the mixin function. * * @param {Class} cls class to mix in * @param {Object} dfn definition object to merge into * @param {string} iname trait object private member instance name * * @return {undefined} */ function mixinCls( cls, dfn, iname ) { var methods = cls.___$$methods$$; mixMethods( methods['public'], dfn, 'public', iname ); mixMethods( methods['protected'], dfn, 'protected', iname ); // if this class inherits from another class that is *not* the base // class, recursively process its methods; otherwise, we will have // incompletely proxied the prototype chain var parent = methods['public'].___$$parent$$; if ( parent && ( parent.constructor !== ClassBuilder.ClassBase ) ) { mixinCls( parent.constructor, dfn, iname ); } } /** * Mix implemented types into destination object * * The provided destination object will ideally be the `implemented' array * of the destination class's meta object. * * @param {Class} cls source class * @param {Object} dest_meta destination object to copy into * * @return {undefined} */ function mixinImpl( cls, dest_meta ) { var impl = ClassBuilder.getMeta( cls ).implemented || [], i = impl.length; while ( i-- ) { // TODO: this could potentially result in duplicates dest_meta.push( impl[ i ] ); } } /** * Mix methods from SRC into DEST using proxies * * @param {Object} src visibility object to scavenge from * @param {Object} dest destination definition object * @param {string} vis visibility modifier * @param {string} iname proxy destination (trait instance) * * @return {undefined} */ function mixMethods( src, dest, vis, iname ) { for ( var f in src ) { if ( !( Object.hasOwnProperty.call( src, f ) ) ) { continue; } // TODO: generalize // __mixin is exclusive to the trait (private-ish, but can be // invoked publically internally) if ( f === '__mixin' ) { continue; } // TODO: this is a kluge; we'll use proper reflection eventually, // but for now, this is how we determine if this is an actual method // vs. something that just happens to be on the visibility object if ( !( src[ f ].___$$keywords$$ ) ) { continue; } var keywords = src[ f ].___$$keywords$$, vis = keywords['protected'] ? 'protected' : 'public'; // if abstract, then we are expected to provide the implementation; // otherwise, we proxy to the trait's implementation if ( keywords[ 'abstract' ] && !( keywords[ 'override' ] ) ) { // copy the abstract definition (N.B. this does not copy the // param names, since that is not [yet] important); the // visibility modified is important to prevent de-escalation // errors on override dest[ vis + ' weak abstract ' + f ] = src[ f ].definition; } else { var vk = keywords['virtual'], virt = vk ? 'virtual ' : '', ovr = ( keywords['override'] ) ? 'override ' : '', pname = ( vk ? '' : 'proxy ' ) + virt + ovr + vis + ' ' + f; // if we have already set up a proxy for a field of this name, // then multiple traits have defined the same concrete member if ( dest[ pname ] !== undefined ) { // TODO: between what traits? throw Error( "Trait member conflict: `" + f + "'" ); } // if non-virtual, a normal proxy should do if ( !( keywords[ 'virtual' ] ) ) { dest[ pname ] = iname; continue; } // proxy this method to what will be the encapsulated trait // object (note that we do not use the proxy keyword here // beacuse we are not proxying to a method of the same name) dest[ pname ] = ( function( f ) { var retf = function() { var pdest = this[ iname ]; // invoke the direct method on the trait instance; this // bypasses the virtual override check on the trait // method to ensure that it is invoked without // additional overhead or confusion var ret = pdest[ '__$$' + f ].apply( pdest, arguments ); // if the trait returns itself, return us instead return ( ret === pdest ) ? this : ret; }; retf.__length = src[ f ].__length; return retf; } )( f ); } } } /** * Add concrete trait class to a class instantion list * * This list---which will be created if it does not already exist---will be * used upon instantiation of the class consuming DFN to instantiate the * concrete trait classes. * * Here, `tc' and `to' are understood to be, respectively, ``trait class'' * and ``trait object''. * * @param {Class} T trait * @param {Object} dfn definition object of class being mixed into * @param {Array} tc trait class object * @param {Class} base target supertyep * * @return {string} private member into which C instance shall be stored */ function addTraitInst( T, dfn, tc, base ) { var base_cid = base.__cid; // creates a property of the form ___$to$N$M to hold the trait object // reference; M is required because of the private member restrictions // imposed to be consistent with pre-ES5 fallback var iname = '___$to$' + T.__acls.__cid + '$' + base_cid; // the trait object array will contain two values: the destination field // and the trait to instantiate tc.push( [ iname, T ] ); // we must also add the private field to the definition object to // support the object assignment indicated by TC dfn[ 'private ' + iname ] = null; // create internal trait ctor if not available if ( dfn.___$$tctor$$ === undefined ) { // TODO: let's check for inheritance or something to avoid this weak // definition (this prevents warnings if there is not a supertype // that defines the trait ctor) dfn[ 'weak virtual ___$$tctor$$' ] = function() {}; dfn[ 'virtual override ___$$tctor$$' ] = createTctor( tc, base ); } return iname; } /** * Trait initialization constructor * * May be used to initialize all traits mixed into the class that invokes * this function. All concrete trait classes are instantiated and their * resulting objects assigned to their rsepective pre-determined field * names. * * The MIXINARGS are only useful in the case of parameterized trait. * * This will lazily create the concrete trait class if it does not already * exist, which saves work if the trait is never used. * * Note that the private symbol used to encapsulate class data must be * passed to this function to provide us access to implementation details * that we really shouldn't be messing around with. :) In particular, we * need access to the protected visibility object, and there is [currently] * no API for doing so. * * @param {Object} tc trait class list * @param {Class} base target supertype * @param {Symbol} privsym symbol used as key for encapsulated data * * @return {undefined} */ function tctor( tc, base, privsym ) { // instantiate all traits and assign the object to their // respective fields for ( var t in tc ) { var f = tc[ t ][ 0 ], T = tc[ t ][ 1 ], C = T.__ccls || ( T.__ccls = createConcrete( T.__acls ) ); // instantiate the trait, providing it with our protected visibility // object so that it has access to our public and protected members // (but not private); in return, we will use its own protected // visibility object to gain access to its protected members...quite // the intimate relationship this[ f ] = C( base, this[ privsym ].vis )[ privsym ].vis; // this has been previously validated to ensure that it is a // function this[ f ].__mixin && this[ f ].__mixin.apply( this[ f ], T.___$$mixinargs ); } // if we are a subtype, be sure to initialize our parent's traits this.__super && this.__super( privsym ); }; /** * Create trait constructor * * This binds the generic trait constructor to a reference to the provided * trait class list. * * @param {Object} tc trait class list * @param {Class} base target supertype * * @return {function()} trait constructor */ function createTctor( tc, base ) { return function( privsym ) { return tctor.call( this, tc, base, privsym ); }; } function _tctorApply() { this.___$$tctor$$.apply( this, arguments ); } module.exports = Trait; } )( module['Trait'] = {}, '.' ); /** class_final **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * Wrapper permitting the definition of final classes * * Copyright (C) 2010, 2011, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ var Class = require( './class' ); /** * Creates a final class * * @return {Function} final class */ exports = module.exports = function() { markFinal( arguments[ arguments.length - 1 ] ); // forward everything to Class var result = Class.apply( this, arguments ); if ( !Class.isClass( result ) ) { finalOverride( result ); } return result; }; /** * Creates a final class from a class extend operation * * @return {Function} final class */ exports.extend = function() { markFinal( arguments[ arguments.length - 1 ] ); return Class.extend.apply( this, arguments ); }; /** * Causes a definition to be flagged as final * * @param {!Arguments} dfn suspected definition object * * @return {undefined} */ function markFinal( dfn ) { if ( typeof dfn === 'object' ) { // mark as abstract dfn.___$$final$$ = true; } } /** * Overrides object members to permit final classes * * @param {Object} obj object to override * * @return {undefined} */ function finalOverride( obj ) { var extend = obj.extend; // wrap extend, applying the abstract flag obj.extend = function() { markFinal( arguments[ arguments.length - 1 ] ); return extend.apply( this, arguments ); }; } } )( module['class_final'] = {}, '.' ); /** FallbackMemberBuilder **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * Handles building members (properties, methods) in a pre-ES5 environment * * Copyright (C) 2011, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * Supertype */ var MemberBuilder = require( './MemberBuilder' ); /** * Responsible for building class members */ module.exports = exports = function FallbackMemberBuilder( wrap_method, wrap_override ) { // permit omitting 'new' keyword if ( !( this instanceof module.exports ) ) { return new module.exports( wrap_method, wrap_override ); } // invoke parent constructor module.exports.prototype.constructor.call( this, wrap_method, wrap_override ); }; // inherit from MemberBuilder module.exports.prototype = new MemberBuilder(); module.exports.constructor = module.exports; /** * Getters/setters are unsupported in a pre-ES5 environment * * Simply throw an exception, as it clearly represents that the developer did * not account for the possibility that their software may have been executed in * a pre-ES5 environment. */ exports.prototype.buildGetterSetter = function() { throw Error( 'Getters/setters are unsupported in this environment' ); }; } )( module['FallbackMemberBuilder'] = {}, '.' ); /** util/Global **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = 'util'; /** * Global scope handling * * Copyright (C) 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ // retrieve global scope; works with ES5 strict mode (0,eval)( 'var _the_global=this' ); // prototype to allow us to augment the global scope for our own purposes // without polluting the global scope function _G() {} _G.prototype = _the_global; /** * Provides access to and augmentation of global variables * * This provides a static method to consistently provide access to the * object representing the global scope, regardless of environment. Through * instantiation, its API permits augmenting a local object whose prototype * is the global scope, providing alternatives to variables that do not * exist. */ function Global() { // allows omitting `new` keyword, consistent with ease.js style if ( !( this instanceof Global ) ) { return new Global(); } // do not pollute the global scope (previously, _the_global was used as // the prototype for a new object to take advantage of native overrides, // but unfortunately IE<=8 did not support this and always returned // undefined values from the prototype). this._alt = {}; } /** * Provides consistent access to the global scope through all ECMAScript * versions, for any root variable name, and works with ES5 strict mode. * * As an example, Node.js exposes the variable `root` to represent global * scope, but browsers expose `window`. Further, ES5 strict mode will * provide an error when checking whether `typeof SomeGlobalVar === * 'undefined'`. * * @return {Object} global object */ Global.expose = function() { return _the_global; }; Global.prototype = { /** * Provide a value for the provided global variable name if it is not * defined * * A function returning the value to assign to NAME should be provided, * ensuring that the alternative is never even evaluated unless it is * needed. * * The global scope will not be polluted with this alternative; * consequently, you must access the value using the `get` method. * * @param {string} name global variable name * @param {function()} f function returning value to assign * * @return {Global} self */ provideAlt: function( name, f ) { if ( ( _the_global[ name ] !== undefined ) || ( this._alt[ name ] !== undefined ) ) { return; } this._alt[ name ] = f(); return this; }, /** * Retrieve global value or provided alternative * * This will take into account values provided via `provideAlt`; if no * alternative was provided, the request will be deleagated to the * global variable NAME, which may or may not be undefined. * * No error will be thrown if NAME is not globally defined. * * @param {string} name global variable name * * @return {*} value associated with global variable NAME or * its provided alternative */ get: function( name ) { return ( this._alt[ name ] !== undefined ) ? this._alt[ name ] : _the_global[ name ]; } }; module.exports = Global; } )( module['util/Global'] = {}, '.' ); /** util/Symbol **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = 'util'; /** * Forward-compatible subset of ES6 Symbol * * Copyright (C) 2010, 2011, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This is *not* intended to be a complete implementation; it merely * performs what is needed for ease.js, preferring the benefits of the ES6 * Symbol implementation while falling back to sane ES5 and ES3 options. */ // to be used if there is no global Symbol available var FallbackSymbol = require( './symbol/FallbackSymbol' ); var _root = require( './Global' ).expose(); module.exports = _root.Symbol || FallbackSymbol; } )( module['util/Symbol'] = {}, '.' ); /** util/symbol/FallbackSymbol **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = 'util/symbol'; /** * Forward-compatible subset of ES6 Symbol for pre-ES6 environments * * Copyright (C) 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * This is *not* intended to be a complete implementation; it merely * performs what is needed for ease.js. In particular, this pre-ES6 * implementation will simply generate a random string to be used as a key; * the caller is expected to add the key to the destination object as * non-enumerable, if supported by the environment. */ // ensures that, so long as these methods have not been overwritten by the // time ease.js is loaded, we will maintain a proper reference var _random = Math.random, _floor = Math.floor; // prefix used for all generated symbol strings (this string is highly // unlikely to exist in practice); it will produce a string containing a // non-printable ASCII character that is *not* the null byte var _root = ' ' + String.fromCharCode( _floor( _random() * 10 ) % 31 + 1 ) + '$'; /** * Generate a pseudo-random string (with a common prefix) to be used as an * object key * * The returned key is unique so long as Math.{random,floor} are reliable. * This will be true so long as (1) the runtime provides a reliable * implementation and (2) Math.{floor,random} have not been overwritten at * the time that this module is loaded. This module stores an internal * reference to this methods, so malicious code loaded after this module * will not be able to compromise the return value. * * Note that the returned string is not wholly random: a common prefix is * used to ensure that collisions with other keys on objects is highly * unlikely; you should not rely on this behavior, though, as it is an * implementation detail that may change in the future. * * @return {string} pseudo-random string with common prefix */ function FallbackSymbol() { if ( !( this instanceof FallbackSymbol ) ) { return new FallbackSymbol(); } this.___$$id$$ = ( _root + _floor( _random() * 1e8 ) ); } FallbackSymbol.prototype = { /** * Return random identifier * * This is convenient, as it allows us to both treat the symbol as an * object of type FallbackSymbol and use the symbol as a key (since * doing so will automatically call this method). * * @return {string} random identifier */ toString: function() { return this.___$$id$$; } }; module.exports = FallbackSymbol; } )( module['util/symbol/FallbackSymbol'] = {}, '.' ); /** version **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = '.'; /** * Provides version information * * Copyright (C) 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . * * @author Mike Gerwitz */ var major = 0, minor = 2, rev = 6, suffix = '', version = [ major, minor, rev, suffix ]; version.major = major; version.minor = minor; version.rev = rev; version.suffix = suffix; version.toString = function() { return this.join( '.' ) .replace( /\.([^.]*)$/, '-$1' ) .replace( /-$/, '' ); }; module.exports = version; } )( module['version'] = {}, '.' ); /** warn/DismissiveHandler **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = 'warn'; /** * Dismissive warning handler * * Copyright (C) 2010, 2011, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * Warning handler that dismisses (ignores) all warnings * * This is useful in a production environment. */ function DismissiveHandler() { if ( !( this instanceof DismissiveHandler ) ) { return new DismissiveHandler(); } } DismissiveHandler.prototype = { /** * Handle a warning * * @param {Warning} warning warning to handle * @return {undefined} */ handle: function( warning ) { // intentionally do nothing } } module.exports = DismissiveHandler; } )( module['warn/DismissiveHandler'] = {}, '.' ); /** warn/LogHandler **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = 'warn'; /** * Logging warning handler * * Copyright (C) 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * Warning handler that logs all warnings to a console * * @param {Object} console console with a warn or log method */ function LogHandler( console ) { if ( !( this instanceof LogHandler ) ) { return new LogHandler( console ); } this._console = console || {}; } LogHandler.prototype = { /** * Handle a warning * * Will attempt to log using console.warn(), falling back to * console.log() if necessary and aborting entirely if neither is * available. * * This is useful as a default option to bring problems to the * developer's attention without affecting the control flow of the * software. * * @param {Warning} warning warning to handle * @return {undefined} */ handle: function( warning ) { var dest = this._console.warn || this._console.log; dest && dest.call( this._console, 'Warning: ' + warning.message ); } } module.exports = LogHandler; } )( module['warn/LogHandler'] = {}, '.' ); /** warn/ThrowHandler **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = 'warn'; /** * Throwing warning handler * * Copyright (C) 2010, 2011, 2013, 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * Warning handler that throws all warnings as exceptions */ function ThrowHandler() { if ( !( this instanceof ThrowHandler ) ) { return new ThrowHandler(); } } ThrowHandler.prototype = { /** * Handle a warning * * Throws the error associated with the warning. * * This handler is useful for development and will ensure that problems * are brought to the attention of the developer. * * @param {Warning} warning warning to handle * @return {undefined} */ handle: function( warning ) { throw warning.getError(); } } module.exports = ThrowHandler; } )( module['warn/ThrowHandler'] = {}, '.' ); /** warn/Warning **/ ( function( module, __dirname ) { var exports = module.exports = {}; __cwd = 'warn'; /** * Warning prototype * * Copyright (C) 2014 Free Software Foundation, Inc. * * This file is part of GNU ease.js. * * ease.js is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * Permits wrapping an exception as a warning * * Warnings are handled differently by the system, depending on the warning * level that has been set. * * @param {Error} e exception (error) to wrap * * @return {Warning} new warning instance * * @constructor */ function Warning( e ) { // allow instantiation without use of 'new' keyword if ( !( this instanceof Warning ) ) { return new Warning( e ); } // ensure we're wrapping an exception if ( !( e instanceof Error ) ) { throw TypeError( "Must provide exception to wrap" ); } Error.prototype.constructor.call( this, e.message ); // copy over the message for convenience this.message = e.message; this.name = 'Warning'; this._error = e; this.stack = e.stack && e.stack.replace( /^.*?\n+/, this.name + ': ' + this.message + "\n" ); }; // ensures the closest compatibility...just be careful not to modify Warning's // prototype Warning.prototype = Error(); Warning.prototype.constructor = Warning; Warning.prototype.name = 'Warning'; /** * Return the error wrapped by the warning * * @return {Error} wrapped error */ Warning.prototype.getError = function() { return this._error; }; module.exports = Warning; } )( module['warn/Warning'] = {}, '.' ); // the following should match the exports of /index.js ns_exports.Class = module['class'].exports; ns_exports.AbstractClass = module['class_abstract'].exports; ns_exports.FinalClass = module['class_final'].exports; ns_exports.Interface = module['interface'].exports; ns_exports.Trait = module['Trait'].exports; ns_exports.version = module['version'].exports; } )( easejs, '.' ); .