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