diff --git a/lib/util/symbol/FallbackSymbol.js b/lib/util/symbol/FallbackSymbol.js new file mode 100644 index 0000000..ab20849 --- /dev/null +++ b/lib/util/symbol/FallbackSymbol.js @@ -0,0 +1,66 @@ +/** + * 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() +{ + return _root + _floor( _random() * 1e8 ); +} + + +module.exports = FallbackSymbol; + diff --git a/test/Util/symbol/FallbackSymbolTest.js b/test/Util/symbol/FallbackSymbolTest.js new file mode 100644 index 0000000..a10aa35 --- /dev/null +++ b/test/Util/symbol/FallbackSymbolTest.js @@ -0,0 +1,76 @@ +/** + * Tests pre-ES6 fallback symbol subset + * + * 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 . + */ + + +require( 'common' ).testCase( +{ + caseSetUp: function() + { + this.Sut = this.require( 'util/symbol/FallbackSymbol' ); + }, + + + /** + * Symbols are used to create an object fields that is accessible only + * to the holder of a reference to the symbol used to create that field. + * Since this fallback is intended to be used in environments that do + * not support symbols, the alternative is to return a random string + * that is highly unlikely to exist in practice. + */ + 'Constructor returns a generated string': function() + { + var result = this.Sut(); + + this.assertOk( typeof result === 'string' ); + this.assertOk( result.length > 0 ); + }, + + + /** + * The generated string should be unique for each call, making it + * unlikely that its value can be guessed. Of course, this relies on the + * assumption that the runtime's PRNG is reliable and that it has not + * been maliciously rewritten. + * + * Note that we don't test the various implementation details, as that + * is intended to be opaque (see SUT source for details). + */ + 'Generated string varies with each call': function() + { + var gen = {}, + i = 32; + + while ( i-- ) + { + var result = this.Sut(); + if ( gen[ result ] ) + { + this.fail( result, '' ); + } + + gen[ result ] = true; + } + + // this prevents the test from being marked as incomplete + this.assertOk( 'passed' ); + }, +} ); +