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:
parent
0393b11dea
commit
d1d4b9ce51
29
src/api.cc
29
src/api.cc
@ -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();
|
||||
|
@ -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};
|
||||
|
@ -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,
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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(
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -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")
|
||||
|
@ -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, \
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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),
|
||||
|
@ -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_;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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_;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -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_;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_);
|
||||
|
@ -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);
|
||||
|
124
test/mjsunit/harmony/function-tostring.js
Normal file
124
test/mjsunit/harmony/function-tostring.js
Normal 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*/");
|
@ -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],
|
||||
|
Loading…
Reference in New Issue
Block a user