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}
This commit is contained in:
jwolfe 2017-02-16 12:19:24 -08:00 committed by Commit bot
parent 0393b11dea
commit d1d4b9ce51
24 changed files with 389 additions and 120 deletions

View File

@ -2275,9 +2275,14 @@ MaybeLocal<Function> ScriptCompiler::CompileFunctionInContext(
Function);
TRACE_EVENT0("v8", "V8.ScriptCompiler");
i::Handle<i::String> source_string;
int parameters_end_pos = i::kNoSourcePosition;
auto factory = isolate->factory();
if (arguments_count) {
source_string = factory->NewStringFromStaticChars("(function(");
if (i::FLAG_harmony_function_tostring) {
source_string = factory->NewStringFromStaticChars("(function anonymous(");
} else {
source_string = factory->NewStringFromStaticChars("(function(");
}
for (size_t i = 0; i < arguments_count; ++i) {
IsIdentifierHelper helper;
if (!helper.Check(*Utils::OpenHandle(*arguments[i]))) {
@ -2295,12 +2300,24 @@ MaybeLocal<Function> ScriptCompiler::CompileFunctionInContext(
',')).ToHandle(&source_string);
RETURN_ON_FAILED_EXECUTION(Function);
}
auto brackets = factory->NewStringFromStaticChars("){");
i::Handle<i::String> brackets;
if (i::FLAG_harmony_function_tostring) {
brackets = factory->NewStringFromStaticChars("\n) {");
parameters_end_pos = source_string->length() - 3;
} else {
brackets = factory->NewStringFromStaticChars("){");
}
has_pending_exception = !factory->NewConsString(source_string, brackets)
.ToHandle(&source_string);
RETURN_ON_FAILED_EXECUTION(Function);
} else {
source_string = factory->NewStringFromStaticChars("(function(){");
if (i::FLAG_harmony_function_tostring) {
source_string =
factory->NewStringFromStaticChars("(function anonymous(\n) {");
parameters_end_pos = source_string->length() - 3;
} else {
source_string = factory->NewStringFromStaticChars("(function(){");
}
}
int scope_position = source_string->length();
@ -2350,9 +2367,9 @@ MaybeLocal<Function> ScriptCompiler::CompileFunctionInContext(
has_pending_exception =
!i::Compiler::GetFunctionFromEval(
source_string, outer_info, context, i::SLOPPY,
i::ONLY_SINGLE_FUNCTION_LITERAL, eval_scope_position, eval_position,
line_offset, column_offset - scope_position, name_obj,
source->resource_options)
i::ONLY_SINGLE_FUNCTION_LITERAL, parameters_end_pos,
eval_scope_position, eval_position, line_offset,
column_offset - scope_position, name_obj, source->resource_options)
.ToHandle(&fun);
if (has_pending_exception) {
isolate->ReportPendingMessages();

View File

@ -3522,6 +3522,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_function_sent)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_tailcalls)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_restrictive_generators)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_trailing_commas)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_function_tostring)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_class_fields)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_object_rest_spread)
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_dynamic_import)
@ -4147,6 +4148,7 @@ bool Genesis::InstallExperimentalNatives() {
#endif
static const char* harmony_restrictive_generators_natives[] = {nullptr};
static const char* harmony_trailing_commas_natives[] = {nullptr};
static const char* harmony_function_tostring_natives[] = {nullptr};
static const char* harmony_class_fields_natives[] = {nullptr};
static const char* harmony_object_rest_spread_natives[] = {nullptr};
static const char* harmony_async_iteration_natives[] = {nullptr};

View File

@ -32,11 +32,16 @@ MaybeHandle<Object> CreateDynamicFunction(Isolate* isolate,
// Build the source string.
Handle<String> source;
int parameters_end_pos = kNoSourcePosition;
{
IncrementalStringBuilder builder(isolate);
builder.AppendCharacter('(');
builder.AppendCString(token);
builder.AppendCharacter('(');
if (FLAG_harmony_function_tostring) {
builder.AppendCString(" anonymous(");
} else {
builder.AppendCharacter('(');
}
bool parenthesis_in_arg_string = false;
if (argc > 1) {
for (int i = 1; i < argc; ++i) {
@ -46,22 +51,30 @@ MaybeHandle<Object> CreateDynamicFunction(Isolate* isolate,
isolate, param, Object::ToString(isolate, args.at(i)), Object);
param = String::Flatten(param);
builder.AppendString(param);
// If the formal parameters string include ) - an illegal
// character - it may make the combined function expression
// compile. We avoid this problem by checking for this early on.
DisallowHeapAllocation no_gc; // Ensure vectors stay valid.
String::FlatContent param_content = param->GetFlatContent();
for (int i = 0, length = param->length(); i < length; ++i) {
if (param_content.Get(i) == ')') {
parenthesis_in_arg_string = true;
break;
if (!FLAG_harmony_function_tostring) {
// If the formal parameters string include ) - an illegal
// character - it may make the combined function expression
// compile. We avoid this problem by checking for this early on.
DisallowHeapAllocation no_gc; // Ensure vectors stay valid.
String::FlatContent param_content = param->GetFlatContent();
for (int i = 0, length = param->length(); i < length; ++i) {
if (param_content.Get(i) == ')') {
parenthesis_in_arg_string = true;
break;
}
}
}
}
// If the formal parameters include an unbalanced block comment, the
// function must be rejected. Since JavaScript does not allow nested
// comments we can include a trailing block comment to catch this.
builder.AppendCString("\n/*``*/");
if (!FLAG_harmony_function_tostring) {
// If the formal parameters include an unbalanced block comment, the
// function must be rejected. Since JavaScript does not allow nested
// comments we can include a trailing block comment to catch this.
builder.AppendCString("\n/*``*/");
}
}
if (FLAG_harmony_function_tostring) {
builder.AppendCharacter('\n');
parameters_end_pos = builder.Length();
}
builder.AppendCString(") {\n");
if (argc > 0) {
@ -86,11 +99,12 @@ MaybeHandle<Object> CreateDynamicFunction(Isolate* isolate,
// come from here.
Handle<JSFunction> function;
{
ASSIGN_RETURN_ON_EXCEPTION(isolate, function,
Compiler::GetFunctionFromString(
handle(target->native_context(), isolate),
source, ONLY_SINGLE_FUNCTION_LITERAL),
Object);
ASSIGN_RETURN_ON_EXCEPTION(
isolate, function,
Compiler::GetFunctionFromString(
handle(target->native_context(), isolate), source,
ONLY_SINGLE_FUNCTION_LITERAL, parameters_end_pos),
Object);
Handle<Object> result;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, result,

View File

@ -92,9 +92,10 @@ BUILTIN(GlobalEval) {
}
Handle<JSFunction> function;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, function, Compiler::GetFunctionFromString(
handle(target->native_context(), isolate),
Handle<String>::cast(x), NO_PARSE_RESTRICTION));
isolate, function,
Compiler::GetFunctionFromString(handle(target->native_context(), isolate),
Handle<String>::cast(x),
NO_PARSE_RESTRICTION, kNoSourcePosition));
RETURN_RESULT_OR_FAILURE(
isolate,
Execution::Call(isolate, function, target_global_proxy, 0, nullptr));

View File

@ -194,8 +194,7 @@ void CompilationCacheScript::Put(Handle<String> source, Handle<Context> context,
InfoVectorPair CompilationCacheEval::Lookup(
Handle<String> source, Handle<SharedFunctionInfo> outer_info,
Handle<Context> native_context, LanguageMode language_mode,
int scope_position) {
Handle<Context> native_context, LanguageMode language_mode, int position) {
HandleScope scope(isolate());
// Make sure not to leak the table into the surrounding handle
// scope. Otherwise, we risk keeping old tables around even after
@ -205,7 +204,7 @@ InfoVectorPair CompilationCacheEval::Lookup(
DCHECK(generations() == 1);
Handle<CompilationCacheTable> table = GetTable(generation);
result = table->LookupEval(source, outer_info, native_context, language_mode,
scope_position);
position);
if (result.has_shared()) {
isolate()->counters()->compilation_cache_hits()->Increment();
} else {
@ -218,12 +217,12 @@ void CompilationCacheEval::Put(Handle<String> source,
Handle<SharedFunctionInfo> outer_info,
Handle<SharedFunctionInfo> function_info,
Handle<Context> native_context,
Handle<Cell> literals, int scope_position) {
Handle<Cell> literals, int position) {
HandleScope scope(isolate());
Handle<CompilationCacheTable> table = GetFirstTable();
table =
CompilationCacheTable::PutEval(table, source, outer_info, function_info,
native_context, literals, scope_position);
native_context, literals, position);
SetFirstTable(table);
}
@ -286,18 +285,18 @@ InfoVectorPair CompilationCache::LookupScript(
InfoVectorPair CompilationCache::LookupEval(
Handle<String> source, Handle<SharedFunctionInfo> outer_info,
Handle<Context> context, LanguageMode language_mode, int scope_position) {
Handle<Context> context, LanguageMode language_mode, int position) {
InfoVectorPair result;
if (!IsEnabled()) return result;
if (context->IsNativeContext()) {
result = eval_global_.Lookup(source, outer_info, context, language_mode,
scope_position);
position);
} else {
DCHECK(scope_position != kNoSourcePosition);
DCHECK(position != kNoSourcePosition);
Handle<Context> native_context(context->native_context(), isolate());
result = eval_contextual_.Lookup(source, outer_info, native_context,
language_mode, scope_position);
language_mode, position);
}
return result;
@ -324,18 +323,18 @@ void CompilationCache::PutEval(Handle<String> source,
Handle<SharedFunctionInfo> outer_info,
Handle<Context> context,
Handle<SharedFunctionInfo> function_info,
Handle<Cell> literals, int scope_position) {
Handle<Cell> literals, int position) {
if (!IsEnabled()) return;
HandleScope scope(isolate());
if (context->IsNativeContext()) {
eval_global_.Put(source, outer_info, function_info, context, literals,
scope_position);
position);
} else {
DCHECK(scope_position != kNoSourcePosition);
DCHECK(position != kNoSourcePosition);
Handle<Context> native_context(context->native_context(), isolate());
eval_contextual_.Put(source, outer_info, function_info, native_context,
literals, scope_position);
literals, position);
}
}

View File

@ -116,12 +116,11 @@ class CompilationCacheEval: public CompilationSubCache {
InfoVectorPair Lookup(Handle<String> source,
Handle<SharedFunctionInfo> outer_info,
Handle<Context> native_context,
LanguageMode language_mode, int scope_position);
LanguageMode language_mode, int position);
void Put(Handle<String> source, Handle<SharedFunctionInfo> outer_info,
Handle<SharedFunctionInfo> function_info,
Handle<Context> native_context, Handle<Cell> literals,
int scope_position);
Handle<Context> native_context, Handle<Cell> literals, int position);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(CompilationCacheEval);
@ -164,7 +163,7 @@ class CompilationCache {
InfoVectorPair LookupEval(Handle<String> source,
Handle<SharedFunctionInfo> outer_info,
Handle<Context> context, LanguageMode language_mode,
int scope_position);
int position);
// Returns the regexp data associated with the given regexp if it
// is in cache, otherwise an empty handle.
@ -183,7 +182,7 @@ class CompilationCache {
void PutEval(Handle<String> source, Handle<SharedFunctionInfo> outer_info,
Handle<Context> context,
Handle<SharedFunctionInfo> function_info, Handle<Cell> literals,
int scope_position);
int position);
// Associate the (source, flags) pair to the given regexp data.
// This may overwrite an existing mapping.

View File

@ -1476,17 +1476,35 @@ Compiler::CompilationTier Compiler::NextCompilationTier(JSFunction* function) {
MaybeHandle<JSFunction> Compiler::GetFunctionFromEval(
Handle<String> source, Handle<SharedFunctionInfo> outer_info,
Handle<Context> context, LanguageMode language_mode,
ParseRestriction restriction, int eval_scope_position, int eval_position,
int line_offset, int column_offset, Handle<Object> script_name,
ParseRestriction restriction, int parameters_end_pos,
int eval_scope_position, int eval_position, int line_offset,
int column_offset, Handle<Object> script_name,
ScriptOriginOptions options) {
Isolate* isolate = source->GetIsolate();
int source_length = source->length();
isolate->counters()->total_eval_size()->Increment(source_length);
isolate->counters()->total_compile_size()->Increment(source_length);
// The cache lookup key needs to be aware of the separation between the
// parameters and the body to prevent this valid invocation:
// Function("", "function anonymous(\n/**/) {\n}");
// from adding an entry that falsely approves this invalid invocation:
// Function("\n/**/) {\nfunction anonymous(", "}");
// The actual eval_scope_position for indirect eval and CreateDynamicFunction
// is unused (just 0), which means it's an available field to use to indicate
// this separation. But to make sure we're not causing other false hits, we
// negate the scope position.
int position = eval_scope_position;
if (FLAG_harmony_function_tostring &&
restriction == ONLY_SINGLE_FUNCTION_LITERAL &&
parameters_end_pos != kNoSourcePosition) {
// use the parameters_end_pos as the eval_scope_position in the eval cache.
DCHECK_EQ(eval_scope_position, 0);
position = -parameters_end_pos;
}
CompilationCache* compilation_cache = isolate->compilation_cache();
InfoVectorPair eval_result = compilation_cache->LookupEval(
source, outer_info, context, language_mode, eval_scope_position);
source, outer_info, context, language_mode, position);
Handle<SharedFunctionInfo> shared_info;
if (eval_result.has_shared()) {
shared_info = Handle<SharedFunctionInfo>(eval_result.shared(), isolate);
@ -1518,6 +1536,7 @@ MaybeHandle<JSFunction> Compiler::GetFunctionFromEval(
parse_info.set_eval();
parse_info.set_language_mode(language_mode);
parse_info.set_parse_restriction(restriction);
parse_info.set_parameters_end_pos(parameters_end_pos);
if (!context->IsNativeContext()) {
parse_info.set_outer_scope_info(handle(context->scope_info()));
}
@ -1595,7 +1614,7 @@ bool ContainsAsmModule(Handle<Script> script) {
MaybeHandle<JSFunction> Compiler::GetFunctionFromString(
Handle<Context> context, Handle<String> source,
ParseRestriction restriction) {
ParseRestriction restriction, int parameters_end_pos) {
Isolate* const isolate = context->GetIsolate();
Handle<Context> native_context(context->native_context(), isolate);
@ -1615,8 +1634,8 @@ MaybeHandle<JSFunction> Compiler::GetFunctionFromString(
int eval_position = kNoSourcePosition;
Handle<SharedFunctionInfo> outer_info(native_context->closure()->shared());
return Compiler::GetFunctionFromEval(source, outer_info, native_context,
SLOPPY, restriction, eval_scope_position,
eval_position);
SLOPPY, restriction, parameters_end_pos,
eval_scope_position, eval_position);
}
Handle<SharedFunctionInfo> Compiler::GetSharedFunctionInfoForScript(

View File

@ -98,15 +98,15 @@ class V8_EXPORT_PRIVATE Compiler : public AllStatic {
MUST_USE_RESULT static MaybeHandle<JSFunction> GetFunctionFromEval(
Handle<String> source, Handle<SharedFunctionInfo> outer_info,
Handle<Context> context, LanguageMode language_mode,
ParseRestriction restriction, int eval_scope_position, int eval_position,
int line_offset = 0, int column_offset = 0,
Handle<Object> script_name = Handle<Object>(),
ParseRestriction restriction, int parameters_end_pos,
int eval_scope_position, int eval_position, int line_offset = 0,
int column_offset = 0, Handle<Object> script_name = Handle<Object>(),
ScriptOriginOptions options = ScriptOriginOptions());
// Create a (bound) function for a String source within a context for eval.
MUST_USE_RESULT static MaybeHandle<JSFunction> GetFunctionFromString(
Handle<Context> context, Handle<String> source,
ParseRestriction restriction);
ParseRestriction restriction, int parameters_end_pos);
// Create a shared function info object for a String source within a context.
static Handle<SharedFunctionInfo> GetSharedFunctionInfoForScript(

View File

@ -93,7 +93,7 @@ MaybeHandle<Object> DebugEvaluate::Evaluate(
isolate, eval_fun,
Compiler::GetFunctionFromEval(source, outer_info, context, SLOPPY,
NO_PARSE_RESTRICTION, kNoSourcePosition,
kNoSourcePosition),
kNoSourcePosition, kNoSourcePosition),
Object);
Handle<Object> result;

View File

@ -202,6 +202,7 @@ DEFINE_IMPLICATION(es_staging, move_object_start)
V(harmony_do_expressions, "harmony do-expressions") \
V(harmony_regexp_named_captures, "harmony regexp named captures") \
V(harmony_regexp_property, "harmony unicode regexp property classes") \
V(harmony_function_tostring, "harmony Function.prototype.toString") \
V(harmony_class_fields, "harmony public fields in class literals") \
V(harmony_async_iteration, "harmony async iteration") \
V(harmony_dynamic_import, "harmony dynamic import")

View File

@ -593,6 +593,9 @@ class ErrorUtils : public AllStatic {
"Setter function argument must not be a rest parameter") \
T(ParamDupe, "Duplicate parameter name not allowed in this context") \
T(ParenthesisInArgString, "Function arg string contains parenthesis") \
T(ArgStringTerminatesParametersEarly, \
"Arg string terminates parameters early") \
T(UnexpectedEndOfArgString, "Unexpected end of arg string") \
T(RuntimeWrongNumArgs, "Runtime function given wrong number of arguments") \
T(SingleFunctionLiteral, "Single function literal required") \
T(SloppyFunction, \

View File

@ -12820,6 +12820,10 @@ Handle<String> JSFunction::ToString(Handle<JSFunction> function) {
return NativeCodeFunctionSourceString(shared_info);
}
if (FLAG_harmony_function_tostring) {
return Handle<String>::cast(shared_info->GetSourceCodeHarmony());
}
IncrementalStringBuilder builder(isolate);
FunctionKind kind = shared_info->kind();
if (!IsArrowFunction(kind)) {
@ -13290,6 +13294,15 @@ Handle<Object> SharedFunctionInfo::GetSourceCode() {
source, start_position(), end_position());
}
Handle<Object> SharedFunctionInfo::GetSourceCodeHarmony() {
Isolate* isolate = GetIsolate();
if (!HasSourceCode()) return isolate->factory()->undefined_value();
Handle<String> script_source(String::cast(Script::cast(script())->source()));
int start_pos = function_token_position();
if (start_pos == kNoSourcePosition) start_pos = start_position();
return isolate->factory()->NewSubString(script_source, start_pos,
end_position());
}
bool SharedFunctionInfo::IsInlineable() {
// Check that the function has a script associated with it.
@ -15767,12 +15780,22 @@ void Symbol::SymbolShortPrint(std::ostream& os) {
// StringSharedKeys are used as keys in the eval cache.
class StringSharedKey : public HashTableKey {
public:
// This tuple unambiguously identifies calls to eval() or
// CreateDynamicFunction() (such as through the Function() constructor).
// * source is the string passed into eval(). For dynamic functions, this is
// the effective source for the function, some of which is implicitly
// generated.
// * shared is the shared function info for the function containing the call
// to eval(). for dynamic functions, shared is the native context closure.
// * When positive, position is the position in the source where eval is
// called. When negative, position is the negation of the position in the
// dynamic function's effective source where the ')' ends the parameters.
StringSharedKey(Handle<String> source, Handle<SharedFunctionInfo> shared,
LanguageMode language_mode, int scope_position)
LanguageMode language_mode, int position)
: source_(source),
shared_(shared),
language_mode_(language_mode),
scope_position_(scope_position) {}
position_(position) {}
bool IsMatch(Object* other) override {
DisallowHeapAllocation no_allocation;
@ -15788,8 +15811,8 @@ class StringSharedKey : public HashTableKey {
DCHECK(is_valid_language_mode(language_unchecked));
LanguageMode language_mode = static_cast<LanguageMode>(language_unchecked);
if (language_mode != language_mode_) return false;
int scope_position = Smi::cast(other_array->get(3))->value();
if (scope_position != scope_position_) return false;
int position = Smi::cast(other_array->get(3))->value();
if (position != position_) return false;
String* source = String::cast(other_array->get(1));
return source->Equals(*source_);
}
@ -15797,7 +15820,7 @@ class StringSharedKey : public HashTableKey {
static uint32_t StringSharedHashHelper(String* source,
SharedFunctionInfo* shared,
LanguageMode language_mode,
int scope_position) {
int position) {
uint32_t hash = source->Hash();
if (shared->HasSourceCode()) {
// Instead of using the SharedFunctionInfo pointer in the hash
@ -15809,14 +15832,14 @@ class StringSharedKey : public HashTableKey {
hash ^= String::cast(script->source())->Hash();
STATIC_ASSERT(LANGUAGE_END == 2);
if (is_strict(language_mode)) hash ^= 0x8000;
hash += scope_position;
hash += position;
}
return hash;
}
uint32_t Hash() override {
return StringSharedHashHelper(*source_, *shared_, language_mode_,
scope_position_);
position_);
}
uint32_t HashForObject(Object* obj) override {
@ -15830,9 +15853,8 @@ class StringSharedKey : public HashTableKey {
int language_unchecked = Smi::cast(other_array->get(2))->value();
DCHECK(is_valid_language_mode(language_unchecked));
LanguageMode language_mode = static_cast<LanguageMode>(language_unchecked);
int scope_position = Smi::cast(other_array->get(3))->value();
return StringSharedHashHelper(source, shared, language_mode,
scope_position);
int position = Smi::cast(other_array->get(3))->value();
return StringSharedHashHelper(source, shared, language_mode, position);
}
@ -15841,7 +15863,7 @@ class StringSharedKey : public HashTableKey {
array->set(0, *shared_);
array->set(1, *source_);
array->set(2, Smi::FromInt(language_mode_));
array->set(3, Smi::FromInt(scope_position_));
array->set(3, Smi::FromInt(position_));
return array;
}
@ -15849,7 +15871,7 @@ class StringSharedKey : public HashTableKey {
Handle<String> source_;
Handle<SharedFunctionInfo> shared_;
LanguageMode language_mode_;
int scope_position_;
int position_;
};
// static
@ -17451,12 +17473,9 @@ InfoVectorPair CompilationCacheTable::LookupScript(Handle<String> src,
InfoVectorPair CompilationCacheTable::LookupEval(
Handle<String> src, Handle<SharedFunctionInfo> outer_info,
Handle<Context> native_context, LanguageMode language_mode,
int scope_position) {
Handle<Context> native_context, LanguageMode language_mode, int position) {
InfoVectorPair empty_result;
// Cache key is the tuple (source, outer shared function info, scope position)
// to unambiguously identify the context chain the cached eval code assumes.
StringSharedKey key(src, outer_info, language_mode, scope_position);
StringSharedKey key(src, outer_info, language_mode, position);
int entry = FindEntry(&key);
if (entry == kNotFound) return empty_result;
int index = EntryToIndex(entry);
@ -17517,9 +17536,9 @@ Handle<CompilationCacheTable> CompilationCacheTable::PutScript(
Handle<CompilationCacheTable> CompilationCacheTable::PutEval(
Handle<CompilationCacheTable> cache, Handle<String> src,
Handle<SharedFunctionInfo> outer_info, Handle<SharedFunctionInfo> value,
Handle<Context> native_context, Handle<Cell> literals, int scope_position) {
Handle<Context> native_context, Handle<Cell> literals, int position) {
Isolate* isolate = cache->GetIsolate();
StringSharedKey key(src, outer_info, value->language_mode(), scope_position);
StringSharedKey key(src, outer_info, value->language_mode(), position);
{
Handle<Object> k = key.AsHandle(isolate);
int entry = cache->FindEntry(&key);

View File

@ -7291,6 +7291,7 @@ class SharedFunctionInfo: public HeapObject {
// [source code]: Source code for the function.
bool HasSourceCode() const;
Handle<Object> GetSourceCode();
Handle<Object> GetSourceCodeHarmony();
// Number of times the function was optimized.
inline int opt_count();
@ -8677,7 +8678,7 @@ class CompilationCacheTable: public HashTable<CompilationCacheTable,
InfoVectorPair LookupEval(Handle<String> src,
Handle<SharedFunctionInfo> shared,
Handle<Context> native_context,
LanguageMode language_mode, int scope_position);
LanguageMode language_mode, int position);
Handle<Object> LookupRegExp(Handle<String> source, JSRegExp::Flags flags);
static Handle<CompilationCacheTable> Put(
Handle<CompilationCacheTable> cache, Handle<String> src,
@ -8690,8 +8691,7 @@ class CompilationCacheTable: public HashTable<CompilationCacheTable,
static Handle<CompilationCacheTable> PutEval(
Handle<CompilationCacheTable> cache, Handle<String> src,
Handle<SharedFunctionInfo> outer_info, Handle<SharedFunctionInfo> value,
Handle<Context> native_context, Handle<Cell> literals,
int scope_position);
Handle<Context> native_context, Handle<Cell> literals, int position);
static Handle<CompilationCacheTable> PutRegExp(
Handle<CompilationCacheTable> cache, Handle<String> src,
JSRegExp::Flags flags, Handle<FixedArray> value);

View File

@ -31,6 +31,7 @@ ParseInfo::ParseInfo(AccountingAllocator* zone_allocator)
compiler_hints_(0),
start_position_(0),
end_position_(0),
parameters_end_pos_(kNoSourcePosition),
function_literal_id_(FunctionLiteral::kIdTypeInvalid),
max_function_literal_id_(FunctionLiteral::kIdTypeInvalid),
isolate_(nullptr),

View File

@ -169,6 +169,11 @@ class V8_EXPORT_PRIVATE ParseInfo {
int end_position() const { return end_position_; }
void set_end_position(int end_position) { end_position_ = end_position; }
int parameters_end_pos() const { return parameters_end_pos_; }
void set_parameters_end_pos(int parameters_end_pos) {
parameters_end_pos_ = parameters_end_pos;
}
int function_literal_id() const { return function_literal_id_; }
void set_function_literal_id(int function_literal_id) {
function_literal_id_ = function_literal_id;
@ -262,6 +267,7 @@ class V8_EXPORT_PRIVATE ParseInfo {
int compiler_hints_;
int start_position_;
int end_position_;
int parameters_end_pos_;
int function_literal_id_;
int max_function_literal_id_;

View File

@ -2191,10 +2191,12 @@ ParserBase<Impl>::ParseClassPropertyDefinition(
Token::Value name_token = peek();
int function_token_position = scanner()->peek_location().beg_pos;
IdentifierT name = impl()->EmptyIdentifier();
ExpressionT name_expression;
if (name_token == Token::STATIC) {
Consume(Token::STATIC);
function_token_position = scanner()->peek_location().beg_pos;
if (peek() == Token::LPAREN) {
kind = PropertyKind::kMethodProperty;
name = impl()->GetSymbol(); // TODO(bakkot) specialize on 'static'
@ -2271,8 +2273,10 @@ ParserBase<Impl>::ParseClassPropertyDefinition(
ExpressionT value = impl()->ParseFunctionLiteral(
name, scanner()->location(), kSkipFunctionNameCheck, kind,
kNoSourcePosition, FunctionLiteral::kAccessorOrMethod,
language_mode(), CHECK_OK_CUSTOM(EmptyClassLiteralProperty));
FLAG_harmony_function_tostring ? function_token_position
: kNoSourcePosition,
FunctionLiteral::kAccessorOrMethod, language_mode(),
CHECK_OK_CUSTOM(EmptyClassLiteralProperty));
*property_kind = ClassLiteralProperty::METHOD;
return factory()->NewClassLiteralProperty(name_expression, value,
@ -2299,8 +2303,10 @@ ParserBase<Impl>::ParseClassPropertyDefinition(
FunctionLiteralT value = impl()->ParseFunctionLiteral(
name, scanner()->location(), kSkipFunctionNameCheck, kind,
kNoSourcePosition, FunctionLiteral::kAccessorOrMethod,
language_mode(), CHECK_OK_CUSTOM(EmptyClassLiteralProperty));
FLAG_harmony_function_tostring ? function_token_position
: kNoSourcePosition,
FunctionLiteral::kAccessorOrMethod, language_mode(),
CHECK_OK_CUSTOM(EmptyClassLiteralProperty));
if (!*is_computed_name) {
impl()->AddAccessorPrefixToFunctionName(is_get, value, name);
@ -2494,8 +2500,9 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker,
ExpressionT value = impl()->ParseFunctionLiteral(
name, scanner()->location(), kSkipFunctionNameCheck, kind,
kNoSourcePosition, FunctionLiteral::kAccessorOrMethod,
language_mode(), CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
FLAG_harmony_function_tostring ? next_beg_pos : kNoSourcePosition,
FunctionLiteral::kAccessorOrMethod, language_mode(),
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
return factory()->NewObjectLiteralProperty(
name_expression, value, ObjectLiteralProperty::COMPUTED,
@ -2523,8 +2530,9 @@ ParserBase<Impl>::ParseObjectPropertyDefinition(ObjectLiteralChecker* checker,
FunctionLiteralT value = impl()->ParseFunctionLiteral(
name, scanner()->location(), kSkipFunctionNameCheck, kind,
kNoSourcePosition, FunctionLiteral::kAccessorOrMethod,
language_mode(), CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
FLAG_harmony_function_tostring ? next_beg_pos : kNoSourcePosition,
FunctionLiteral::kAccessorOrMethod, language_mode(),
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
if (!*is_computed_name) {
impl()->AddAccessorPrefixToFunctionName(is_get, value, name);
@ -3363,7 +3371,12 @@ typename ParserBase<Impl>::ExpressionT ParserBase<Impl>::ParseMemberExpression(
Scanner::Location function_name_location = Scanner::Location::invalid();
FunctionLiteral::FunctionType function_type =
FunctionLiteral::kAnonymousExpression;
if (peek_any_identifier()) {
if (impl()->ParsingDynamicFunctionDeclaration()) {
// We don't want dynamic functions to actually declare their name
// "anonymous". We just want that name in the toString().
Consume(Token::IDENTIFIER);
DCHECK(scanner()->UnescapedLiteralMatches("anonymous", 9));
} else if (peek_any_identifier()) {
name = ParseIdentifierOrStrictReservedWord(
function_kind, &is_strict_reserved_name, CHECK_OK);
function_name_location = scanner()->location();
@ -4376,7 +4389,12 @@ ParserBase<Impl>::ParseAsyncFunctionLiteral(bool* ok) {
IdentifierT name = impl()->EmptyIdentifier();
FunctionLiteral::FunctionType type = FunctionLiteral::kAnonymousExpression;
if (peek_any_identifier()) {
if (impl()->ParsingDynamicFunctionDeclaration()) {
// We don't want dynamic functions to actually declare their name
// "anonymous". We just want that name in the toString().
Consume(Token::IDENTIFIER);
DCHECK(scanner()->UnescapedLiteralMatches("anonymous", 9));
} else if (peek_any_identifier()) {
type = FunctionLiteral::kNamedExpression;
name = ParseIdentifierOrStrictReservedWord(FunctionKind::kAsyncFunction,
&is_strict_reserved, CHECK_OK);

View File

@ -516,7 +516,8 @@ Parser::Parser(ParseInfo* info)
total_preparse_skipped_(0),
temp_zoned_(false),
log_(nullptr),
preparsed_scope_data_(info->preparsed_scope_data()) {
preparsed_scope_data_(info->preparsed_scope_data()),
parameters_end_pos_(info->parameters_end_pos()) {
// Even though we were passed ParseInfo, we should not store it in
// Parser - this makes sure that Isolate is not accidentally accessed via
// ParseInfo during background parsing.
@ -2749,6 +2750,7 @@ Parser::LazyParsingResult Parser::SkipFunction(
int* expected_property_count, bool is_inner_function, bool may_abort,
bool* ok) {
DCHECK_NE(kNoSourcePosition, function_scope->start_position());
DCHECK_EQ(kNoSourcePosition, parameters_end_pos_);
if (produce_cached_parse_data()) CHECK(log_);
DCHECK_IMPLIES(IsArrowFunction(kind),
@ -3127,8 +3129,34 @@ ZoneList<Statement*>* Parser::ParseFunction(
if (IsResumableFunction(kind)) PrepareGeneratorVariables();
int expected_parameters_end_pos = parameters_end_pos_;
if (expected_parameters_end_pos != kNoSourcePosition) {
// This is the first function encountered in a CreateDynamicFunction eval.
parameters_end_pos_ = kNoSourcePosition;
// The function name should have been ignored, giving us the empty string
// here.
DCHECK_EQ(function_name, ast_value_factory()->empty_string());
}
ParserFormalParameters formals(function_scope);
ParseFormalParameterList(&formals, CHECK_OK);
if (expected_parameters_end_pos != kNoSourcePosition) {
// Check for '(' or ')' shenanigans in the parameter string for dynamic
// functions.
int position = peek_position();
if (position < expected_parameters_end_pos) {
ReportMessageAt(Scanner::Location(position, position + 1),
MessageTemplate::kArgStringTerminatesParametersEarly);
*ok = false;
return nullptr;
} else if (position > expected_parameters_end_pos) {
ReportMessageAt(Scanner::Location(expected_parameters_end_pos - 2,
expected_parameters_end_pos),
MessageTemplate::kUnexpectedEndOfArgString);
*ok = false;
return nullptr;
}
}
Expect(Token::RPAREN, CHECK_OK);
int formals_end_position = scanner()->location().end_pos;
*num_parameters = formals.num_parameters();

View File

@ -1121,6 +1121,12 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
++use_counts_[feature];
}
// Returns true iff we're parsing the first function literal during
// CreateDynamicFunction().
V8_INLINE bool ParsingDynamicFunctionDeclaration() const {
return parameters_end_pos_ != kNoSourcePosition;
}
// Parser's private field members.
friend class DiscardableZoneScope; // Uses reusable_preparser_.
// FIXME(marja): Make reusable_preparser_ always use its own temp Zone (call
@ -1149,6 +1155,12 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
ParserLogger* log_;
PreParsedScopeData* preparsed_scope_data_;
// If not kNoSourcePosition, indicates that the first function literal
// encountered is a dynamic function, see CreateDynamicFunction(). This field
// indicates the correct position of the ')' that closes the parameter list.
// After that ')' is encountered, this field is reset to kNoSourcePosition.
int parameters_end_pos_;
};
// ----------------------------------------------------------------------------

View File

@ -1690,6 +1690,8 @@ class PreParser : public ParserBase<PreParser> {
if (use_counts_ != nullptr) ++use_counts_[feature];
}
V8_INLINE bool ParsingDynamicFunctionDeclaration() const { return false; }
// Preparser's private field members.
int* use_counts_;

View File

@ -446,9 +446,10 @@ static Object* CompileGlobalEval(Isolate* isolate, Handle<String> source,
static const ParseRestriction restriction = NO_PARSE_RESTRICTION;
Handle<JSFunction> compiled;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate, compiled, Compiler::GetFunctionFromEval(
source, outer_info, context, language_mode,
restriction, eval_scope_position, eval_position),
isolate, compiled,
Compiler::GetFunctionFromEval(source, outer_info, context, language_mode,
restriction, kNoSourcePosition,
eval_scope_position, eval_position),
isolate->heap()->exception());
return *compiled;
}

View File

@ -310,6 +310,8 @@ class IncrementalStringBuilder {
INLINE(bool HasOverflowed()) const { return overflowed_; }
INLINE(int Length()) const { return accumulator_->length() + current_index_; }
// Change encoding to two-byte.
void ChangeEncoding() {
DCHECK_EQ(String::ONE_BYTE_ENCODING, encoding_);

View File

@ -138,7 +138,8 @@ Handle<JSObject> MakeFakeBreakpoint(Isolate* isolate, int position) {
Handle<String> source = isolate->factory()->NewStringFromStaticChars("true");
Handle<Context> context(isolate->context(), isolate);
Handle<JSFunction> triggered_fun =
Compiler::GetFunctionFromString(context, source, NO_PARSE_RESTRICTION)
Compiler::GetFunctionFromString(context, source, NO_PARSE_RESTRICTION,
kNoSourcePosition)
.ToHandleChecked();
PropertyDescriptor desc;
desc.set_value(triggered_fun);

View File

@ -0,0 +1,124 @@
// 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.
// Flags: --harmony-function-tostring
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*/");

View File

@ -248,32 +248,32 @@
'built-ins/Simd/*': [SKIP],
# https://bugs.chromium.org/p/v8/issues/detail?id=4958
'built-ins/Function/prototype/toString/AsyncFunction': [FAIL],
'built-ins/Function/prototype/toString/async-function-declaration': [FAIL],
'built-ins/Function/prototype/toString/async-function-expression': [FAIL],
'built-ins/Function/prototype/toString/async-method': [FAIL],
'built-ins/Function/prototype/toString/Function': [FAIL],
'built-ins/Function/prototype/toString/GeneratorFunction': [FAIL],
'built-ins/Function/prototype/toString/function-declaration': [FAIL],
'built-ins/Function/prototype/toString/function-declaration-non-simple-parameter-list': [FAIL],
'built-ins/Function/prototype/toString/function-expression': [FAIL],
'built-ins/Function/prototype/toString/generator-function-declaration': [FAIL],
'built-ins/Function/prototype/toString/generator-function-expression': [FAIL],
'built-ins/Function/prototype/toString/generator-method': [FAIL],
'built-ins/Function/prototype/toString/getter-class': [FAIL],
'built-ins/Function/prototype/toString/getter-class-static': [FAIL],
'built-ins/Function/prototype/toString/getter-object': [FAIL],
'built-ins/Function/prototype/toString/line-terminator-normalisation-CR': [FAIL],
'built-ins/Function/prototype/toString/line-terminator-normalisation-CR-LF': [FAIL],
'built-ins/Function/prototype/toString/line-terminator-normalisation-LF': [FAIL],
'built-ins/Function/prototype/toString/method-class': [FAIL],
'built-ins/Function/prototype/toString/method-class-static': [FAIL],
'built-ins/Function/prototype/toString/method-computed-property-name': [FAIL],
'built-ins/Function/prototype/toString/method-object': [FAIL],
'built-ins/Function/prototype/toString/setter-class': [FAIL],
'built-ins/Function/prototype/toString/setter-class-static': [FAIL],
'built-ins/Function/prototype/toString/setter-object': [FAIL],
'built-ins/Function/prototype/toString/unicode': [FAIL],
'built-ins/Function/prototype/toString/AsyncFunction': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/async-function-declaration': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/async-function-expression': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/async-method': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/Function': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/GeneratorFunction': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/function-declaration': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/function-declaration-non-simple-parameter-list': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/function-expression': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/generator-function-declaration': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/generator-function-expression': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/generator-method': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/getter-class': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/getter-class-static': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/getter-object': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/line-terminator-normalisation-CR': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/line-terminator-normalisation-CR-LF': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/line-terminator-normalisation-LF': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/method-class': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/method-class-static': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/method-computed-property-name': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/method-object': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/setter-class': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/setter-class-static': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/setter-object': ['--harmony-function-tostring'],
'built-ins/Function/prototype/toString/unicode': ['--harmony-function-tostring'],
# https://bugs.chromium.org/p/v8/issues/detail?id=5115
'language/statements/class/subclass/class-definition-null-proto-missing-return-override': [FAIL],