Revision: 24663
Author: [email protected]
Date: Thu Oct 16 13:19:36 2014 UTC
Log: Track usage of "this" and "arguments" in Scope
This adds flags in Scope to track wheter a Scope uses "this" and,
"arguments". The information is exposed via Scope::uses_this(),
and Scope::uses_arguments(), respectively. Flags for tracking
usage on any inner scope uses are available as well via
Scope::inner_uses_this(), and Scope::inner_uses_arguments().
Knowing whether scopes use "this" and "arguments" will be handy
to generate the code needed to capture their values when generating
the code for arrow functions.
BUG=v8:2700
LOG=
[email protected]
Review URL: https://codereview.chromium.org/422923004
Patch from Adrian Perez de Castro <[email protected]>.
https://code.google.com/p/v8/source/detail?r=24663
Modified:
/branches/bleeding_edge/src/ast-value-factory.h
/branches/bleeding_edge/src/globals.h
/branches/bleeding_edge/src/objects.h
/branches/bleeding_edge/src/preparser.h
/branches/bleeding_edge/src/runtime/runtime-debug.cc
/branches/bleeding_edge/src/scopeinfo.cc
/branches/bleeding_edge/src/scopes.cc
/branches/bleeding_edge/src/scopes.h
/branches/bleeding_edge/test/cctest/test-parsing.cc
=======================================
--- /branches/bleeding_edge/src/ast-value-factory.h Thu Oct 2 13:05:11
2014 UTC
+++ /branches/bleeding_edge/src/ast-value-factory.h Thu Oct 16 13:19:36
2014 UTC
@@ -87,6 +87,8 @@
reinterpret_cast<const uint16_t*>(literal_bytes_.start());
return *c;
}
+
+ V8_INLINE bool IsArguments(AstValueFactory* ast_value_factory) const;
// For storing AstRawStrings in a hash map.
uint32_t hash() const {
@@ -340,6 +342,10 @@
#undef F
};
+
+bool AstRawString::IsArguments(AstValueFactory* ast_value_factory) const {
+ return ast_value_factory->arguments_string() == this;
+}
} } // namespace v8::internal
#undef STRING_CONSTANTS
=======================================
--- /branches/bleeding_edge/src/globals.h Thu Oct 2 15:48:48 2014 UTC
+++ /branches/bleeding_edge/src/globals.h Thu Oct 16 13:19:36 2014 UTC
@@ -648,7 +648,8 @@
GLOBAL_SCOPE, // The top-level scope for a program or a top-level
eval.
CATCH_SCOPE, // The scope introduced by catch.
BLOCK_SCOPE, // The scope introduced by a new block.
- WITH_SCOPE // The scope introduced by with.
+ WITH_SCOPE, // The scope introduced by with.
+ ARROW_SCOPE // The top-level scope for an arrow function literal.
};
=======================================
--- /branches/bleeding_edge/src/objects.h Thu Oct 16 11:42:47 2014 UTC
+++ /branches/bleeding_edge/src/objects.h Thu Oct 16 13:19:36 2014 UTC
@@ -4312,13 +4312,13 @@
};
// Properties of scopes.
- class ScopeTypeField: public BitField<ScopeType, 0, 3>
{};
- class CallsEvalField: public BitField<bool, 3, 1>
{};
- class StrictModeField: public BitField<StrictMode, 4, 1>
{};
- class FunctionVariableField: public BitField<FunctionVariableInfo, 5, 2>
{};
- class FunctionVariableMode: public BitField<VariableMode, 7, 3>
{};
- class AsmModuleField : public BitField<bool, 10, 1> {};
- class AsmFunctionField : public BitField<bool, 11, 1> {};
+ class ScopeTypeField : public BitField<ScopeType, 0, 4> {};
+ class CallsEvalField : public BitField<bool, 4, 1> {};
+ class StrictModeField : public BitField<StrictMode, 5, 1> {};
+ class FunctionVariableField : public BitField<FunctionVariableInfo, 6,
2> {};
+ class FunctionVariableMode : public BitField<VariableMode, 8, 3> {};
+ class AsmModuleField : public BitField<bool, 11, 1> {};
+ class AsmFunctionField : public BitField<bool, 12, 1> {};
// BitFields representing the encoded information for context locals in
the
// ContextLocalInfoEntries part.
=======================================
--- /branches/bleeding_edge/src/preparser.h Thu Oct 9 10:40:18 2014 UTC
+++ /branches/bleeding_edge/src/preparser.h Thu Oct 16 13:19:36 2014 UTC
@@ -615,7 +615,9 @@
return PreParserIdentifier(kConstructorIdentifier);
}
bool IsEval() const { return type_ == kEvalIdentifier; }
- bool IsArguments() const { return type_ == kArgumentsIdentifier; }
+ bool IsArguments(const AstValueFactory* = NULL) const {
+ return type_ == kArgumentsIdentifier;
+ }
bool IsYield() const { return type_ == kYieldIdentifier; }
bool IsPrototype() const { return type_ == kPrototypeIdentifier; }
bool IsConstructor() const { return type_ == kConstructorIdentifier; }
@@ -956,6 +958,8 @@
bool IsDeclared(const PreParserIdentifier& identifier) const { return
false; }
void DeclareParameter(const PreParserIdentifier& identifier,
VariableMode) {}
+ void RecordArgumentsUsage() {}
+ void RecordThisUsage() {}
// Allow scope->Foo() to work.
PreParserScope* operator->() { return this; }
@@ -1615,6 +1619,8 @@
ReportMessage("strict_eval_arguments");
*ok = false;
}
+ if (name->IsArguments(this->ast_value_factory()))
+ scope_->RecordArgumentsUsage();
return name;
} else if (strict_mode() == SLOPPY &&
(next == Token::FUTURE_STRICT_RESERVED_WORD ||
@@ -1645,7 +1651,11 @@
*ok = false;
return Traits::EmptyIdentifier();
}
- return this->GetSymbol(scanner());
+
+ IdentifierT name = this->GetSymbol(scanner());
+ if (name->IsArguments(this->ast_value_factory()))
+ scope_->RecordArgumentsUsage();
+ return name;
}
@@ -1660,7 +1670,11 @@
*ok = false;
return Traits::EmptyIdentifier();
}
- return this->GetSymbol(scanner());
+
+ IdentifierT name = this->GetSymbol(scanner());
+ if (name->IsArguments(this->ast_value_factory()))
+ scope_->RecordArgumentsUsage();
+ return name;
}
@@ -1738,6 +1752,7 @@
switch (token) {
case Token::THIS: {
Consume(Token::THIS);
+ scope_->RecordThisUsage();
result = this->ThisExpression(scope_, factory());
break;
}
@@ -2591,9 +2606,7 @@
typename ParserBase<Traits>::ExpressionT ParserBase<
Traits>::ParseArrowFunctionLiteral(int start_pos, ExpressionT
params_ast,
bool* ok) {
- // TODO(aperez): Change this to use ARROW_SCOPE
- typename Traits::Type::ScopePtr scope =
- this->NewScope(scope_, FUNCTION_SCOPE);
+ typename Traits::Type::ScopePtr scope = this->NewScope(scope_,
ARROW_SCOPE);
typename Traits::Type::StatementList body;
typename Traits::Type::AstProperties ast_properties;
BailoutReason dont_optimize_reason = kNoReason;
=======================================
--- /branches/bleeding_edge/src/runtime/runtime-debug.cc Fri Oct 10
14:59:53 2014 UTC
+++ /branches/bleeding_edge/src/runtime/runtime-debug.cc Thu Oct 16
13:19:36 2014 UTC
@@ -1132,7 +1132,8 @@
context_ = Handle<Context>(context_->previous(), isolate_);
}
}
- if (scope_info->scope_type() == FUNCTION_SCOPE) {
+ if (scope_info->scope_type() == FUNCTION_SCOPE ||
+ scope_info->scope_type() == ARROW_SCOPE) {
nested_scope_chain_.Add(scope_info);
}
} else {
@@ -1142,7 +1143,8 @@
// Check whether we are in global, eval or function code.
Handle<ScopeInfo> scope_info(shared_info->scope_info());
- if (scope_info->scope_type() != FUNCTION_SCOPE) {
+ if (scope_info->scope_type() != FUNCTION_SCOPE &&
+ scope_info->scope_type() != ARROW_SCOPE) {
// Global or eval code.
CompilationInfoWithZone info(script);
if (scope_info->scope_type() == GLOBAL_SCOPE) {
@@ -1215,6 +1217,7 @@
Handle<ScopeInfo> scope_info = nested_scope_chain_.last();
switch (scope_info->scope_type()) {
case FUNCTION_SCOPE:
+ case ARROW_SCOPE:
DCHECK(context_->IsFunctionContext() |
| !scope_info->HasContext());
return ScopeTypeLocal;
case MODULE_SCOPE:
=======================================
--- /branches/bleeding_edge/src/scopeinfo.cc Fri Sep 19 12:50:50 2014 UTC
+++ /branches/bleeding_edge/src/scopeinfo.cc Thu Oct 16 13:19:36 2014 UTC
@@ -170,11 +170,11 @@
int context_locals = ContextLocalCount();
bool function_name_context_slot =
FunctionVariableField::decode(Flags()) == CONTEXT;
- bool has_context = context_locals > 0 ||
- function_name_context_slot ||
- scope_type() == WITH_SCOPE ||
- (scope_type() == FUNCTION_SCOPE && CallsEval()) ||
- scope_type() == MODULE_SCOPE;
+ bool has_context = context_locals > 0 || function_name_context_slot ||
+ scope_type() == WITH_SCOPE ||
+ (scope_type() == ARROW_SCOPE && CallsEval()) ||
+ (scope_type() == FUNCTION_SCOPE && CallsEval()) ||
+ scope_type() == MODULE_SCOPE;
if (has_context) {
return Context::MIN_CONTEXT_SLOTS + context_locals +
(function_name_context_slot ? 1 : 0);
=======================================
--- /branches/bleeding_edge/src/scopes.cc Mon Oct 6 12:56:11 2014 UTC
+++ /branches/bleeding_edge/src/scopes.cc Thu Oct 16 13:19:36 2014 UTC
@@ -160,12 +160,16 @@
scope_inside_with_ = false;
scope_contains_with_ = false;
scope_calls_eval_ = false;
+ scope_uses_this_ = false;
+ scope_uses_arguments_ = false;
asm_module_ = false;
asm_function_ = outer_scope != NULL && outer_scope->asm_module_;
// Inherit the strict mode from the parent scope.
strict_mode_ = outer_scope != NULL ? outer_scope->strict_mode_ : SLOPPY;
outer_scope_calls_sloppy_eval_ = false;
inner_scope_calls_eval_ = false;
+ inner_scope_uses_this_ = false;
+ inner_scope_uses_arguments_ = false;
force_eager_compilation_ = false;
force_context_allocation_ = (outer_scope != NULL && !is_function_scope())
? outer_scope->has_forced_context_allocation() : false;
@@ -779,6 +783,7 @@
case CATCH_SCOPE: return "catch";
case BLOCK_SCOPE: return "block";
case WITH_SCOPE: return "with";
+ case ARROW_SCOPE: return "arrow";
}
UNREACHABLE();
return NULL;
@@ -885,6 +890,12 @@
if (scope_inside_with_) Indent(n1, "// scope inside 'with'\n");
if (scope_contains_with_) Indent(n1, "// scope contains 'with'\n");
if (scope_calls_eval_) Indent(n1, "// scope calls 'eval'\n");
+ if (scope_uses_this_) Indent(n1, "// scope uses 'this'\n");
+ if (scope_uses_arguments_) Indent(n1, "// scope uses 'arguments'\n");
+ if (inner_scope_uses_this_) Indent(n1, "// inner scope uses 'this'\n");
+ if (inner_scope_uses_arguments_) {
+ Indent(n1, "// inner scope uses 'arguments'\n");
+ }
if (outer_scope_calls_sloppy_eval_) {
Indent(n1, "// outer scope calls 'eval' in sloppy context\n");
}
@@ -1166,6 +1177,17 @@
if (inner->scope_calls_eval_ || inner->inner_scope_calls_eval_) {
inner_scope_calls_eval_ = true;
}
+ // If the inner scope is an arrow function, propagate the flags
tracking
+ // usage of this/arguments, but do not propagate them out from normal
+ // functions.
+ if (!inner->is_function_scope() || inner->is_arrow_scope()) {
+ if (inner->scope_uses_this_ || inner->inner_scope_uses_this_) {
+ inner_scope_uses_this_ = true;
+ }
+ if (inner->scope_uses_arguments_ ||
inner->inner_scope_uses_arguments_) {
+ inner_scope_uses_arguments_ = true;
+ }
+ }
if (inner->force_eager_compilation_) {
force_eager_compilation_ = true;
}
=======================================
--- /branches/bleeding_edge/src/scopes.h Fri Sep 19 12:50:50 2014 UTC
+++ /branches/bleeding_edge/src/scopes.h Thu Oct 16 13:19:36 2014 UTC
@@ -210,6 +210,12 @@
// Inform the scope that the corresponding code contains an eval call.
void RecordEvalCall() { if (!is_global_scope()) scope_calls_eval_ =
true; }
+
+ // Inform the scope that the corresponding code uses "this".
+ void RecordThisUsage() { scope_uses_this_ = true; }
+
+ // Inform the scope that the corresponding code uses "arguments".
+ void RecordArgumentsUsage() { scope_uses_arguments_ = true; }
// Set the strict mode flag (unless disabled by a global flag).
void SetStrictMode(StrictMode strict_mode) { strict_mode_ = strict_mode;
}
@@ -262,12 +268,15 @@
// Specific scope types.
bool is_eval_scope() const { return scope_type_ == EVAL_SCOPE; }
- bool is_function_scope() const { return scope_type_ == FUNCTION_SCOPE; }
+ bool is_function_scope() const {
+ return scope_type_ == FUNCTION_SCOPE || scope_type_ == ARROW_SCOPE;
+ }
bool is_module_scope() const { return scope_type_ == MODULE_SCOPE; }
bool is_global_scope() const { return scope_type_ == GLOBAL_SCOPE; }
bool is_catch_scope() const { return scope_type_ == CATCH_SCOPE; }
bool is_block_scope() const { return scope_type_ == BLOCK_SCOPE; }
bool is_with_scope() const { return scope_type_ == WITH_SCOPE; }
+ bool is_arrow_scope() const { return scope_type_ == ARROW_SCOPE; }
bool is_declaration_scope() const {
return is_eval_scope() || is_function_scope() ||
is_module_scope() || is_global_scope();
@@ -291,6 +300,15 @@
bool inside_with() const { return scope_inside_with_; }
// Does this scope contain a with statement.
bool contains_with() const { return scope_contains_with_; }
+
+ // Does this scope access "this".
+ bool uses_this() const { return scope_uses_this_; }
+ // Does any inner scope access "this".
+ bool inner_uses_this() const { return inner_scope_uses_this_; }
+ // Does this scope access "arguments".
+ bool uses_arguments() const { return scope_uses_arguments_; }
+ // Does any inner scope access "arguments".
+ bool inner_uses_arguments() const { return inner_scope_uses_arguments_; }
//
---------------------------------------------------------------------------
// Accessors.
@@ -468,6 +486,10 @@
// This scope or a nested catch scope or with scope contain an 'eval'
call. At
// the 'eval' call site this scope is the declaration scope.
bool scope_calls_eval_;
+ // This scope uses "this".
+ bool scope_uses_this_;
+ // This scope uses "arguments".
+ bool scope_uses_arguments_;
// This scope contains an "use asm" annotation.
bool asm_module_;
// This scope's outer context is an asm module.
@@ -481,6 +503,8 @@
// Computed via PropagateScopeInfo.
bool outer_scope_calls_sloppy_eval_;
bool inner_scope_calls_eval_;
+ bool inner_scope_uses_this_;
+ bool inner_scope_uses_arguments_;
bool force_eager_compilation_;
bool force_context_allocation_;
=======================================
--- /branches/bleeding_edge/test/cctest/test-parsing.cc Mon Sep 29 14:15:48
2014 UTC
+++ /branches/bleeding_edge/test/cctest/test-parsing.cc Thu Oct 16 13:19:36
2014 UTC
@@ -915,6 +915,122 @@
}
return character_length;
}
+
+
+TEST(ScopeUsesThisAndArguments) {
+ static const struct {
+ const char* prefix;
+ const char* suffix;
+ } surroundings[] = {
+ { "function f() {", "}" },
+ { "var f = () => {", "}" },
+ };
+
+ static const struct {
+ const char* body;
+ bool uses_this;
+ bool uses_arguments;
+ bool inner_uses_this;
+ bool inner_uses_arguments;
+ } source_data[] = {
+ { "",
+ false, false, false, false },
+ { "return this",
+ true, false, false, false },
+ { "return arguments",
+ false, true, false, false },
+ { "return arguments[0]",
+ false, true, false, false },
+ { "return this + arguments[0]",
+ true, true, false, false },
+ { "return x => this + x",
+ false, false, true, false },
+ { "this.foo = 42;",
+ true, false, false, false },
+ { "this.foo();",
+ true, false, false, false },
+ { "if (foo()) { this.f() }",
+ true, false, false, false },
+ { "if (arguments.length) { this.f() }",
+ true, true, false, false },
+ { "while (true) { this.f() }",
+ true, false, false, false },
+ { "if (true) { while (true) this.foo(arguments) }",
+ true, true, false, false },
+ // Multiple nesting levels must work as well.
+ { "while (true) { while (true) { while (true) return this } }",
+ true, false, false, false },
+ { "if (1) { return () => { while (true) new this() } }",
+ false, false, true, false },
+ // Note that propagation of the inner_uses_this() value does not
+ // cross boundaries of normal functions onto parent scopes.
+ { "return function (x) { return this + x }",
+ false, false, false, false },
+ { "var x = function () { this.foo = 42 };",
+ false, false, false, false },
+ { "if (1) { return function () { while (true) new this() } }",
+ false, false, false, false },
+ { "return function (x) { return () => this }",
+ false, false, false, false },
+ // Flags must be correctly set when using block scoping.
+ { "\"use strict\"; while (true) { let x; this, arguments; }",
+ false, false, true, true },
+ { "\"use strict\"; if (foo()) { let x; this.f() }",
+ false, false, true, false },
+ { "\"use strict\"; if (1) {"
+ " let x; return function () { return this + arguments }"
+ "}",
+ false, false, false, false },
+ };
+
+ i::Isolate* isolate = CcTest::i_isolate();
+ i::Factory* factory = isolate->factory();
+
+ v8::HandleScope handles(CcTest::isolate());
+ v8::Handle<v8::Context> context = v8::Context::New(CcTest::isolate());
+ v8::Context::Scope context_scope(context);
+
+ isolate->stack_guard()->SetStackLimit(i::GetCurrentStackPosition() -
+ 128 * 1024);
+
+ for (unsigned j = 0; j < arraysize(surroundings); ++j) {
+ for (unsigned i = 0; i < arraysize(source_data); ++i) {
+ int kProgramByteSize = i::StrLength(surroundings[j].prefix) +
+ i::StrLength(surroundings[j].suffix) +
+ i::StrLength(source_data[i].body);
+ i::ScopedVector<char> program(kProgramByteSize + 1);
+ i::SNPrintF(program, "%s%s%s", surroundings[j].prefix,
+ source_data[i].body, surroundings[j].suffix);
+ i::Handle<i::String> source =
+ factory->NewStringFromUtf8(i::CStrVector(program.start()))
+ .ToHandleChecked();
+ i::Handle<i::Script> script = factory->NewScript(source);
+ i::CompilationInfoWithZone info(script);
+ i::Parser::ParseInfo parse_info =
{isolate->stack_guard()->real_climit(),
+ isolate->heap()->HashSeed(),
+ isolate->unicode_cache()};
+ i::Parser parser(&info, &parse_info);
+ parser.set_allow_arrow_functions(true);
+ parser.set_allow_harmony_scoping(true);
+ info.MarkAsGlobal();
+ parser.Parse();
+ CHECK(i::Rewriter::Rewrite(&info));
+ CHECK(i::Scope::Analyze(&info));
+ CHECK(info.function() != NULL);
+
+ i::Scope* global_scope = info.function()->scope();
+ CHECK(global_scope->is_global_scope());
+ CHECK_EQ(1, global_scope->inner_scopes()->length());
+
+ i::Scope* scope = global_scope->inner_scopes()->at(0);
+ CHECK_EQ(source_data[i].uses_this, scope->uses_this());
+ CHECK_EQ(source_data[i].uses_arguments, scope->uses_arguments());
+ CHECK_EQ(source_data[i].inner_uses_this, scope->inner_uses_this());
+ CHECK_EQ(source_data[i].inner_uses_arguments,
+ scope->inner_uses_arguments());
+ }
+ }
+}
TEST(ScopePositions) {
@@ -974,11 +1090,10 @@
" infunction;\n"
" }", "\n"
" more;", i::FUNCTION_SCOPE, i::SLOPPY },
- // TODO(aperez): Change to use i::ARROW_SCOPE when implemented
{ " start;\n", "(a,b) => a + b", "; more;",
- i::FUNCTION_SCOPE, i::SLOPPY },
+ i::ARROW_SCOPE, i::SLOPPY },
{ " start;\n", "(a,b) => { return a+b; }", "\nmore;",
- i::FUNCTION_SCOPE, i::SLOPPY },
+ i::ARROW_SCOPE, i::SLOPPY },
{ " start;\n"
" (function fun", "(a,b) { infunction; }", ")();",
i::FUNCTION_SCOPE, i::SLOPPY },
--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.