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.native_context();
     77   Handle<JSObject> receiver(context->global_proxy());
     78   MaybeHandle<Object> maybe_result = Evaluate(
     79       isolate, context_builder.outer_info(),
     80       context_builder.innermost_context(), context_extension, receiver, source);
     81   if (!maybe_result.is_null() && !FLAG_debug_eval_readonly_locals) {
     82     context_builder.UpdateValues();
     83   }
     84   return maybe_result;
     85 }
     86 
     87 
     88 // Compile and evaluate source for the given context.
     89 MaybeHandle<Object> DebugEvaluate::Evaluate(
     90     Isolate* isolate, Handle<SharedFunctionInfo> outer_info,
     91     Handle<Context> context, Handle<HeapObject> context_extension,
     92     Handle<Object> receiver, Handle<String> source) {
     93   if (context_extension->IsJSObject()) {
     94     Handle<JSObject> extension = Handle<JSObject>::cast(context_extension);
     95     Handle<JSFunction> closure(context->closure(), isolate);
     96     context = isolate->factory()->NewWithContext(closure, context, extension);
     97   }
     98 
     99   Handle<JSFunction> eval_fun;
    100   ASSIGN_RETURN_ON_EXCEPTION(isolate, eval_fun,
    101                              Compiler::GetFunctionFromEval(
    102                                  source, outer_info, context, SLOPPY,
    103                                  NO_PARSE_RESTRICTION, RelocInfo::kNoPosition),
    104                              Object);
    105 
    106   Handle<Object> result;
    107   ASSIGN_RETURN_ON_EXCEPTION(
    108       isolate, result, Execution::Call(isolate, eval_fun, receiver, 0, NULL),
    109       Object);
    110 
    111   // Skip the global proxy as it has no properties and always delegates to the
    112   // real global object.
    113   if (result->IsJSGlobalProxy()) {
    114     PrototypeIterator iter(isolate, result);
    115     // TODO(verwaest): This will crash when the global proxy is detached.
    116     result = PrototypeIterator::GetCurrent<JSObject>(iter);
    117   }
    118 
    119   return result;
    120 }
    121 
    122 
    123 DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
    124                                               JavaScriptFrame* frame,
    125                                               int inlined_jsframe_index)
    126     : isolate_(isolate),
    127       frame_(frame),
    128       inlined_jsframe_index_(inlined_jsframe_index) {
    129   FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
    130   Handle<JSFunction> local_function =
    131       handle(JSFunction::cast(frame_inspector.GetFunction()));
    132   Handle<Context> outer_context(local_function->context());
    133   native_context_ = Handle<Context>(outer_context->native_context());
    134   Handle<JSFunction> global_function(native_context_->closure());
    135   outer_info_ = handle(global_function->shared());
    136   Handle<Context> inner_context;
    137 
    138   bool stop = false;
    139 
    140   // Iterate the original context chain to create a context chain that reflects
    141   // our needs. The original context chain may look like this:
    142   // <native context> <outer contexts> <function context> <inner contexts>
    143   // In the resulting context chain, we want to materialize the receiver,
    144   // the parameters of the current function, the stack locals. We only
    145   // materialize context variables that the function already references,
    146   // because only for those variables we can be sure that they will be resolved
    147   // correctly. Variables that are not referenced by the function may be
    148   // context-allocated and thus accessible, but may be shadowed by stack-
    149   // allocated variables and the resolution would be incorrect.
    150   // The result will look like this:
    151   // <native context> <receiver context>
    152   //     <materialized stack and accessible context vars> <inner contexts>
    153   // All contexts use the closure of the native context, since there is no
    154   // function context in the chain. Variables that cannot be resolved are
    155   // bound to toplevel (script contexts or global object).
    156   // Once debug-evaluate has been executed, the changes to the materialized
    157   // objects are written back to the original context chain. Any changes to
    158   // the original context chain will therefore be overwritten.
    159   const ScopeIterator::Option option = ScopeIterator::COLLECT_NON_LOCALS;
    160   for (ScopeIterator it(isolate, &frame_inspector, option);
    161        !it.Failed() && !it.Done() && !stop; it.Next()) {
    162     ScopeIterator::ScopeType scope_type = it.Type();
    163     if (scope_type == ScopeIterator::ScopeTypeLocal) {
    164       DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type());
    165       it.GetNonLocals(&non_locals_);
    166       Handle<Context> local_context =
    167           it.HasContext() ? it.CurrentContext() : outer_context;
    168 
    169       // The "this" binding, if any, can't be bound via "with".  If we need
    170       // to, add another node onto the outer context to bind "this".
    171       Handle<Context> receiver_context =
    172           MaterializeReceiver(native_context_, local_context, local_function,
    173                               global_function, it.ThisIsNonLocal());
    174 
    175       Handle<JSObject> materialized_function = NewJSObjectWithNullProto();
    176       frame_inspector.MaterializeStackLocals(materialized_function,
    177                                              local_function);
    178       MaterializeArgumentsObject(materialized_function, local_function);
    179       MaterializeContextChain(materialized_function, local_context);
    180 
    181       Handle<Context> with_context = isolate->factory()->NewWithContext(
    182           global_function, receiver_context, materialized_function);
    183 
    184       ContextChainElement context_chain_element;
    185       context_chain_element.original_context = local_context;
    186       context_chain_element.materialized_object = materialized_function;
    187       context_chain_element.scope_info = it.CurrentScopeInfo();
    188       context_chain_.Add(context_chain_element);
    189 
    190       stop = true;
    191       RecordContextsInChain(&inner_context, receiver_context, with_context);
    192     } else if (scope_type == ScopeIterator::ScopeTypeCatch ||
    193                scope_type == ScopeIterator::ScopeTypeWith) {
    194       Handle<Context> cloned_context = Handle<Context>::cast(
    195           isolate->factory()->CopyFixedArray(it.CurrentContext()));
    196 
    197       ContextChainElement context_chain_element;
    198       context_chain_element.original_context = it.CurrentContext();
    199       context_chain_element.cloned_context = cloned_context;
    200       context_chain_.Add(context_chain_element);
    201 
    202       RecordContextsInChain(&inner_context, cloned_context, cloned_context);
    203     } else if (scope_type == ScopeIterator::ScopeTypeBlock) {
    204       Handle<JSObject> materialized_object = NewJSObjectWithNullProto();
    205       frame_inspector.MaterializeStackLocals(materialized_object,
    206                                              it.CurrentScopeInfo());
    207       if (it.HasContext()) {
    208         Handle<Context> cloned_context = Handle<Context>::cast(
    209             isolate->factory()->CopyFixedArray(it.CurrentContext()));
    210         Handle<Context> with_context = isolate->factory()->NewWithContext(
    211             global_function, cloned_context, materialized_object);
    212 
    213         ContextChainElement context_chain_element;
    214         context_chain_element.original_context = it.CurrentContext();
    215         context_chain_element.cloned_context = cloned_context;
    216         context_chain_element.materialized_object = materialized_object;
    217         context_chain_element.scope_info = it.CurrentScopeInfo();
    218         context_chain_.Add(context_chain_element);
    219 
    220         RecordContextsInChain(&inner_context, cloned_context, with_context);
    221       } else {
    222         Handle<Context> with_context = isolate->factory()->NewWithContext(
    223             global_function, outer_context, materialized_object);
    224 
    225         ContextChainElement context_chain_element;
    226         context_chain_element.materialized_object = materialized_object;
    227         context_chain_element.scope_info = it.CurrentScopeInfo();
    228         context_chain_.Add(context_chain_element);
    229 
    230         RecordContextsInChain(&inner_context, with_context, with_context);
    231       }
    232     } else {
    233       stop = true;
    234     }
    235   }
    236   if (innermost_context_.is_null()) {
    237     innermost_context_ = outer_context;
    238   }
    239   DCHECK(!innermost_context_.is_null());
    240 }
    241 
    242 
    243 void DebugEvaluate::ContextBuilder::UpdateValues() {
    244   // TODO(yangguo): remove updating values.
    245   for (int i = 0; i < context_chain_.length(); i++) {
    246     ContextChainElement element = context_chain_[i];
    247     if (!element.original_context.is_null() &&
    248         !element.cloned_context.is_null()) {
    249       Handle<Context> cloned_context = element.cloned_context;
    250       cloned_context->CopyTo(
    251           Context::MIN_CONTEXT_SLOTS, *element.original_context,
    252           Context::MIN_CONTEXT_SLOTS,
    253           cloned_context->length() - Context::MIN_CONTEXT_SLOTS);
    254     }
    255     if (!element.materialized_object.is_null()) {
    256       // Write back potential changes to materialized stack locals to the
    257       // stack.
    258       FrameInspector(frame_, inlined_jsframe_index_, isolate_)
    259           .UpdateStackLocalsFromMaterializedObject(element.materialized_object,
    260                                                    element.scope_info);
    261       if (element.scope_info->scope_type() == FUNCTION_SCOPE) {
    262         DCHECK_EQ(context_chain_.length() - 1, i);
    263         UpdateContextChainFromMaterializedObject(element.materialized_object,
    264                                                  element.original_context);
    265       }
    266     }
    267   }
    268 }
    269 
    270 
    271 Handle<JSObject> DebugEvaluate::ContextBuilder::NewJSObjectWithNullProto() {
    272   Handle<JSObject> result =
    273       isolate_->factory()->NewJSObject(isolate_->object_function());
    274   Handle<Map> new_map =
    275       Map::Copy(Handle<Map>(result->map()), "ObjectWithNullProto");
    276   Map::SetPrototype(new_map, isolate_->factory()->null_value());
    277   JSObject::MigrateToMap(result, new_map);
    278   return result;
    279 }
    280 
    281 
    282 void DebugEvaluate::ContextBuilder::RecordContextsInChain(
    283     Handle<Context>* inner_context, Handle<Context> first,
    284     Handle<Context> last) {
    285   if (!inner_context->is_null()) {
    286     (*inner_context)->set_previous(*last);
    287   } else {
    288     innermost_context_ = last;
    289   }
    290   *inner_context = first;
    291 }
    292 
    293 
    294 void DebugEvaluate::ContextBuilder::MaterializeArgumentsObject(
    295     Handle<JSObject> target, Handle<JSFunction> function) {
    296   // Do not materialize the arguments object for eval or top-level code.
    297   // Skip if "arguments" is already taken.
    298   if (!function->shared()->is_function()) return;
    299   Maybe<bool> maybe = JSReceiver::HasOwnProperty(
    300       target, isolate_->factory()->arguments_string());
    301   DCHECK(maybe.IsJust());
    302   if (maybe.FromJust()) return;
    303 
    304   // FunctionGetArguments can't throw an exception.
    305   Handle<JSObject> arguments =
    306       Handle<JSObject>::cast(Accessors::FunctionGetArguments(function));
    307   Handle<String> arguments_str = isolate_->factory()->arguments_string();
    308   JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments,
    309                                            NONE)
    310       .Check();
    311 }
    312 
    313 
    314 MaybeHandle<Object> DebugEvaluate::ContextBuilder::LoadFromContext(
    315     Handle<Context> context, Handle<String> name, bool* global) {
    316   static const ContextLookupFlags flags = FOLLOW_CONTEXT_CHAIN;
    317   int index;
    318   PropertyAttributes attributes;
    319   BindingFlags binding;
    320   Handle<Object> holder =
    321       context->Lookup(name, flags, &index, &attributes, &binding);
    322   if (holder.is_null()) return MaybeHandle<Object>();
    323   Handle<Object> value;
    324   if (index != Context::kNotFound) {  // Found on context.
    325     Handle<Context> context = Handle<Context>::cast(holder);
    326     // Do not shadow variables on the script context.
    327     *global = context->IsScriptContext();
    328     return Handle<Object>(context->get(index), isolate_);
    329   } else {  // Found on object.
    330     Handle<JSReceiver> object = Handle<JSReceiver>::cast(holder);
    331     // Do not shadow properties on the global object.
    332     *global = object->IsJSGlobalObject();
    333     return JSReceiver::GetDataProperty(object, name);
    334   }
    335 }
    336 
    337 
    338 void DebugEvaluate::ContextBuilder::MaterializeContextChain(
    339     Handle<JSObject> target, Handle<Context> context) {
    340   for (const Handle<String>& name : non_locals_) {
    341     HandleScope scope(isolate_);
    342     Handle<Object> value;
    343     bool global;
    344     if (!LoadFromContext(context, name, &global).ToHandle(&value) || global) {
    345       // If resolving the variable fails, skip it. If it resolves to a global
    346       // variable, skip it as well since it's not read-only and can be resolved
    347       // within debug-evaluate.
    348       continue;
    349     }
    350     JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check();
    351   }
    352 }
    353 
    354 
    355 void DebugEvaluate::ContextBuilder::StoreToContext(Handle<Context> context,
    356                                                    Handle<String> name,
    357                                                    Handle<Object> value) {
    358   static const ContextLookupFlags flags = FOLLOW_CONTEXT_CHAIN;
    359   int index;
    360   PropertyAttributes attributes;
    361   BindingFlags binding;
    362   Handle<Object> holder =
    363       context->Lookup(name, flags, &index, &attributes, &binding);
    364   if (holder.is_null()) return;
    365   if (attributes & READ_ONLY) return;
    366   if (index != Context::kNotFound) {  // Found on context.
    367     Handle<Context> context = Handle<Context>::cast(holder);
    368     context->set(index, *value);
    369   } else {  // Found on object.
    370     Handle<JSReceiver> object = Handle<JSReceiver>::cast(holder);
    371     LookupIterator lookup(object, name);
    372     if (lookup.state() != LookupIterator::DATA) return;
    373     CHECK(JSReceiver::SetDataProperty(&lookup, value).FromJust());
    374   }
    375 }
    376 
    377 
    378 void DebugEvaluate::ContextBuilder::UpdateContextChainFromMaterializedObject(
    379     Handle<JSObject> source, Handle<Context> context) {
    380   // TODO(yangguo): check whether overwriting context fields is actually safe
    381   //                wrt fields we consider constant.
    382   for (const Handle<String>& name : non_locals_) {
    383     HandleScope scope(isolate_);
    384     Handle<Object> value = JSReceiver::GetDataProperty(source, name);
    385     StoreToContext(context, name, value);
    386   }
    387 }
    388 
    389 
    390 Handle<Context> DebugEvaluate::ContextBuilder::MaterializeReceiver(
    391     Handle<Context> parent_context, Handle<Context> lookup_context,
    392     Handle<JSFunction> local_function, Handle<JSFunction> global_function,
    393     bool this_is_non_local) {
    394   Handle<Object> receiver = isolate_->factory()->undefined_value();
    395   Handle<String> this_string = isolate_->factory()->this_string();
    396   if (this_is_non_local) {
    397     bool global;
    398     LoadFromContext(lookup_context, this_string, &global).ToHandle(&receiver);
    399   } else if (local_function->shared()->scope_info()->HasReceiver()) {
    400     receiver = handle(frame_->receiver(), isolate_);
    401   }
    402   return isolate_->factory()->NewCatchContext(global_function, parent_context,
    403                                               this_string, receiver);
    404 }
    405 
    406 }  // namespace internal
    407 }  // namespace v8
    408