Add tests for repair functions
MODIFY
package.json
package.json
"prettier": "^1.13.7",
"rollup": "^0.62.0",
"rollup-plugin-babel-minify": "^5.0.0",
+ "rollup-plugin-strip-code": "^0.2.6",
"rollup-watch": "^4.3.1",
"sinon": "^6.1.3",
"tap-nyc": "^1.0.3",

MODIFY
shim/rollup.config.js
shim/rollup.config.js
import path from 'path';
import minify from 'rollup-plugin-babel-minify';
+import stripCode from "rollup-plugin-strip-code"
const isProduction = process.env.NODE_ENV === 'production';
@@ -12,6 +13,10 @@ export default {
sourcemap: true
},
plugins: [
+ stripCode({
+ start_comment: 'START_TESTS_ONLY',
+ end_comment: 'END_TESTS_ONLY'
+ }),
isProduction
? minify({
comments: false

MODIFY
shim/src/functions.js
shim/src/functions.js
@@ -36,7 +36,7 @@ export function repairFunctions() {
// Use Function() because eval() has issues with serializing functions under the esm module.
// TODO: investigate esm distortion of source code.
// eslint-disable-next-line no-new-func
- FunctionInstance = Function(`return ${declaration}`)();
+ FunctionInstance = (0, eval)(declaration);
} catch (e) {
if (e instanceof SyntaxError) {
// Prevent failure on platforms where async and/or generators are not supported.
@@ -49,7 +49,7 @@ export function repairFunctions() {
// Prevents the evaluation of source when calling constructor on the prototype of functions.
// eslint-disable-next-line no-new-func
- const TamedFunction = Function('throw new Error("Not available");');
+ const TamedFunction = Function('throw new TypeError("Not available");');
defineProperty(TamedFunction, 'name', { value: name });
// (new Error()).constructors does not inherit from Function, because Error

MODIFY
shim/src/unsafeRec.js
shim/src/unsafeRec.js
@@ -3,7 +3,6 @@ import { getSharedGlobalDescs } from './stdlib';
import { repairAccessors } from './accessors';
import { repairFunctions } from './functions';
import { freeze } from './commons';
-import { stripCoverage } from './utilities';
// A "context" is a fresh unsafe Realm as given to us by existing platforms.
// We need this to implement the shim. However, when Realms land for real,
@@ -71,8 +70,23 @@ function createUnsafeRec(unsafeGlobal, allShims) {
});
}
-const repairAccessorsShim = stripCoverage(`"use strict"; (${repairAccessors})();`);
-const repairFunctionsShim = stripCoverage(`"use strict"; (${repairFunctions})();`);
+function strip(src) {
+ /* START_TESTS_ONLY */
+
+ // Restore eval.
+ src = src.replace(/\(0,[^)]+\)/g, '(0, eval)');
+
+ // Remove code coverage.
+ if (typeof __coverage__ !== 'undefined') {
+ src = src.replace(/cov_[^+]+\+\+[;,]/g, '');
+ }
+
+ /* END_TESTS_ONLY */
+ return src;
+}
+
+const repairAccessorsShim = strip(`"use strict"; (${repairAccessors})();`);
+const repairFunctionsShim = strip(`"use strict"; (${repairFunctions})();`);
// Create a new unsafeRec from a brand new context, with new intrinsics and a
// new global object

MODIFY
shim/src/utilities.js
shim/src/utilities.js
@@ -27,10 +27,3 @@ export function assert(condition, message) {
throwTantrum(message);
}
}
-
-// Remove metrics injected by code coverage.
-// todo: remove in production
-export function stripCoverage(src) {
- if (typeof __coverage__ === 'undefined') return src;
- return src.replace(/cov_[^+]+\+\+[;,]/g, '');
-}

ADD
shim/test/realm/functions.js
+import test from 'tape';
+import { repairFunctions } from '../../src/functions';
+
+/* eslint-disable no-restricted-properties, no-underscore-dangle */
+
+repairFunctions();
+
+test('Function.prototype.constructor', t => {
+ t.plan(3);
+
+ // eslint-disable-next-line no-proto
+ t.throws(() => Error.__proto__.constructor(''), TypeError);
+ t.throws(() => Function.prototype.constructor(''), TypeError);
+
+ const proto = Object.getPrototypeOf((0, eval)('(function() {})'));
+ t.throws(() => proto.constructor(''), TypeError);
+});
+
+test('AsyncFunction.constructor', t => {
+ t.plan(1);
+
+ try {
+ const proto = Object.getPrototypeOf((0, eval)('(async function() {})'));
+ t.throws(() => proto.constructor(''), TypeError);
+ } catch (e) {
+ if (e instanceof SyntaxError && e.message.startsWith('Unexpected token')) {
+ t.pass('not supported');
+ } else {
+ throw e;
+ }
+ }
+});
+
+test('GeneratorFunction.constructor', t => {
+ t.plan(1);
+
+ try {
+ const proto = Object.getPrototypeOf((0, eval)('(function* () {})'));
+ t.throws(() => proto.constructor(''), TypeError);
+ } catch (e) {
+ if (e instanceof SyntaxError && e.message.startsWith('Unexpected token')) {
+ t.pass('not supported');
+ } else {
+ throw e;
+ }
+ }
+});
+
+test('AsyncGeneratorFunction.constructor', t => {
+ t.plan(1);
+
+ try {
+ const proto = Object.getPrototypeOf((0, eval)('(async function* () {})'));
+ t.throws(() => proto.constructor(''), TypeError);
+ } catch (e) {
+ if (e instanceof SyntaxError && e.message.startsWith('Unexpected token')) {
+ t.pass('not supported');
+ } else {
+ throw e;
+ }
+ }
+});

MODIFY
shim/test/realm/stdlib.js
shim/test/realm/stdlib.js
@@ -46,8 +46,14 @@ test('Global accessor throws', t => {
t.throws(() => getSharedGlobalDescs(mockGlobal), /unexpected accessor on global property: JSON/);
+ // eslint-disable-next-line no-console
t.equals(console.error.callCount, 1);
- t.equals(console.error.getCall(0).args[0], 'please report internal shim error: unexpected accessor on global property: JSON');
+ t.equals(
+ // eslint-disable-next-line no-console
+ console.error.getCall(0).args[0],
+ 'please report internal shim error: unexpected accessor on global property: JSON'
+ );
+ // eslint-disable-next-line no-console
console.error.restore();
});

Add tests for repair functions