time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

index.js (2903B)


      1 const copyProperty = (to, from, property, ignoreNonConfigurable) => {
      2 	// `Function#length` should reflect the parameters of `to` not `from` since we keep its body.
      3 	// `Function#prototype` is non-writable and non-configurable so can never be modified.
      4 	if (property === 'length' || property === 'prototype') {
      5 		return;
      6 	}
      7 
      8 	// `Function#arguments` and `Function#caller` should not be copied. They were reported to be present in `Reflect.ownKeys` for some devices in React Native (#41), so we explicitly ignore them here.
      9 	if (property === 'arguments' || property === 'caller') {
     10 		return;
     11 	}
     12 
     13 	const toDescriptor = Object.getOwnPropertyDescriptor(to, property);
     14 	const fromDescriptor = Object.getOwnPropertyDescriptor(from, property);
     15 
     16 	if (!canCopyProperty(toDescriptor, fromDescriptor) && ignoreNonConfigurable) {
     17 		return;
     18 	}
     19 
     20 	Object.defineProperty(to, property, fromDescriptor);
     21 };
     22 
     23 // `Object.defineProperty()` throws if the property exists, is not configurable and either:
     24 // - one its descriptors is changed
     25 // - it is non-writable and its value is changed
     26 const canCopyProperty = function (toDescriptor, fromDescriptor) {
     27 	return toDescriptor === undefined || toDescriptor.configurable || (
     28 		toDescriptor.writable === fromDescriptor.writable &&
     29 		toDescriptor.enumerable === fromDescriptor.enumerable &&
     30 		toDescriptor.configurable === fromDescriptor.configurable &&
     31 		(toDescriptor.writable || toDescriptor.value === fromDescriptor.value)
     32 	);
     33 };
     34 
     35 const changePrototype = (to, from) => {
     36 	const fromPrototype = Object.getPrototypeOf(from);
     37 	if (fromPrototype === Object.getPrototypeOf(to)) {
     38 		return;
     39 	}
     40 
     41 	Object.setPrototypeOf(to, fromPrototype);
     42 };
     43 
     44 const wrappedToString = (withName, fromBody) => `/* Wrapped ${withName}*/\n${fromBody}`;
     45 
     46 const toStringDescriptor = Object.getOwnPropertyDescriptor(Function.prototype, 'toString');
     47 const toStringName = Object.getOwnPropertyDescriptor(Function.prototype.toString, 'name');
     48 
     49 // We call `from.toString()` early (not lazily) to ensure `from` can be garbage collected.
     50 // We use `bind()` instead of a closure for the same reason.
     51 // Calling `from.toString()` early also allows caching it in case `to.toString()` is called several times.
     52 const changeToString = (to, from, name) => {
     53 	const withName = name === '' ? '' : `with ${name.trim()}() `;
     54 	const newToString = wrappedToString.bind(null, withName, from.toString());
     55 	// Ensure `to.toString.toString` is non-enumerable and has the same `same`
     56 	Object.defineProperty(newToString, 'name', toStringName);
     57 	Object.defineProperty(to, 'toString', {...toStringDescriptor, value: newToString});
     58 };
     59 
     60 export default function mimicFunction(to, from, {ignoreNonConfigurable = false} = {}) {
     61 	const {name} = to;
     62 
     63 	for (const property of Reflect.ownKeys(from)) {
     64 		copyProperty(to, from, property, ignoreNonConfigurable);
     65 	}
     66 
     67 	changePrototype(to, from);
     68 	changeToString(to, from, name);
     69 
     70 	return to;
     71 }