Reapply "Fix the ScopeIterator reimplementation".
This reapplies a fixed version of r10076 that also works on arm. Patch set one is r10076 reapplied and patch set 2 contains the new fix. Review URL: http://codereview.chromium.org/8725001 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10080 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
e26093f3d8
commit
b3a2e242db
12
src/ast.h
12
src/ast.h
@ -1140,6 +1140,11 @@ class VariableProxy: public Expression {
|
||||
public:
|
||||
VariableProxy(Isolate* isolate, Variable* var);
|
||||
|
||||
VariableProxy(Isolate* isolate,
|
||||
Handle<String> name,
|
||||
bool is_this,
|
||||
int position = RelocInfo::kNoPosition);
|
||||
|
||||
DECLARE_NODE_TYPE(VariableProxy)
|
||||
|
||||
virtual bool IsValidLeftHandSide() {
|
||||
@ -1170,13 +1175,6 @@ class VariableProxy: public Expression {
|
||||
bool is_this_;
|
||||
bool is_trivial_;
|
||||
int position_;
|
||||
|
||||
VariableProxy(Isolate* isolate,
|
||||
Handle<String> name,
|
||||
bool is_this,
|
||||
int position = RelocInfo::kNoPosition);
|
||||
|
||||
friend class Scope;
|
||||
};
|
||||
|
||||
|
||||
|
@ -5562,7 +5562,7 @@ static ScriptDataImpl* DoPreParse(UC16CharacterStream* source,
|
||||
|
||||
// Preparse, but only collect data that is immediately useful,
|
||||
// even if the preparser data is only used once.
|
||||
ScriptDataImpl* ParserApi::PartialPreParse(UC16CharacterStream* source,
|
||||
ScriptDataImpl* ParserApi::PartialPreParse(Handle<String> source,
|
||||
v8::Extension* extension,
|
||||
int flags) {
|
||||
bool allow_lazy = FLAG_lazy && (extension == NULL);
|
||||
@ -5573,7 +5573,15 @@ ScriptDataImpl* ParserApi::PartialPreParse(UC16CharacterStream* source,
|
||||
}
|
||||
flags |= kAllowLazy;
|
||||
PartialParserRecorder recorder;
|
||||
return DoPreParse(source, flags, &recorder);
|
||||
int source_length = source->length();
|
||||
if (source->IsExternalTwoByteString()) {
|
||||
ExternalTwoByteStringUC16CharacterStream stream(
|
||||
Handle<ExternalTwoByteString>::cast(source), 0, source_length);
|
||||
return DoPreParse(&stream, flags, &recorder);
|
||||
} else {
|
||||
GenericStringUC16CharacterStream stream(source, 0, source_length);
|
||||
return DoPreParse(&stream, flags, &recorder);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -178,7 +178,7 @@ class ParserApi {
|
||||
|
||||
// Preparser that only does preprocessing that makes sense if only used
|
||||
// immediately after.
|
||||
static ScriptDataImpl* PartialPreParse(UC16CharacterStream* source,
|
||||
static ScriptDataImpl* PartialPreParse(Handle<String> source,
|
||||
v8::Extension* extension,
|
||||
int flags);
|
||||
};
|
||||
|
@ -236,10 +236,22 @@ bool Rewriter::Rewrite(CompilationInfo* info) {
|
||||
if (processor.HasStackOverflow()) return false;
|
||||
|
||||
if (processor.result_assigned()) {
|
||||
ASSERT(function->end_position() != RelocInfo::kNoPosition);
|
||||
Isolate* isolate = info->isolate();
|
||||
Zone* zone = isolate->zone();
|
||||
VariableProxy* result_proxy = new(zone) VariableProxy(isolate, result);
|
||||
body->Add(new(zone) ReturnStatement(result_proxy));
|
||||
// Set the position of the assignment statement one character past the
|
||||
// source code, such that it definitely is not in the source code range
|
||||
// of an immediate inner scope. For example in
|
||||
// eval('with ({x:1}) x = 1');
|
||||
// the end position of the function generated for executing the eval code
|
||||
// coincides with the end of the with scope which is the position of '1'.
|
||||
int position = function->end_position();
|
||||
VariableProxy* result_proxy = new(zone) VariableProxy(
|
||||
isolate, result->name(), false, position);
|
||||
result_proxy->BindTo(result);
|
||||
Statement* result_statement = new(zone) ReturnStatement(result_proxy);
|
||||
result_statement->set_statement_pos(position);
|
||||
body->Add(result_statement);
|
||||
}
|
||||
}
|
||||
|
||||
|
276
src/runtime.cc
276
src/runtime.cc
@ -11201,27 +11201,83 @@ class ScopeIterator {
|
||||
inlined_frame_index_(inlined_frame_index),
|
||||
function_(JSFunction::cast(frame->function())),
|
||||
context_(Context::cast(frame->context())),
|
||||
local_done_(false),
|
||||
at_local_(false) {
|
||||
nested_scope_chain_(4) {
|
||||
|
||||
// Check whether the first scope is actually a local scope.
|
||||
// If there is a stack slot for .result then this local scope has been
|
||||
// created for evaluating top level code and it is not a real local scope.
|
||||
// Checking for the existence of .result seems fragile, but the scope info
|
||||
// saved with the code object does not otherwise have that information.
|
||||
int index = function_->shared()->scope_info()->
|
||||
StackSlotIndex(isolate_->heap()->result_symbol());
|
||||
if (index >= 0) {
|
||||
local_done_ = true;
|
||||
} else if (context_->IsGlobalContext() ||
|
||||
context_->IsFunctionContext()) {
|
||||
at_local_ = true;
|
||||
} else if (context_->closure() != *function_) {
|
||||
// The context_ is a block or with or catch block from the outer function.
|
||||
ASSERT(context_->IsWithContext() ||
|
||||
context_->IsCatchContext() ||
|
||||
context_->IsBlockContext());
|
||||
at_local_ = true;
|
||||
// Catch the case when the debugger stops in an internal function.
|
||||
Handle<SharedFunctionInfo> shared_info(function_->shared());
|
||||
Handle<ScopeInfo> scope_info(shared_info->scope_info());
|
||||
if (shared_info->script() == isolate->heap()->undefined_value()) {
|
||||
while (context_->closure() == *function_) {
|
||||
context_ = Handle<Context>(context_->previous(), isolate_);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the debug info (create it if it does not exist).
|
||||
if (!isolate->debug()->EnsureDebugInfo(shared_info)) {
|
||||
// Return if ensuring debug info failed.
|
||||
return;
|
||||
}
|
||||
Handle<DebugInfo> debug_info = Debug::GetDebugInfo(shared_info);
|
||||
|
||||
// Find the break point where execution has stopped.
|
||||
BreakLocationIterator break_location_iterator(debug_info,
|
||||
ALL_BREAK_LOCATIONS);
|
||||
break_location_iterator.FindBreakLocationFromAddress(frame->pc());
|
||||
if (break_location_iterator.IsExit()) {
|
||||
// We are within the return sequence. At the momemt it is not possible to
|
||||
// get a source position which is consistent with the current scope chain.
|
||||
// Thus all nested with, catch and block contexts are skipped and we only
|
||||
// provide the function scope.
|
||||
if (scope_info->HasContext()) {
|
||||
context_ = Handle<Context>(context_->declaration_context(), isolate_);
|
||||
} else {
|
||||
while (context_->closure() == *function_) {
|
||||
context_ = Handle<Context>(context_->previous(), isolate_);
|
||||
}
|
||||
}
|
||||
if (scope_info->Type() != EVAL_SCOPE) nested_scope_chain_.Add(scope_info);
|
||||
} else {
|
||||
// Reparse the code and analyze the scopes.
|
||||
ZoneScope zone_scope(isolate, DELETE_ON_EXIT);
|
||||
Handle<Script> script(Script::cast(shared_info->script()));
|
||||
Scope* scope = NULL;
|
||||
|
||||
// Check whether we are in global, eval or function code.
|
||||
Handle<ScopeInfo> scope_info(shared_info->scope_info());
|
||||
if (scope_info->Type() != FUNCTION_SCOPE) {
|
||||
// Global or eval code.
|
||||
CompilationInfo info(script);
|
||||
if (scope_info->Type() == GLOBAL_SCOPE) {
|
||||
info.MarkAsGlobal();
|
||||
} else {
|
||||
ASSERT(scope_info->Type() == EVAL_SCOPE);
|
||||
info.MarkAsEval();
|
||||
info.SetCallingContext(Handle<Context>(function_->context()));
|
||||
}
|
||||
if (ParserApi::Parse(&info, kNoParsingFlags) && Scope::Analyze(&info)) {
|
||||
scope = info.function()->scope();
|
||||
}
|
||||
} else {
|
||||
// Function code
|
||||
CompilationInfo info(shared_info);
|
||||
if (ParserApi::Parse(&info, kNoParsingFlags) && Scope::Analyze(&info)) {
|
||||
scope = info.function()->scope();
|
||||
}
|
||||
}
|
||||
|
||||
// Retrieve the scope chain for the current position.
|
||||
if (scope != NULL) {
|
||||
int source_position = shared_info->code()->SourcePosition(frame_->pc());
|
||||
scope->GetNestedScopeChain(&nested_scope_chain_, source_position);
|
||||
} else {
|
||||
// A failed reparse indicates that the preparser has diverged from the
|
||||
// parser or that the preparse data given to the initial parse has been
|
||||
// faulty. We fail in debug mode but in release mode we only provide the
|
||||
// information we get from the context chain but nothing about
|
||||
// completely stack allocated scopes or stack allocated locals.
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11230,40 +11286,49 @@ class ScopeIterator {
|
||||
|
||||
// Move to the next scope.
|
||||
void Next() {
|
||||
// If at a local scope mark the local scope as passed.
|
||||
if (at_local_) {
|
||||
at_local_ = false;
|
||||
local_done_ = true;
|
||||
|
||||
// If the current context is not associated with the local scope the
|
||||
// current context is the next real scope, so don't move to the next
|
||||
// context in this case.
|
||||
if (context_->closure() != *function_) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The global scope is always the last in the chain.
|
||||
if (context_->IsGlobalContext()) {
|
||||
ScopeType scope_type = Type();
|
||||
if (scope_type == ScopeTypeGlobal) {
|
||||
// The global scope is always the last in the chain.
|
||||
ASSERT(context_->IsGlobalContext());
|
||||
context_ = Handle<Context>();
|
||||
return;
|
||||
}
|
||||
|
||||
// Move to the next context.
|
||||
context_ = Handle<Context>(context_->previous(), isolate_);
|
||||
|
||||
// If passing the local scope indicate that the current scope is now the
|
||||
// local scope.
|
||||
if (!local_done_ &&
|
||||
(context_->IsGlobalContext() || context_->IsFunctionContext())) {
|
||||
at_local_ = true;
|
||||
if (nested_scope_chain_.is_empty()) {
|
||||
context_ = Handle<Context>(context_->previous(), isolate_);
|
||||
} else {
|
||||
if (nested_scope_chain_.last()->HasContext()) {
|
||||
ASSERT(context_->previous() != NULL);
|
||||
context_ = Handle<Context>(context_->previous(), isolate_);
|
||||
}
|
||||
nested_scope_chain_.RemoveLast();
|
||||
}
|
||||
}
|
||||
|
||||
// Return the type of the current scope.
|
||||
ScopeType Type() {
|
||||
if (at_local_) {
|
||||
return ScopeTypeLocal;
|
||||
if (!nested_scope_chain_.is_empty()) {
|
||||
Handle<ScopeInfo> scope_info = nested_scope_chain_.last();
|
||||
switch (scope_info->Type()) {
|
||||
case FUNCTION_SCOPE:
|
||||
ASSERT(context_->IsFunctionContext() ||
|
||||
!scope_info->HasContext());
|
||||
return ScopeTypeLocal;
|
||||
case GLOBAL_SCOPE:
|
||||
ASSERT(context_->IsGlobalContext());
|
||||
return ScopeTypeGlobal;
|
||||
case WITH_SCOPE:
|
||||
ASSERT(context_->IsWithContext());
|
||||
return ScopeTypeWith;
|
||||
case CATCH_SCOPE:
|
||||
ASSERT(context_->IsCatchContext());
|
||||
return ScopeTypeCatch;
|
||||
case BLOCK_SCOPE:
|
||||
ASSERT(!scope_info->HasContext() ||
|
||||
context_->IsBlockContext());
|
||||
return ScopeTypeBlock;
|
||||
case EVAL_SCOPE:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
if (context_->IsGlobalContext()) {
|
||||
ASSERT(context_->global()->IsGlobalObject());
|
||||
@ -11289,6 +11354,7 @@ class ScopeIterator {
|
||||
return Handle<JSObject>(CurrentContext()->global());
|
||||
case ScopeIterator::ScopeTypeLocal:
|
||||
// Materialize the content of the local scope into a JSObject.
|
||||
ASSERT(nested_scope_chain_.length() == 1);
|
||||
return MaterializeLocalScope(isolate_, frame_, inlined_frame_index_);
|
||||
case ScopeIterator::ScopeTypeWith:
|
||||
// Return the with object.
|
||||
@ -11305,13 +11371,28 @@ class ScopeIterator {
|
||||
return Handle<JSObject>();
|
||||
}
|
||||
|
||||
Handle<ScopeInfo> CurrentScopeInfo() {
|
||||
if (!nested_scope_chain_.is_empty()) {
|
||||
return nested_scope_chain_.last();
|
||||
} else if (context_->IsBlockContext()) {
|
||||
return Handle<ScopeInfo>(ScopeInfo::cast(context_->extension()));
|
||||
} else if (context_->IsFunctionContext()) {
|
||||
return Handle<ScopeInfo>(context_->closure()->shared()->scope_info());
|
||||
}
|
||||
return Handle<ScopeInfo>::null();
|
||||
}
|
||||
|
||||
// Return the context for this scope. For the local context there might not
|
||||
// be an actual context.
|
||||
Handle<Context> CurrentContext() {
|
||||
if (at_local_ && context_->closure() != *function_) {
|
||||
if (Type() == ScopeTypeGlobal ||
|
||||
nested_scope_chain_.is_empty()) {
|
||||
return context_;
|
||||
} else if (nested_scope_chain_.last()->HasContext()) {
|
||||
return context_;
|
||||
} else {
|
||||
return Handle<Context>();
|
||||
}
|
||||
return context_;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -11373,8 +11454,7 @@ class ScopeIterator {
|
||||
int inlined_frame_index_;
|
||||
Handle<JSFunction> function_;
|
||||
Handle<Context> context_;
|
||||
bool local_done_;
|
||||
bool at_local_;
|
||||
List<Handle<ScopeInfo> > nested_scope_chain_;
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);
|
||||
};
|
||||
@ -11837,45 +11917,65 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ClearStepping) {
|
||||
|
||||
// Creates a copy of the with context chain. The copy of the context chain is
|
||||
// is linked to the function context supplied.
|
||||
static Handle<Context> CopyWithContextChain(Isolate* isolate,
|
||||
Handle<JSFunction> function,
|
||||
Handle<Context> current,
|
||||
Handle<Context> base) {
|
||||
// At the end of the chain. Return the base context to link to.
|
||||
if (current->IsFunctionContext() || current->IsGlobalContext()) {
|
||||
return base;
|
||||
static Handle<Context> CopyNestedScopeContextChain(Isolate* isolate,
|
||||
Handle<JSFunction> function,
|
||||
Handle<Context> base,
|
||||
JavaScriptFrame* frame,
|
||||
int inlined_frame_index) {
|
||||
HandleScope scope(isolate);
|
||||
List<Handle<ScopeInfo> > scope_chain;
|
||||
List<Handle<Context> > context_chain;
|
||||
|
||||
ScopeIterator it(isolate, frame, inlined_frame_index);
|
||||
for (; it.Type() != ScopeIterator::ScopeTypeGlobal &&
|
||||
it.Type() != ScopeIterator::ScopeTypeLocal ; it.Next()) {
|
||||
ASSERT(!it.Done());
|
||||
scope_chain.Add(it.CurrentScopeInfo());
|
||||
context_chain.Add(it.CurrentContext());
|
||||
}
|
||||
|
||||
// Recursively copy the with and catch contexts.
|
||||
HandleScope scope(isolate);
|
||||
Handle<Context> previous(current->previous());
|
||||
Handle<Context> new_previous =
|
||||
CopyWithContextChain(isolate, function, previous, base);
|
||||
Handle<Context> new_current;
|
||||
if (current->IsCatchContext()) {
|
||||
Handle<String> name(String::cast(current->extension()));
|
||||
Handle<Object> thrown_object(current->get(Context::THROWN_OBJECT_INDEX));
|
||||
new_current =
|
||||
isolate->factory()->NewCatchContext(function,
|
||||
new_previous,
|
||||
name,
|
||||
thrown_object);
|
||||
} else if (current->IsBlockContext()) {
|
||||
Handle<ScopeInfo> scope_info(ScopeInfo::cast(current->extension()));
|
||||
new_current =
|
||||
isolate->factory()->NewBlockContext(function, new_previous, scope_info);
|
||||
// Copy context slots.
|
||||
int num_context_slots = scope_info->ContextLength();
|
||||
for (int i = Context::MIN_CONTEXT_SLOTS; i < num_context_slots; ++i) {
|
||||
new_current->set(i, current->get(i));
|
||||
// At the end of the chain. Return the base context to link to.
|
||||
Handle<Context> context = base;
|
||||
|
||||
// Iteratively copy and or materialize the nested contexts.
|
||||
while (!scope_chain.is_empty()) {
|
||||
Handle<ScopeInfo> scope_info = scope_chain.RemoveLast();
|
||||
Handle<Context> current = context_chain.RemoveLast();
|
||||
ASSERT(!(scope_info->HasContext() & current.is_null()));
|
||||
|
||||
if (scope_info->Type() == CATCH_SCOPE) {
|
||||
Handle<String> name(String::cast(current->extension()));
|
||||
Handle<Object> thrown_object(current->get(Context::THROWN_OBJECT_INDEX));
|
||||
context =
|
||||
isolate->factory()->NewCatchContext(function,
|
||||
context,
|
||||
name,
|
||||
thrown_object);
|
||||
} else if (scope_info->Type() == BLOCK_SCOPE) {
|
||||
// Materialize the contents of the block scope into a JSObject.
|
||||
Handle<JSObject> block_scope_object =
|
||||
MaterializeBlockScope(isolate, current);
|
||||
if (block_scope_object.is_null()) {
|
||||
return Handle<Context>::null();
|
||||
}
|
||||
// Allocate a new function context for the debug evaluation and set the
|
||||
// extension object.
|
||||
Handle<Context> new_context =
|
||||
isolate->factory()->NewFunctionContext(Context::MIN_CONTEXT_SLOTS,
|
||||
function);
|
||||
new_context->set_extension(*block_scope_object);
|
||||
new_context->set_previous(*context);
|
||||
context = new_context;
|
||||
} else {
|
||||
ASSERT(scope_info->Type() == WITH_SCOPE);
|
||||
ASSERT(current->IsWithContext());
|
||||
Handle<JSObject> extension(JSObject::cast(current->extension()));
|
||||
context =
|
||||
isolate->factory()->NewWithContext(function, context, extension);
|
||||
}
|
||||
} else {
|
||||
ASSERT(current->IsWithContext());
|
||||
Handle<JSObject> extension(JSObject::cast(current->extension()));
|
||||
new_current =
|
||||
isolate->factory()->NewWithContext(function, new_previous, extension);
|
||||
}
|
||||
return scope.CloseAndEscape(new_current);
|
||||
|
||||
return scope.CloseAndEscape(context);
|
||||
}
|
||||
|
||||
|
||||
@ -12010,7 +12110,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DebugEvaluate) {
|
||||
if (scope_info->HasContext()) {
|
||||
function_context = Handle<Context>(frame_context->declaration_context());
|
||||
}
|
||||
context = CopyWithContextChain(isolate, go_between, frame_context, context);
|
||||
context = CopyNestedScopeContextChain(isolate,
|
||||
go_between,
|
||||
context,
|
||||
frame,
|
||||
inlined_frame_index);
|
||||
|
||||
if (additional_context->IsJSObject()) {
|
||||
Handle<JSObject> extension = Handle<JSObject>::cast(additional_context);
|
||||
|
@ -661,14 +661,14 @@ Handle<ScopeInfo> Scope::GetScopeInfo() {
|
||||
void Scope::GetNestedScopeChain(
|
||||
List<Handle<ScopeInfo> >* chain,
|
||||
int position) {
|
||||
chain->Add(Handle<ScopeInfo>(GetScopeInfo()));
|
||||
if (!is_eval_scope()) chain->Add(Handle<ScopeInfo>(GetScopeInfo()));
|
||||
|
||||
for (int i = 0; i < inner_scopes_.length(); i++) {
|
||||
Scope* scope = inner_scopes_[i];
|
||||
int beg_pos = scope->start_position();
|
||||
int end_pos = scope->end_position();
|
||||
ASSERT(beg_pos >= 0 && end_pos >= 0);
|
||||
if (beg_pos <= position && position <= end_pos) {
|
||||
if (beg_pos <= position && position < end_pos) {
|
||||
scope->GetNestedScopeChain(chain, position);
|
||||
return;
|
||||
}
|
||||
|
@ -349,10 +349,10 @@ TEST(Regress928) {
|
||||
"try { } catch (e) { var foo = function () { /* first */ } }"
|
||||
"var bar = function () { /* second */ }";
|
||||
|
||||
i::Utf8ToUC16CharacterStream stream(reinterpret_cast<const i::byte*>(program),
|
||||
static_cast<unsigned>(strlen(program)));
|
||||
i::ScriptDataImpl* data =
|
||||
i::ParserApi::PartialPreParse(&stream, NULL, false);
|
||||
v8::HandleScope handles;
|
||||
i::Handle<i::String> source(
|
||||
FACTORY->NewStringFromAscii(i::CStrVector(program)));
|
||||
i::ScriptDataImpl* data = i::ParserApi::PartialPreParse(source, NULL, false);
|
||||
CHECK(!data->HasError());
|
||||
|
||||
data->Initialize();
|
||||
|
423
test/mjsunit/debug-stepout-scope.js
Normal file
423
test/mjsunit/debug-stepout-scope.js
Normal file
@ -0,0 +1,423 @@
|
||||
// Copyright 2011 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Flags: --expose-debug-as debug --expose-natives-as=builtins
|
||||
|
||||
// Check that the ScopeIterator can properly recreate the scope at
|
||||
// every point when stepping through functions.
|
||||
|
||||
var Debug = debug.Debug;
|
||||
|
||||
function listener(event, exec_state, event_data, data) {
|
||||
if (event == Debug.DebugEvent.Break) {
|
||||
// Access scope details.
|
||||
var scope_count = exec_state.frame().scopeCount();
|
||||
for (var i = 0; i < scope_count; i++) {
|
||||
var scope = exec_state.frame().scope(i);
|
||||
// assertTrue(scope.isScope());
|
||||
scope.scopeType();
|
||||
scope.scopeObject();
|
||||
}
|
||||
|
||||
// Do steps until we reach the global scope again.
|
||||
if (true) {
|
||||
exec_state.prepareStep(Debug.StepAction.StepInMin, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.setListener(listener);
|
||||
|
||||
|
||||
function test1() {
|
||||
debugger;
|
||||
with ({x:1}) {
|
||||
x = 2;
|
||||
}
|
||||
}
|
||||
test1();
|
||||
|
||||
|
||||
function test2() {
|
||||
if (true) {
|
||||
with ({}) {
|
||||
debugger;
|
||||
}
|
||||
} else {
|
||||
with ({}) {
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
test2();
|
||||
|
||||
|
||||
function test3() {
|
||||
if (true) {
|
||||
debugger;
|
||||
} else {
|
||||
with ({}) {
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
test3();
|
||||
|
||||
|
||||
function test4() {
|
||||
debugger;
|
||||
with ({x:1}) x = 1
|
||||
}
|
||||
test4();
|
||||
|
||||
|
||||
function test5() {
|
||||
debugger;
|
||||
var dummy = 1;
|
||||
with ({}) {
|
||||
with ({}) {
|
||||
dummy = 2;
|
||||
}
|
||||
}
|
||||
dummy = 3;
|
||||
}
|
||||
test5();
|
||||
|
||||
|
||||
function test6() {
|
||||
debugger;
|
||||
try {
|
||||
throw 'stuff';
|
||||
} catch (e) {
|
||||
e = 1;
|
||||
}
|
||||
}
|
||||
test6();
|
||||
|
||||
|
||||
function test7() {
|
||||
debugger;
|
||||
function foo() {}
|
||||
}
|
||||
test7();
|
||||
|
||||
|
||||
function test8() {
|
||||
debugger;
|
||||
(function foo() {})();
|
||||
}
|
||||
test8();
|
||||
|
||||
|
||||
var q = 42;
|
||||
var prefixes = [ "debugger; ",
|
||||
"if (false) { try { throw 0; } catch(x) { return x; } }; debugger; " ];
|
||||
var bodies = [ "1",
|
||||
"1 ",
|
||||
"1;",
|
||||
"1; ",
|
||||
"q",
|
||||
"q ",
|
||||
"q;",
|
||||
"q; ",
|
||||
"try { throw 'stuff' } catch (e) { e = 1; }",
|
||||
"try { throw 'stuff' } catch (e) { e = 1; } ",
|
||||
"try { throw 'stuff' } catch (e) { e = 1; };",
|
||||
"try { throw 'stuff' } catch (e) { e = 1; }; " ];
|
||||
var with_bodies = [ "with ({}) {}",
|
||||
"with ({x:1}) x",
|
||||
"with ({x:1}) x = 1",
|
||||
"with ({x:1}) x ",
|
||||
"with ({x:1}) x = 1 ",
|
||||
"with ({x:1}) x;",
|
||||
"with ({x:1}) x = 1;",
|
||||
"with ({x:1}) x; ",
|
||||
"with ({x:1}) x = 1; " ];
|
||||
|
||||
|
||||
function test9() {
|
||||
debugger;
|
||||
for (var i = 0; i < prefixes.length; ++i) {
|
||||
var pre = prefixes[i];
|
||||
for (var j = 0; j < bodies.length; ++j) {
|
||||
var body = bodies[j];
|
||||
eval(pre + body);
|
||||
eval("'use strict'; " + pre + body);
|
||||
}
|
||||
for (var j = 0; j < with_bodies.length; ++j) {
|
||||
var body = with_bodies[j];
|
||||
eval(pre + body);
|
||||
}
|
||||
}
|
||||
}
|
||||
test9();
|
||||
|
||||
|
||||
function test10() {
|
||||
debugger;
|
||||
with ({}) {
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
test10();
|
||||
|
||||
|
||||
function test11() {
|
||||
debugger;
|
||||
try {
|
||||
throw 'stuff';
|
||||
} catch (e) {
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
test11();
|
||||
|
||||
|
||||
// Test global eval and function constructor.
|
||||
for (var i = 0; i < prefixes.length; ++i) {
|
||||
var pre = prefixes[i];
|
||||
for (var j = 0; j < bodies.length; ++j) {
|
||||
var body = bodies[j];
|
||||
eval(pre + body);
|
||||
eval("'use strict'; " + pre + body);
|
||||
Function(pre + body)();
|
||||
}
|
||||
for (var j = 0; j < with_bodies.length; ++j) {
|
||||
var body = with_bodies[j];
|
||||
eval(pre + body);
|
||||
Function(pre + body)();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
with({}) {
|
||||
debugger;
|
||||
eval("{}$%:^");
|
||||
}
|
||||
} catch(e) {
|
||||
nop();
|
||||
}
|
||||
|
||||
// Return from function constructed with Function constructor.
|
||||
var anon = 12;
|
||||
for (var i = 0; i < prefixes.length; ++i) {
|
||||
var pre = prefixes[i];
|
||||
Function(pre + "return 42")();
|
||||
Function(pre + "return 42 ")();
|
||||
Function(pre + "return 42;")();
|
||||
Function(pre + "return 42; ")();
|
||||
Function(pre + "return anon")();
|
||||
Function(pre + "return anon ")();
|
||||
Function(pre + "return anon;")();
|
||||
Function(pre + "return anon; ")();
|
||||
}
|
||||
|
||||
|
||||
function nop() {}
|
||||
|
||||
|
||||
function stress() {
|
||||
debugger;
|
||||
|
||||
L: with ({x:12}) {
|
||||
break L;
|
||||
}
|
||||
|
||||
|
||||
with ({x: 'outer'}) {
|
||||
label: {
|
||||
with ({x: 'inner'}) {
|
||||
break label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
with ({x: 'outer'}) {
|
||||
label: {
|
||||
with ({x: 'inner'}) {
|
||||
break label;
|
||||
}
|
||||
}
|
||||
nop();
|
||||
}
|
||||
|
||||
|
||||
with ({x: 'outer'}) {
|
||||
label: {
|
||||
with ({x: 'middle'}) {
|
||||
with ({x: 'inner'}) {
|
||||
break label;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
with ({x: 'outer'}) {
|
||||
label: {
|
||||
with ({x: 'middle'}) {
|
||||
with ({x: 'inner'}) {
|
||||
break label;
|
||||
}
|
||||
}
|
||||
}
|
||||
nop();
|
||||
}
|
||||
|
||||
|
||||
with ({x: 'outer'}) {
|
||||
for (var i = 0; i < 3; ++i) {
|
||||
with ({x: 'inner' + i}) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
with ({x: 'outer'}) {
|
||||
label: for (var i = 0; i < 3; ++i) {
|
||||
with ({x: 'middle' + i}) {
|
||||
for (var j = 0; j < 3; ++j) {
|
||||
with ({x: 'inner' + j}) {
|
||||
continue label;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
with ({x: 'outer'}) {
|
||||
try {
|
||||
with ({x: 'inner'}) {
|
||||
throw 0;
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
with ({x: 'outer'}) {
|
||||
try {
|
||||
with ({x: 'inner'}) {
|
||||
throw 0;
|
||||
}
|
||||
} catch (e) {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
with ({x: 'outer'}) {
|
||||
try {
|
||||
with ({x: 'middle'}) {
|
||||
with ({x: 'inner'}) {
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
with ({x: 'outer'}) {
|
||||
try {
|
||||
with ({x: 'inner'}) {
|
||||
throw 0;
|
||||
}
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
with ({x: 'outer'}) {
|
||||
try {
|
||||
with ({x: 'inner'}) {
|
||||
throw 0;
|
||||
}
|
||||
} finally {
|
||||
nop();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
|
||||
function stress1() {
|
||||
with ({x:12}) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
stress1();
|
||||
|
||||
|
||||
function stress2() {
|
||||
with ({x: 'outer'}) {
|
||||
with ({x: 'inner'}) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
stress2();
|
||||
|
||||
function stress3() {
|
||||
try {
|
||||
with ({x: 'inner'}) {
|
||||
throw 0;
|
||||
}
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
stress3();
|
||||
|
||||
|
||||
function stress4() {
|
||||
try {
|
||||
with ({x: 'inner'}) {
|
||||
throw 0;
|
||||
}
|
||||
} catch (e) {
|
||||
with ({x: 'inner'}) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
stress4();
|
||||
|
||||
}
|
||||
stress();
|
||||
|
||||
|
||||
// With block as the last(!) statement in global code.
|
||||
with ({}) { debugger; }
|
Loading…
Reference in New Issue
Block a user