Home | History | Annotate | Download | only in debug
      1 // Copyright 2015 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/debug/debug-evaluate.h"
      6 
      7 #include "src/accessors.h"
      8 #include "src/contexts.h"
      9 #include "src/debug/debug.h"
     10 #include "src/debug/debug-frames.h"
     11 #include "src/debug/debug-scopes.h"
     12 #include "src/frames-inl.h"
     13 #include "src/isolate-inl.h"
     14 
     15 namespace v8 {
     16 namespace internal {
     17 
     18 static inline bool IsDebugContext(Isolate* isolate, Context* context) {
     19   return context->native_context() == *isolate->debug()->debug_context();
     20 }
     21 
     22 
     23 MaybeHandle<Object> DebugEvaluate::Global(
     24     Isolate* isolate, Handle<String> source, bool disable_break,
     25     Handle<HeapObject> context_extension) {
     26   // Handle the processing of break.
     27   DisableBreak disable_break_scope(isolate->debug(), disable_break);
     28 
     29   // Enter the top context from before the debugger was invoked.
     30   SaveContext save(isolate);
     31   SaveContext* top = &save;
     32   while (top != NULL && IsDebugContext(isolate, *top->context())) {
     33     top = top->prev();
     34   }
     35   if (top != NULL) isolate->set_context(*top->context());
     36 
     37   // Get the native context now set to the top context from before the
     38   // debugger was invoked.
     39   Handle<Context> context = isolate->native_context();
     40   Handle<JSObject> receiver(context->global_proxy());
     41   Handle<SharedFunctionInfo> outer_info(context->closure()->shared(), isolate);
     42   return Evaluate(isolate, outer_info, context, context_extension, receiver,
     43                   source);
     44 }
     45 
     46 
     47 MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate,
     48                                          StackFrame::Id frame_id,
     49                                          int inlined_jsframe_index,
     50                                          Handle<String> source,
     51                                          bool disable_break,
     52                                          Handle<HeapObject> context_extension) {
     53   // Handle the processing of break.
     54   DisableBreak disable_break_scope(isolate->debug(), disable_break);
     55 
     56   // Get the frame where the debugging is performed.
     57   JavaScriptFrameIterator it(isolate, frame_id);
     58   JavaScriptFrame* frame = it.frame();
     59 
     60   // Traverse the saved contexts chain to find the active context for the
     61   // selected frame.
     62   SaveContext* save =
     63       DebugFrameHelper::FindSavedContextForFrame(isolate, frame);
     64   SaveContext savex(isolate);
     65   isolate->set_context(*(save->context()));
     66 
     67   // This is not a lot different than DebugEvaluate::Global, except that
     68   // variables accessible by the function we are evaluating from are
     69   // materialized and included on top of the native context. Changes to
     70   // the materialized object are written back afterwards.
     71   // Note that the native context is taken from the original context chain,
     72   // which may not be the current native context of the isolate.
     73   ContextBuilder context_builder(isolate, frame, inlined_jsframe_index);
     74   if (isolate->has_pending_exception()) return MaybeHandle<Object>();
     75 
     76   Handle<Context> context = context_builder.evaluation_context();
     77   Handle<JSObject> receiver(context->global_proxy());
     78   MaybeHandle<Object> maybe_result =
     79       Evaluate(isolate, context_builder.outer_info(), context,
     80                context_extension, receiver, source);
     81   if (!maybe_result.is_null()) context_builder.UpdateValues();
     82   return maybe_result;
     83 }
     84 
     85 
     86 // Compile and evaluate source for the given context.
     87 MaybeHandle<Object> DebugEvaluate::Evaluate(
     88     Isolate* isolate, Handle<SharedFunctionInfo> outer_info,
     89     Handle<Context> context, Handle<HeapObject> context_extension,
     90     Handle<Object> receiver, Handle<String> source) {
     91   if (context_extension->IsJSObject()) {
     92     Handle<JSObject> extension = Handle<JSObject>::cast(context_extension);
     93     Handle<JSFunction> closure(context->closure(), isolate);
     94     context = isolate->factory()->NewWithContext(closure, context, extension);
     95   }
     96 
     97   Handle<JSFunction> eval_fun;
     98   ASSIGN_RETURN_ON_EXCEPTION(
     99       isolate, eval_fun,
    100       Compiler::GetFunctionFromEval(
    101           source, outer_info, context, SLOPPY, NO_PARSE_RESTRICTION,
    102           RelocInfo::kNoPosition, RelocInfo::kNoPosition),
    103       Object);
    104 
    105   Handle<Object> result;
    106   ASSIGN_RETURN_ON_EXCEPTION(
    107       isolate, result, Execution::Call(isolate, eval_fun, receiver, 0, NULL),
    108       Object);
    109 
    110   // Skip the global proxy as it has no properties and always delegates to the
    111   // real global object.
    112   if (result->IsJSGlobalProxy()) {
    113     PrototypeIterator iter(isolate, Handle<JSGlobalProxy>::cast(result));
    114     // TODO(verwaest): This will crash when the global proxy is detached.
    115     result = PrototypeIterator::GetCurrent<JSObject>(iter);
    116   }
    117 
    118   return result;
    119 }
    120 
    121 
    122 DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
    123                                               JavaScriptFrame* frame,
    124                                               int inlined_jsframe_index)
    125     : isolate_(isolate),
    126       frame_(frame),
    127       inlined_jsframe_index_(inlined_jsframe_index) {
    128   FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
    129   Handle<JSFunction> local_function =
    130       Handle<JSFunction>::cast(frame_inspector.GetFunction());
    131   Handle<Context> outer_context(local_function->context());
    132   evaluation_context_ = outer_context;
    133   outer_info_ = handle(local_function->shared());
    134   Factory* factory = isolate->factory();
    135 
    136   // To evaluate as if we were running eval at the point of the debug break,
    137   // we reconstruct the context chain as follows:
    138   //  - To make stack-allocated variables visible, we materialize them and
    139   //    use a debug-evaluate context to wrap both the materialized object and
    140   //    the original context.
    141   //  - We use the original context chain from the function context to the
    142   //    native context.
    143   //  - Between the function scope and the native context, we only resolve
    144   //    variable names that the current function already uses. Only for these
    145   //    names we can be sure that they will be correctly resolved. For the
    146   //    rest, we only resolve to with, script, and native contexts. We use a
    147   //    whitelist to implement that.
    148   // Context::Lookup has special handling for debug-evaluate contexts:
    149   //  - Look up in the materialized stack variables.
    150   //  - Look up in the original context.
    151   //  - Check the whitelist to find out whether to skip contexts during lookup.
    152   const ScopeIterator::Option option = ScopeIterator::COLLECT_NON_LOCALS;
    153   for (ScopeIterator it(isolate, &frame_inspector, option);
    154        !it.Failed() && !it.Done(); it.Next()) {
    155     ScopeIterator::ScopeType scope_type = it.Type();
    156     if (scope_type == ScopeIterator::ScopeTypeLocal) {
    157       DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type());
    158       Handle<JSObject> materialized = factory->NewJSObjectWithNullProto();
    159       Handle<Context> local_context =
    160           it.HasContext() ? it.CurrentContext() : outer_context;
    161       Handle<StringSet> non_locals = it.GetNonLocals();
    162       MaterializeReceiver(materialized, local_context, local_function,
    163                           non_locals);
    164       frame_inspector.MaterializeStackLocals(materialized, local_function);
    165       MaterializeArgumentsObject(materialized, local_function);
    166       ContextChainElement context_chain_element;
    167       context_chain_element.scope_info = it.CurrentScopeInfo();
    168       context_chain_element.materialized_object = materialized;
    169       // Non-locals that are already being referenced by the current function
    170       // are guaranteed to be correctly resolved.
    171       context_chain_element.whitelist = non_locals;
    172       if (it.HasContext()) {
    173         context_chain_element.wrapped_context = it.CurrentContext();
    174       }
    175       context_chain_.Add(context_chain_element);
    176       evaluation_context_ = outer_context;
    177       break;
    178     } else if (scope_type == ScopeIterator::ScopeTypeCatch ||
    179                scope_type == ScopeIterator::ScopeTypeWith) {
    180       ContextChainElement context_chain_element;
    181       Handle<Context> current_context = it.CurrentContext();
    182       if (!current_context->IsDebugEvaluateContext()) {
    183         context_chain_element.wrapped_context = current_context;
    184       }
    185       context_chain_.Add(context_chain_element);
    186     } else if (scope_type == ScopeIterator::ScopeTypeBlock ||
    187                scope_type == ScopeIterator::ScopeTypeEval) {
    188       Handle<JSObject> materialized = factory->NewJSObjectWithNullProto();
    189       frame_inspector.MaterializeStackLocals(materialized,
    190                                              it.CurrentScopeInfo());
    191       ContextChainElement context_chain_element;
    192       context_chain_element.scope_info = it.CurrentScopeInfo();
    193       context_chain_element.materialized_object = materialized;
    194       if (it.HasContext()) {
    195         context_chain_element.wrapped_context = it.CurrentContext();
    196       }
    197       context_chain_.Add(context_chain_element);
    198     } else {
    199       break;
    200     }
    201   }
    202 
    203   for (int i = context_chain_.length() - 1; i >= 0; i--) {
    204     evaluation_context_ = factory->NewDebugEvaluateContext(
    205         evaluation_context_, context_chain_[i].materialized_object,
    206         context_chain_[i].wrapped_context, context_chain_[i].whitelist);
    207   }
    208 }
    209 
    210 
    211 void DebugEvaluate::ContextBuilder::UpdateValues() {
    212   // TODO(yangguo): remove updating values.
    213   for (int i = 0; i < context_chain_.length(); i++) {
    214     ContextChainElement element = context_chain_[i];
    215     if (!element.materialized_object.is_null()) {
    216       // Write back potential changes to materialized stack locals to the stack.
    217       FrameInspector(frame_, inlined_jsframe_index_, isolate_)
    218           .UpdateStackLocalsFromMaterializedObject(element.materialized_object,
    219                                                    element.scope_info);
    220     }
    221   }
    222 }
    223 
    224 
    225 void DebugEvaluate::ContextBuilder::MaterializeArgumentsObject(
    226     Handle<JSObject> target, Handle<JSFunction> function) {
    227   // Do not materialize the arguments object for eval or top-level code.
    228   // Skip if "arguments" is already taken.
    229   if (!function->shared()->is_function()) return;
    230   Maybe<bool> maybe = JSReceiver::HasOwnProperty(
    231       target, isolate_->factory()->arguments_string());
    232   DCHECK(maybe.IsJust());
    233   if (maybe.FromJust()) return;
    234 
    235   // FunctionGetArguments can't throw an exception.
    236   Handle<JSObject> arguments = Accessors::FunctionGetArguments(function);
    237   Handle<String> arguments_str = isolate_->factory()->arguments_string();
    238   JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments,
    239                                            NONE)
    240       .Check();
    241 }
    242 
    243 void DebugEvaluate::ContextBuilder::MaterializeReceiver(
    244     Handle<JSObject> target, Handle<Context> local_context,
    245     Handle<JSFunction> local_function, Handle<StringSet> non_locals) {
    246   Handle<Object> recv = isolate_->factory()->undefined_value();
    247   Handle<String> name = isolate_->factory()->this_string();
    248   if (non_locals->Has(name)) {
    249     // 'this' is allocated in an outer context and is is already being
    250     // referenced by the current function, so it can be correctly resolved.
    251     return;
    252   } else if (local_function->shared()->scope_info()->HasReceiver() &&
    253              !frame_->receiver()->IsTheHole(isolate_)) {
    254     recv = handle(frame_->receiver(), isolate_);
    255   }
    256   JSObject::SetOwnPropertyIgnoreAttributes(target, name, recv, NONE).Check();
    257 }
    258 
    259 }  // namespace internal
    260 }  // namespace v8
    261