reflect-rdeprecated

_R is a powerful reflection library with support for lamba-lifting, modyfing code flow in fly.

Usage no npm install needed!

<script type="module">
  import reflectR from 'https://cdn.skypack.dev/reflect-r';
</script>

README

Table of Contents generated with DocToc

_R

_R is a reflection library.

Installation

npm install reflect-r

Node.js:

var _R = require('reflect-r');

Browsers:

<script src="./reflect-helpers.js"></script>

Running tests

gulp test

Building project

gulp build

Compatibility, requirements

_R requires following ES5/ES6 features:

Required functions (some functions can not work without them)

  • Object.defineProperty
  • Object.getOwnPropertyDescriptor
  • JSON.stringify (can be polyfilled)

Recommended functions (some functions can work incorrectly without them)

  • Object.getPrototypeOf
  • Object.getOwnPropertyNames

Tested environments

These enviroments pass automatic tests.

  • Node 0.8.14
  • Firefox 33
  • Chrome 36

See section 'Implementation dependant' for futher information.

Test coverage

  • Statements : 91.1% (215/236)
  • Branches : 70.41% (69/98)
  • Functions : 88.68% (47/53)
  • Lines : 90.99% (212/233)

_R settings

_R.$setDirective(directive)

Changes directive placed in every function.

Available directives

_R.DIRECTIVE_NORMAL // ''; is placed before every new function
_R.DIRECTIVE_STRICT // 'use strict'; is placed before every function; default
_R.DIRECTIVE_ASM    // 'use asm'; is placed before every new function

Implementation dependant

  • _R.isValidVariableName tests variable name against current implementation rules.
  • _R.isBoundOrNativeFunction is slow in V8 (due to V8's incompatibility with ES6 spec).
  • _R.getObjectPrototype can fail in IE8 and lower. Internally it prefers Object.getPrototypeOf over .__proto__ over .constructor.prototype.

_R methods

Checks and tests

isValidVariableName

_R.isValidVariableName(name)

Checks if supplied name is a valid variable identifier in current JS implementation.

isBoundOrNativeFunction

_R.isBoundOrNativeFunction(func)

Checks if supplied func is bound (.bind) or native code.

Retrieve informations

getFunctionSourceCode

_R.getFunctionSourceCode(func)

Returns FunctionExpression. Throws error when called on non-function, bound function or native-code function.

getInternalClass

_R.getInternalClass(what)

Returns [[Class]]'s name of what.

getObjectPrototype

_R.getObjectPrototype(what)

If what is an object, returns it's prototype. Otherwise, returns null. Can return invalid object in IE8 and lower.

getPrototypesChain

_R.getPrototypesChain(what)

If what is an object, returns array containing what and objects in it's prototype chain (array ends with null). Otherwise, return [what, null]. When cyclical reference is detected (possible in IE8 and lower), function returns with current prototypes list.

Modify functions

declosureFunction

_R.declosureFunction(func[, transformer]);

Returns func redefined in global context. transformer function is called on source code of func before code evaluation.

transformer

function transformer(sourceCode, originalFunction) { /* magic */ return transformedSourceCode; }

createNamedFunction

_R.createNamedFunction(name, [...argsNames[, sourceCode]])

Works like Function constructor but first argument is the function name (used in recursive calls).

createClosure

_R.createClosure(func, context, name)

Creates closure in given context. Example:

console.log(window.$) // undefined
var showFactorial = _R.createClosure(
    function (n) {
        if (!n || n === 1)
            return 1;
        var curr = n*factorial(n-1);
        $('div.factorial').html(curr);
        return curr;
    },
    {
        $: jQuery
    },
    'factorial'
);
showFactorial(5);

makeGeneric

_R.makeGeneric(func);

Changes method to function accepting this as first argument.

Usage example

var slice = _R.makeGeneric([].slice);
slice(arguments, 2, 7);

wrapFunction

_R.wrapFunction(func, before, after, [dataObject])

Returns new function. New function pseudocode:

function newFunc() {
var error = null;
var data = null;
var newArgs = before(func, this, arguments, dataObject) || arguments;
try {
data = func.applyOrConstruct(this, newArgs);
} catch(e){
error = e;
}
var afterResult = after(func, this, error, data, newArgs, dataObject);
data = afterResult || data;
return data;
}

before

function before(func, thisArg, funcArguments, dataObject) {}

before function can return array or modify funcArguments object. It will be used by wraped function.

after

function after(func, thisArg, funcError, funcResult, funcArguments, dataObject) {}

Usage example

$ = jQuery = _R.wrapFunction(
    jQuery,
    function($,ignored1,ignored2,ignored3, data){
        data.startTime = Date.now();
    },
    function($,ignored1,ignored2,ignored3, data) {
        console.log('jQuery function took '+(Date.now() - data.startTime)+' miliseconds to run.');
    },
    {})

Utility

indirectEval

_R.indirectEval(code[, preparationCode]);

Works like eval but:

  • code scope and context always follow rules for ECMAScript strict mode eval
  • if preparationCode isn't supplied, code is run with global settings directive (default: 'use strict')

ES6 Reflect methods

construct

_R.construct(constructor, args)

This function follows specification of Reflect.construct from ES6 (26.1.2).

Usage example

_R.construct(Date, [30,3,1990]);

has

_R.has(obj, key)

This function follows specification of Reflect.has from ES6 (26.1.9).

apply

_R.apply(function, target, args);

This function follows specification of Reflect.apply from ES6 (26.1.3).

Create and modify objects

Proxy

new _R.Proxy(target, getHandler, setHandler);

Creates proxy object for target. Proxy objects are sealed (Object.seal).

createProxy

_R.createProxy(target, getHandler, setHandler);

Alias for new _R.Proxy.

defaultSetter

_R.defaultSetter

Transfers assigment to original object.

Usage example

function Circle(r) {
    'strict mode';
    this.diameter = undefined;  // property have to exist 
    this.area = undefined;      // property have to exist
    this.radius = r;
    return _R.createProxy(this, Circle.getter, Circle.setter);
}
Circle.getter = function circleGetter(originalObject, proxyObject, propertyName) {
    if (propertyName === 'radius') {
        return originalObject.radius;
    }
    if (propertyName === 'diameter') {
        return proxyObject.radius*2;
    }
    if (propertyName === 'area') {
        return proxyObject.radius*proxyObject.radius*Math.PI;
    }
};

Circle.setter = function circleSetter(originalObject, proxyObject, propertyName, propertyValue) {
    if (propertyName !== 'radius') {
        throw Error('You can not modify anything in circle except radius');
    }
    else {
        return originalObject.radius = propertyValue;
    }
};

var k = new Circle(5);
k.radius === k.diameter*2; // true
k.diameter = 7; // Always throw Error: You can not modify anything in circle except radius
k.oh = 'hai'; // Error in strict mode; Does nothing in sloppy mode
k.radius = 11; // works
console.log(k.diameter); // 22

forbidPropertyNames

_R.forbidPropertyNames(what, ...names)

Forbids writing to properties of object. Returns modified what. ...names can be any combination of strings and array of strings.

Usage example

_R.forbidPropertyNames(Object.prototype, ['get', 'set'], 'enumerable', ['writable', 'configurable']);
Object.prototype.get = function(){}; // throws TypeError

addMagicLengthProperty

_R.addMagicLengthProperty(what [,readOnly=true]);

Adds magic length property to what object. It works almost as length property of arrays but it does not support length exceeding maximal index.

If readOnly argument is false, changes in length property will remove indexes over specified length.