Implement new Function.prototype.toString --harmony-function-tostring
For functions declared in source code, the .toString() representation
will be an excerpt of the source code.
* For functions declared with the "function" keyword, the excerpt
starts at the "function" or "async" keyword and ends at the final "}".
The previous behavior would start the excerpt at the "(" of the
parameter list, and prepend a canonical `"function " + name` or
similar, which would discard comments and formatting surrounding the
function's name. Anonymous functions declared as function expressions
no longer get the name "anonymous" in their toString representation.
* For methods, the excerpt starts at the "get", "set", "*" (for
generator methods), or property name, whichever comes first.
Previously, the toString representation for methods would use a
canonical prefix before the "(" of the parameter list. Note that any
"static" keyword is omitted.
* For arrow functions and class declarations, the excerpt is unchanged.
For functions created with the Function, GeneratorFunction, or
AsyncFunction constructors:
* The string separating the parameter text and body text is now
"\n) {\n", where previously it was "\n/*``*/) {\n" or ") {\n".
* At one point, newline normalization was required by the spec here,
but that was removed from the spec, and so this CL does not do it.
Included in this CL is a fix for CreateDynamicFunction parsing. ')'
and '`' characters in the parameter string are no longer disallowed,
and Function("a=function(", "}){") is no longer allowed.
BUG=v8:4958, v8:4230
Review-Url: https://codereview.chromium.org/2156303002
Cr-Commit-Position: refs/heads/master@{#43262}
2017-02-16 20:19:24 +00:00
|
|
|
// Copyright 2016 the V8 project authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
|
|
|
var prefix = "/*before*/";
|
|
|
|
var suffix = "/*after*/";
|
|
|
|
|
|
|
|
function checkStringRepresentation(f, source) {
|
|
|
|
assertEquals(typeof f, "function");
|
|
|
|
assertEquals(source, f.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
function testDeclaration(source) {
|
|
|
|
// this eval should define a local variable f that is a function
|
|
|
|
eval(prefix + source + suffix);
|
|
|
|
checkStringRepresentation(f, source);
|
|
|
|
}
|
|
|
|
testDeclaration( "function f(){}");
|
|
|
|
testDeclaration( "function*f(){}");
|
|
|
|
testDeclaration("async function f(){}");
|
|
|
|
testDeclaration( "function/*A*/ f/*B*/(/*C*/a/*D*/,/*E*/b/*G*/)/*H*/{/*I*/}");
|
|
|
|
testDeclaration( "function/*A*/*f/*B*/(/*C*/a/*D*/,/*E*/b/*G*/)/*H*/{/*I*/}");
|
|
|
|
testDeclaration("async/*Z*/function/*A*/ f/*B*/(/*C*/a/*D*/,/*E*/b/*G*/)/*H*/{/*I*/}");
|
|
|
|
testDeclaration( "function \t f \n ( \r a \r\n,\n\r b ) {'\u2654'}");
|
|
|
|
testDeclaration( "function \t *f \n ( \r a \r\n,\n\r b ) { }");
|
|
|
|
testDeclaration( "function *\t f \n ( \r a \r\n,\n\r b ) { }");
|
|
|
|
testDeclaration("async \t function f \n ( \r a \r\n,\n\r b ) { }");
|
|
|
|
|
|
|
|
function testExpression(source) {
|
|
|
|
// this eval should return a function
|
|
|
|
var f = eval("(" + prefix + source + suffix + ")");
|
|
|
|
checkStringRepresentation(f, source);
|
|
|
|
}
|
|
|
|
testExpression( "function (){}");
|
|
|
|
testExpression( "function f(){}");
|
|
|
|
testExpression( "function* (){}");
|
|
|
|
testExpression( "function*f(){}");
|
|
|
|
testExpression("async function (){}");
|
|
|
|
testExpression("async function f(){}");
|
|
|
|
testExpression( "function/*A*/ /*B*/(/*C*/a/*D*/,/*E*/b/*G*/)/*H*/{/*I*/}");
|
|
|
|
testExpression( "function/*A*/ f/*B*/(/*C*/a/*D*/,/*E*/b/*G*/)/*H*/{/*I*/}");
|
|
|
|
testExpression( "function/*A*/* /*B*/(/*C*/a/*D*/,/*E*/b/*G*/)/*H*/{/*I*/}");
|
|
|
|
testExpression( "function/*A*/*f/*B*/(/*C*/a/*D*/,/*E*/b/*G*/)/*H*/{/*I*/}");
|
|
|
|
testExpression("async/*Z*/function/*A*/ f/*B*/(/*C*/a/*D*/,/*E*/b/*G*/)/*H*/{/*I*/}");
|
|
|
|
testExpression( "function \t \n ( \r a \r\n,\n\r b ) { }");
|
|
|
|
testExpression( "function \t f \n ( \r a \r\n,\n\r b ) { }");
|
|
|
|
testExpression( "function \t * \n ( \r a \r\n,\n\r b ) { }");
|
|
|
|
testExpression( "function \t *f \n ( \r a \r\n,\n\r b ) { }");
|
|
|
|
testExpression( "function *\t \n ( \r a \r\n,\n\r b ) { }");
|
|
|
|
testExpression( "function *\t f \n ( \r a \r\n,\n\r b ) { }");
|
|
|
|
testExpression("async \t function \n ( \r a \r\n,\n\r b ) { }");
|
|
|
|
|
|
|
|
testExpression( "(/*A*/ /*B*/ /*C*/ /*D*/ /*E*/ /*F*/)/*G*/=>/*H*/0");
|
|
|
|
testExpression( "a/*B*/ /*C*/ /*D*/ /*E*/ /*F*/ /*G*/=>/*H*/{}");
|
|
|
|
testExpression( "(/*A*/a/*B*/ /*C*/ /*D*/ /*E*/ /*F*/)/*G*/=>/*H*/0");
|
|
|
|
testExpression( "(/*A*/a/*B*/,/*C*/b/*D*/,/*E*/c/*F*/)/*G*/=>/*H*/{}");
|
|
|
|
testExpression("async (/*A*/ /*B*/ /*C*/ /*D*/ /*E*/ /*F*/)/*G*/=>/*H*/0");
|
|
|
|
testExpression("async a/*B*/ /*C*/ /*D*/ /*E*/ /*F*/ /*G*/=>/*H*/{}");
|
|
|
|
testExpression("async (/*A*/a/*B*/ /*C*/ /*D*/ /*E*/ /*F*/)/*G*/=>/*H*/0");
|
|
|
|
testExpression("async (/*A*/a/*B*/,/*C*/b/*D*/,/*E*/c/*F*/)/*G*/=>/*H*/{}");
|
|
|
|
|
|
|
|
function testSimpleMethod(source) {
|
|
|
|
// the source should define a method f
|
|
|
|
|
|
|
|
// object method
|
|
|
|
var f = eval("({" + prefix + source + suffix + "}.f)");
|
|
|
|
checkStringRepresentation(f, source);
|
|
|
|
|
|
|
|
// nonstatic class method
|
|
|
|
var f = eval("new class{" + prefix + source + suffix + "}().f");
|
|
|
|
checkStringRepresentation(f, source);
|
|
|
|
|
|
|
|
// static class method
|
|
|
|
var f = eval("(class{static" + prefix + source + suffix + "}).f");
|
|
|
|
checkStringRepresentation(f, source);
|
|
|
|
}
|
|
|
|
testSimpleMethod("f(){}");
|
|
|
|
testSimpleMethod("*f(){}");
|
|
|
|
testSimpleMethod("async f(){}");
|
|
|
|
testSimpleMethod("f \t (){}");
|
|
|
|
testSimpleMethod("* \tf(){}");
|
|
|
|
testSimpleMethod("async \t f (){}");
|
|
|
|
|
|
|
|
function testAccessorMethod(source, getOrSet) {
|
|
|
|
// the source should define a getter or setter method
|
|
|
|
|
|
|
|
// object method
|
|
|
|
var f = Object.getOwnPropertyDescriptor(eval("({" + prefix + source + suffix + "})"), "f")[getOrSet];
|
|
|
|
checkStringRepresentation(f, source);
|
|
|
|
|
|
|
|
// nonstatic class method
|
|
|
|
var f = Object.getOwnPropertyDescriptor(eval("(class{" + prefix + source + suffix + "})").prototype, "f")[getOrSet];
|
|
|
|
|
|
|
|
// static class method
|
|
|
|
var f = Object.getOwnPropertyDescriptor(eval("(class{static" + prefix + source + suffix + "})"), "f")[getOrSet];
|
|
|
|
checkStringRepresentation(f, source);
|
|
|
|
}
|
|
|
|
|
|
|
|
testAccessorMethod("get f( ){}", "get");
|
|
|
|
testAccessorMethod("set f(a){}", "set");
|
|
|
|
testAccessorMethod("get/*A*/f/*B*/(/*C*/ /*D*/)/*E*/{/*F*/}", "get");
|
|
|
|
testAccessorMethod("set/*A*/f/*B*/(/*C*/a/*D*/)/*E*/{/*F*/}", "set");
|
|
|
|
|
|
|
|
const GeneratorFunction = function*(){}.constructor;
|
|
|
|
const AsyncFunction = async function(){}.constructor;
|
|
|
|
function testDynamicFunction(...args) {
|
|
|
|
var P = args.slice(0, args.length - 1).join(",");
|
|
|
|
var bodyText = args.length > 0 ? args[args.length - 1] : "";
|
|
|
|
var source = " anonymous(" + P + "\n) {\n" + bodyText + "\n}";
|
|
|
|
checkStringRepresentation( Function(...args), "function" + source);
|
|
|
|
checkStringRepresentation(GeneratorFunction(...args), "function*" + source);
|
|
|
|
checkStringRepresentation( AsyncFunction(...args), "async function" + source);
|
|
|
|
}
|
|
|
|
testDynamicFunction();
|
|
|
|
testDynamicFunction(";");
|
|
|
|
testDynamicFunction("return");
|
|
|
|
testDynamicFunction("a", "return a");
|
|
|
|
testDynamicFunction("a", "b", "return a");
|
|
|
|
testDynamicFunction("a, b", "return a");
|
|
|
|
testDynamicFunction("a,/*A*/b", "return a");
|
|
|
|
testDynamicFunction("/*A*/a,b", "return a");
|
|
|
|
testDynamicFunction("a,b", "return a/*A*/");
|
2018-02-22 22:19:40 +00:00
|
|
|
|
|
|
|
// Proxies of functions should not throw, but return a NativeFunction.
|
|
|
|
assertEquals("function () { [native code] }",
|
|
|
|
new Proxy(function () { hidden }, {}).toString());
|
|
|
|
assertEquals("function () { [native code] }",
|
|
|
|
new Proxy(() => { hidden }, {}).toString());
|
|
|
|
assertEquals("function () { [native code] }",
|
|
|
|
new Proxy(class {}, {}).toString());
|
|
|
|
assertEquals("function () { [native code] }",
|
|
|
|
new Proxy(function() { hidden }.bind({}), {}).toString());
|
|
|
|
assertEquals("function () { [native code] }",
|
|
|
|
new Proxy(function*() { hidden }, {}).toString());
|
|
|
|
assertEquals("function () { [native code] }",
|
|
|
|
new Proxy(async function() { hidden }, {}).toString());
|
|
|
|
assertEquals("function () { [native code] }",
|
|
|
|
new Proxy(async function*() { hidden }, {}).toString());
|
|
|
|
assertEquals("function () { [native code] }",
|
|
|
|
new Proxy({ method() { hidden } }.method, {}).toString());
|
|
|
|
|
2018-07-04 15:21:47 +00:00
|
|
|
// Assert that we return a NativeFunction for script that has too large an
|
|
|
|
// offset between function token position and start position for us to return
|
|
|
|
// an exact representation of the source code.
|
|
|
|
function testLongFunctionTokenOffset(functionType) {
|
|
|
|
var expected = "function f() { [native code] }";
|
|
|
|
// Spec requires that we return something that will cause eval to throws if we
|
|
|
|
// can't reproduce the function's source code.
|
|
|
|
assertThrows(() => eval(expected), SyntaxError);
|
|
|
|
|
|
|
|
var functionSource = functionType + " ".repeat(65535) + " f(){}";
|
|
|
|
|
|
|
|
// Function declaration
|
|
|
|
eval(functionSource);
|
|
|
|
assertEquals(expected, f.toString());
|
|
|
|
|
|
|
|
// Function expression
|
|
|
|
var f = eval("(" + functionSource + ")");
|
|
|
|
assertEquals(expected, f.toString());
|
|
|
|
}
|
|
|
|
testLongFunctionTokenOffset("function");
|
|
|
|
testLongFunctionTokenOffset("function*");
|
|
|
|
testLongFunctionTokenOffset("async function");
|
|
|
|
testLongFunctionTokenOffset("async function*");
|
|
|
|
|
2018-02-22 22:19:40 +00:00
|
|
|
// Non-callable proxies still throw.
|
|
|
|
assertThrows(() => Function.prototype.toString.call(new Proxy({}, {})),
|
|
|
|
TypeError);
|