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