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 
IsDebugContext(Isolate * isolate,Context * context)18 static inline bool IsDebugContext(Isolate* isolate, Context* context) {
19   return context->native_context() == *isolate->debug()->debug_context();
20 }
21 
22 
Global(Isolate * isolate,Handle<String> source,bool disable_break,Handle<HeapObject> context_extension)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 
Local(Isolate * isolate,StackFrame::Id frame_id,int inlined_jsframe_index,Handle<String> source,bool disable_break,Handle<HeapObject> context_extension)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.
Evaluate(Isolate * isolate,Handle<SharedFunctionInfo> outer_info,Handle<Context> context,Handle<HeapObject> context_extension,Handle<Object> receiver,Handle<String> source)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 
ContextBuilder(Isolate * isolate,JavaScriptFrame * frame,int inlined_jsframe_index)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 
UpdateValues()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 
NewJSObjectWithNullProto()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 
RecordContextsInChain(Handle<Context> * inner_context,Handle<Context> first,Handle<Context> last)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 
MaterializeArgumentsObject(Handle<JSObject> target,Handle<JSFunction> function)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 
LoadFromContext(Handle<Context> context,Handle<String> name,bool * global)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 
MaterializeContextChain(Handle<JSObject> target,Handle<Context> context)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 
StoreToContext(Handle<Context> context,Handle<String> name,Handle<Object> value)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 
UpdateContextChainFromMaterializedObject(Handle<JSObject> source,Handle<Context> context)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 
MaterializeReceiver(Handle<Context> parent_context,Handle<Context> lookup_context,Handle<JSFunction> local_function,Handle<JSFunction> global_function,bool this_is_non_local)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