blob: 62b7a2b4269bf79104633d0e0a552c8ac4f98bc7 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// 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
15namespace v8 {
16namespace internal {
17
18static inline bool IsDebugContext(Isolate* isolate, Context* context) {
19 return context->native_context() == *isolate->debug()->debug_context();
20}
21
22
23MaybeHandle<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
47MaybeHandle<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
Ben Murdochda12d292016-06-02 14:46:10 +010076 Handle<Context> context = context_builder.evaluation_context();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000077 Handle<JSObject> receiver(context->global_proxy());
Ben Murdochda12d292016-06-02 14:46:10 +010078 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();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000082 return maybe_result;
83}
84
85
86// Compile and evaluate source for the given context.
87MaybeHandle<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;
Ben Murdochc5610432016-08-08 18:44:38 +010098 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);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000104
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()) {
Ben Murdoch097c5b22016-05-18 11:27:45 +0100113 PrototypeIterator iter(isolate, Handle<JSGlobalProxy>::cast(result));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000114 // 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
122DebugEvaluate::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 =
Ben Murdoch097c5b22016-05-18 11:27:45 +0100130 Handle<JSFunction>::cast(frame_inspector.GetFunction());
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000131 Handle<Context> outer_context(local_function->context());
Ben Murdochda12d292016-06-02 14:46:10 +0100132 evaluation_context_ = outer_context;
133 outer_info_ = handle(local_function->shared());
134 Factory* factory = isolate->factory();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000135
Ben Murdochda12d292016-06-02 14:46:10 +0100136 // 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.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000152 const ScopeIterator::Option option = ScopeIterator::COLLECT_NON_LOCALS;
153 for (ScopeIterator it(isolate, &frame_inspector, option);
Ben Murdochda12d292016-06-02 14:46:10 +0100154 !it.Failed() && !it.Done(); it.Next()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000155 ScopeIterator::ScopeType scope_type = it.Type();
156 if (scope_type == ScopeIterator::ScopeTypeLocal) {
157 DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type());
Ben Murdochda12d292016-06-02 14:46:10 +0100158 Handle<JSObject> materialized = factory->NewJSObjectWithNullProto();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000159 Handle<Context> local_context =
160 it.HasContext() ? it.CurrentContext() : outer_context;
Ben Murdochda12d292016-06-02 14:46:10 +0100161 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);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000166 ContextChainElement context_chain_element;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000167 context_chain_element.scope_info = it.CurrentScopeInfo();
Ben Murdochda12d292016-06-02 14:46:10 +0100168 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 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000175 context_chain_.Add(context_chain_element);
Ben Murdochda12d292016-06-02 14:46:10 +0100176 evaluation_context_ = outer_context;
177 break;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000178 } else if (scope_type == ScopeIterator::ScopeTypeCatch ||
179 scope_type == ScopeIterator::ScopeTypeWith) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000180 ContextChainElement context_chain_element;
Ben Murdochda12d292016-06-02 14:46:10 +0100181 Handle<Context> current_context = it.CurrentContext();
182 if (!current_context->IsDebugEvaluateContext()) {
183 context_chain_element.wrapped_context = current_context;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000184 }
Ben Murdochda12d292016-06-02 14:46:10 +0100185 context_chain_.Add(context_chain_element);
Ben Murdochc5610432016-08-08 18:44:38 +0100186 } else if (scope_type == ScopeIterator::ScopeTypeBlock ||
187 scope_type == ScopeIterator::ScopeTypeEval) {
Ben Murdochda12d292016-06-02 14:46:10 +0100188 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);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000198 } else {
Ben Murdochda12d292016-06-02 14:46:10 +0100199 break;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000200 }
201 }
Ben Murdochda12d292016-06-02 14:46:10 +0100202
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);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000207 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000208}
209
210
211void 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];
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000215 if (!element.materialized_object.is_null()) {
Ben Murdochda12d292016-06-02 14:46:10 +0100216 // Write back potential changes to materialized stack locals to the stack.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000217 FrameInspector(frame_, inlined_jsframe_index_, isolate_)
218 .UpdateStackLocalsFromMaterializedObject(element.materialized_object,
219 element.scope_info);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000220 }
221 }
222}
223
224
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000225void 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.
Ben Murdoch097c5b22016-05-18 11:27:45 +0100236 Handle<JSObject> arguments = Accessors::FunctionGetArguments(function);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000237 Handle<String> arguments_str = isolate_->factory()->arguments_string();
238 JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments,
239 NONE)
240 .Check();
241}
242
Ben Murdochda12d292016-06-02 14:46:10 +0100243void 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;
Ben Murdochc5610432016-08-08 18:44:38 +0100252 } else if (local_function->shared()->scope_info()->HasReceiver() &&
Ben Murdoch61f157c2016-09-16 13:49:30 +0100253 !frame_->receiver()->IsTheHole(isolate_)) {
Ben Murdochda12d292016-06-02 14:46:10 +0100254 recv = handle(frame_->receiver(), isolate_);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000255 }
Ben Murdochda12d292016-06-02 14:46:10 +0100256 JSObject::SetOwnPropertyIgnoreAttributes(target, name, recv, NONE).Check();
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000257}
258
259} // namespace internal
260} // namespace v8