Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(164)

Side by Side Diff: src/debug/debug-evaluate.cc

Issue 1834633003: [debugger] allow debug-evaluate to change stack and context values. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: address comments Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/debug/debug-evaluate.h ('k') | src/debug/debug-scopes.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 the V8 project authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/debug/debug-evaluate.h" 5 #include "src/debug/debug-evaluate.h"
6 6
7 #include "src/accessors.h" 7 #include "src/accessors.h"
8 #include "src/contexts.h" 8 #include "src/contexts.h"
9 #include "src/debug/debug.h" 9 #include "src/debug/debug.h"
10 #include "src/debug/debug-frames.h" 10 #include "src/debug/debug-frames.h"
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
66 66
67 // This is not a lot different than DebugEvaluate::Global, except that 67 // This is not a lot different than DebugEvaluate::Global, except that
68 // variables accessible by the function we are evaluating from are 68 // variables accessible by the function we are evaluating from are
69 // materialized and included on top of the native context. Changes to 69 // materialized and included on top of the native context. Changes to
70 // the materialized object are written back afterwards. 70 // the materialized object are written back afterwards.
71 // Note that the native context is taken from the original context chain, 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. 72 // which may not be the current native context of the isolate.
73 ContextBuilder context_builder(isolate, frame, inlined_jsframe_index); 73 ContextBuilder context_builder(isolate, frame, inlined_jsframe_index);
74 if (isolate->has_pending_exception()) return MaybeHandle<Object>(); 74 if (isolate->has_pending_exception()) return MaybeHandle<Object>();
75 75
76 Handle<Context> context = context_builder.native_context(); 76 Handle<Context> context = context_builder.evaluation_context();
77 Handle<JSObject> receiver(context->global_proxy()); 77 Handle<JSObject> receiver(context->global_proxy());
78 MaybeHandle<Object> maybe_result = Evaluate( 78 MaybeHandle<Object> maybe_result =
79 isolate, context_builder.outer_info(), 79 Evaluate(isolate, context_builder.outer_info(), context,
80 context_builder.innermost_context(), context_extension, receiver, source); 80 context_extension, receiver, source);
81 if (!maybe_result.is_null() && !FLAG_debug_eval_readonly_locals) { 81 if (!maybe_result.is_null()) context_builder.UpdateValues();
82 context_builder.UpdateValues();
83 }
84 return maybe_result; 82 return maybe_result;
85 } 83 }
86 84
87 85
88 // Compile and evaluate source for the given context. 86 // Compile and evaluate source for the given context.
89 MaybeHandle<Object> DebugEvaluate::Evaluate( 87 MaybeHandle<Object> DebugEvaluate::Evaluate(
90 Isolate* isolate, Handle<SharedFunctionInfo> outer_info, 88 Isolate* isolate, Handle<SharedFunctionInfo> outer_info,
91 Handle<Context> context, Handle<HeapObject> context_extension, 89 Handle<Context> context, Handle<HeapObject> context_extension,
92 Handle<Object> receiver, Handle<String> source) { 90 Handle<Object> receiver, Handle<String> source) {
93 if (context_extension->IsJSObject()) { 91 if (context_extension->IsJSObject()) {
(...skipping 29 matching lines...) Expand all
123 DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate, 121 DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
124 JavaScriptFrame* frame, 122 JavaScriptFrame* frame,
125 int inlined_jsframe_index) 123 int inlined_jsframe_index)
126 : isolate_(isolate), 124 : isolate_(isolate),
127 frame_(frame), 125 frame_(frame),
128 inlined_jsframe_index_(inlined_jsframe_index) { 126 inlined_jsframe_index_(inlined_jsframe_index) {
129 FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate); 127 FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
130 Handle<JSFunction> local_function = 128 Handle<JSFunction> local_function =
131 Handle<JSFunction>::cast(frame_inspector.GetFunction()); 129 Handle<JSFunction>::cast(frame_inspector.GetFunction());
132 Handle<Context> outer_context(local_function->context()); 130 Handle<Context> outer_context(local_function->context());
133 native_context_ = Handle<Context>(outer_context->native_context()); 131 evaluation_context_ = outer_context;
134 Handle<JSFunction> global_function(native_context_->closure()); 132 outer_info_ = handle(local_function->shared());
135 outer_info_ = handle(global_function->shared()); 133 Factory* factory = isolate->factory();
136 Handle<Context> inner_context;
137 134
138 bool stop = false; 135 // To evaluate as if we were running eval at the point of the debug break,
139 136 // we reconstruct the context chain as follows:
140 // Iterate the original context chain to create a context chain that reflects 137 // - To make stack-allocated variables visible, we materialize them and
141 // our needs. The original context chain may look like this: 138 // use a debug-evaluate context to wrap both the materialized object and
142 // <native context> <outer contexts> <function context> <inner contexts> 139 // the original context.
143 // In the resulting context chain, we want to materialize the receiver, 140 // - We use the original context chain from the function context to the
144 // the parameters of the current function, the stack locals. We only 141 // native context.
145 // materialize context variables that the function already references, 142 // - Between the function scope and the native context, we only resolve
146 // because only for those variables we can be sure that they will be resolved 143 // variable names that the current function already uses. Only for these
147 // correctly. Variables that are not referenced by the function may be 144 // names we can be sure that they will be correctly resolved. For the
148 // context-allocated and thus accessible, but may be shadowed by stack- 145 // rest, we only resolve to with, script, and native contexts. We use a
149 // allocated variables and the resolution would be incorrect. 146 // whitelist to implement that.
150 // The result will look like this: 147 // Context::Lookup has special handling for debug-evaluate contexts:
151 // <native context> <receiver context> 148 // - Look up in the materialized stack variables.
152 // <materialized stack and accessible context vars> <inner contexts> 149 // - Look up in the original context.
153 // All contexts use the closure of the native context, since there is no 150 // - Check the whitelist to find out whether to skip contexts during lookup.
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; 151 const ScopeIterator::Option option = ScopeIterator::COLLECT_NON_LOCALS;
160 for (ScopeIterator it(isolate, &frame_inspector, option); 152 for (ScopeIterator it(isolate, &frame_inspector, option);
161 !it.Failed() && !it.Done() && !stop; it.Next()) { 153 !it.Failed() && !it.Done(); it.Next()) {
162 ScopeIterator::ScopeType scope_type = it.Type(); 154 ScopeIterator::ScopeType scope_type = it.Type();
163 if (scope_type == ScopeIterator::ScopeTypeLocal) { 155 if (scope_type == ScopeIterator::ScopeTypeLocal) {
164 DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type()); 156 DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type());
165 it.GetNonLocals(&non_locals_); 157 Handle<JSObject> materialized = NewJSObjectWithNullProto();
166 Handle<Context> local_context = 158 Handle<Context> local_context =
167 it.HasContext() ? it.CurrentContext() : outer_context; 159 it.HasContext() ? it.CurrentContext() : outer_context;
168 160 Handle<StringSet> non_locals = it.GetNonLocals();
169 // The "this" binding, if any, can't be bound via "with". If we need 161 MaterializeReceiver(materialized, local_context, local_function,
170 // to, add another node onto the outer context to bind "this". 162 non_locals);
171 Handle<Context> receiver_context = 163 frame_inspector.MaterializeStackLocals(materialized, local_function);
172 MaterializeReceiver(native_context_, local_context, local_function, 164 MaterializeArgumentsObject(materialized, 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; 165 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(); 166 context_chain_element.scope_info = it.CurrentScopeInfo();
167 context_chain_element.materialized_object = materialized;
168 // Non-locals that are already being referenced by the current function
169 // are guaranteed to be correctly resolved.
170 context_chain_element.whitelist = non_locals;
171 if (it.HasContext()) {
172 context_chain_element.wrapped_context = it.CurrentContext();
173 }
188 context_chain_.Add(context_chain_element); 174 context_chain_.Add(context_chain_element);
189 175 evaluation_context_ = outer_context;
190 stop = true; 176 break;
191 RecordContextsInChain(&inner_context, receiver_context, with_context);
192 } else if (scope_type == ScopeIterator::ScopeTypeCatch || 177 } else if (scope_type == ScopeIterator::ScopeTypeCatch ||
193 scope_type == ScopeIterator::ScopeTypeWith) { 178 scope_type == ScopeIterator::ScopeTypeWith) {
194 Handle<Context> cloned_context = Handle<Context>::cast(
195 isolate->factory()->CopyFixedArray(it.CurrentContext()));
196
197 ContextChainElement context_chain_element; 179 ContextChainElement context_chain_element;
198 context_chain_element.original_context = it.CurrentContext(); 180 Handle<Context> current_context = it.CurrentContext();
199 context_chain_element.cloned_context = cloned_context; 181 if (!current_context->IsDebugEvaluateContext()) {
182 context_chain_element.wrapped_context = current_context;
183 }
200 context_chain_.Add(context_chain_element); 184 context_chain_.Add(context_chain_element);
201
202 RecordContextsInChain(&inner_context, cloned_context, cloned_context);
203 } else if (scope_type == ScopeIterator::ScopeTypeBlock) { 185 } else if (scope_type == ScopeIterator::ScopeTypeBlock) {
204 Handle<JSObject> materialized_object = NewJSObjectWithNullProto(); 186 Handle<JSObject> materialized = NewJSObjectWithNullProto();
205 frame_inspector.MaterializeStackLocals(materialized_object, 187 frame_inspector.MaterializeStackLocals(materialized,
206 it.CurrentScopeInfo()); 188 it.CurrentScopeInfo());
189 ContextChainElement context_chain_element;
190 context_chain_element.scope_info = it.CurrentScopeInfo();
191 context_chain_element.materialized_object = materialized;
207 if (it.HasContext()) { 192 if (it.HasContext()) {
208 Handle<Context> cloned_context = Handle<Context>::cast( 193 context_chain_element.wrapped_context = it.CurrentContext();
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 } 194 }
195 context_chain_.Add(context_chain_element);
232 } else { 196 } else {
233 stop = true; 197 break;
234 } 198 }
235 } 199 }
236 if (innermost_context_.is_null()) { 200
237 innermost_context_ = outer_context; 201 for (int i = context_chain_.length() - 1; i >= 0; i--) {
202 evaluation_context_ = factory->NewDebugEvaluateContext(
203 evaluation_context_, context_chain_[i].materialized_object,
204 context_chain_[i].wrapped_context, context_chain_[i].whitelist);
238 } 205 }
239 DCHECK(!innermost_context_.is_null());
240 } 206 }
241 207
242 208
243 void DebugEvaluate::ContextBuilder::UpdateValues() { 209 void DebugEvaluate::ContextBuilder::UpdateValues() {
244 // TODO(yangguo): remove updating values. 210 // TODO(yangguo): remove updating values.
245 for (int i = 0; i < context_chain_.length(); i++) { 211 for (int i = 0; i < context_chain_.length(); i++) {
246 ContextChainElement element = context_chain_[i]; 212 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()) { 213 if (!element.materialized_object.is_null()) {
256 // Write back potential changes to materialized stack locals to the 214 // Write back potential changes to materialized stack locals to the stack.
257 // stack.
258 FrameInspector(frame_, inlined_jsframe_index_, isolate_) 215 FrameInspector(frame_, inlined_jsframe_index_, isolate_)
259 .UpdateStackLocalsFromMaterializedObject(element.materialized_object, 216 .UpdateStackLocalsFromMaterializedObject(element.materialized_object,
260 element.scope_info); 217 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 } 218 }
267 } 219 }
268 } 220 }
269 221
270 222
271 Handle<JSObject> DebugEvaluate::ContextBuilder::NewJSObjectWithNullProto() { 223 Handle<JSObject> DebugEvaluate::ContextBuilder::NewJSObjectWithNullProto() {
272 Handle<JSObject> result = 224 Handle<JSObject> result =
273 isolate_->factory()->NewJSObject(isolate_->object_function()); 225 isolate_->factory()->NewJSObject(isolate_->object_function());
274 Handle<Map> new_map = 226 Handle<Map> new_map =
275 Map::Copy(Handle<Map>(result->map()), "ObjectWithNullProto"); 227 Map::Copy(Handle<Map>(result->map()), "ObjectWithNullProto");
276 Map::SetPrototype(new_map, isolate_->factory()->null_value()); 228 Map::SetPrototype(new_map, isolate_->factory()->null_value());
277 JSObject::MigrateToMap(result, new_map); 229 JSObject::MigrateToMap(result, new_map);
278 return result; 230 return result;
279 } 231 }
280 232
281 233
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( 234 void DebugEvaluate::ContextBuilder::MaterializeArgumentsObject(
295 Handle<JSObject> target, Handle<JSFunction> function) { 235 Handle<JSObject> target, Handle<JSFunction> function) {
296 // Do not materialize the arguments object for eval or top-level code. 236 // Do not materialize the arguments object for eval or top-level code.
297 // Skip if "arguments" is already taken. 237 // Skip if "arguments" is already taken.
298 if (!function->shared()->is_function()) return; 238 if (!function->shared()->is_function()) return;
299 Maybe<bool> maybe = JSReceiver::HasOwnProperty( 239 Maybe<bool> maybe = JSReceiver::HasOwnProperty(
300 target, isolate_->factory()->arguments_string()); 240 target, isolate_->factory()->arguments_string());
301 DCHECK(maybe.IsJust()); 241 DCHECK(maybe.IsJust());
302 if (maybe.FromJust()) return; 242 if (maybe.FromJust()) return;
303 243
304 // FunctionGetArguments can't throw an exception. 244 // FunctionGetArguments can't throw an exception.
305 Handle<JSObject> arguments = Accessors::FunctionGetArguments(function); 245 Handle<JSObject> arguments = Accessors::FunctionGetArguments(function);
306 Handle<String> arguments_str = isolate_->factory()->arguments_string(); 246 Handle<String> arguments_str = isolate_->factory()->arguments_string();
307 JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments, 247 JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments,
308 NONE) 248 NONE)
309 .Check(); 249 .Check();
310 } 250 }
311 251
312 252 void DebugEvaluate::ContextBuilder::MaterializeReceiver(
313 MaybeHandle<Object> DebugEvaluate::ContextBuilder::LoadFromContext( 253 Handle<JSObject> target, Handle<Context> local_context,
314 Handle<Context> context, Handle<String> name, bool* global) { 254 Handle<JSFunction> local_function, Handle<StringSet> non_locals) {
315 static const ContextLookupFlags flags = FOLLOW_CONTEXT_CHAIN; 255 Handle<Object> recv = isolate_->factory()->undefined_value();
316 int index; 256 Handle<String> name = isolate_->factory()->this_string();
317 PropertyAttributes attributes; 257 if (non_locals->Has(name)) {
318 BindingFlags binding; 258 // 'this' is allocated in an outer context and is is already being
319 Handle<Object> holder = 259 // referenced by the current function, so it can be correctly resolved.
320 context->Lookup(name, flags, &index, &attributes, &binding); 260 return;
321 if (holder.is_null()) return MaybeHandle<Object>(); 261 } else if (local_function->shared()->scope_info()->HasReceiver()) {
322 Handle<Object> value; 262 recv = handle(frame_->receiver(), isolate_);
323 if (index != Context::kNotFound) { // Found on context.
324 Handle<Context> context = Handle<Context>::cast(holder);
325 // Do not shadow variables on the script context.
326 *global = context->IsScriptContext();
327 return Handle<Object>(context->get(index), isolate_);
328 } else { // Found on object.
329 Handle<JSReceiver> object = Handle<JSReceiver>::cast(holder);
330 // Do not shadow properties on the global object.
331 *global = object->IsJSGlobalObject();
332 return JSReceiver::GetDataProperty(object, name);
333 } 263 }
334 } 264 JSObject::SetOwnPropertyIgnoreAttributes(target, name, recv, NONE).Check();
335
336
337 void DebugEvaluate::ContextBuilder::MaterializeContextChain(
338 Handle<JSObject> target, Handle<Context> context) {
339 for (const Handle<String>& name : non_locals_) {
340 HandleScope scope(isolate_);
341 Handle<Object> value;
342 bool global;
343 if (!LoadFromContext(context, name, &global).ToHandle(&value) || global) {
344 // If resolving the variable fails, skip it. If it resolves to a global
345 // variable, skip it as well since it's not read-only and can be resolved
346 // within debug-evaluate.
347 continue;
348 }
349 JSObject::SetOwnPropertyIgnoreAttributes(target, name, value, NONE).Check();
350 }
351 }
352
353
354 void DebugEvaluate::ContextBuilder::StoreToContext(Handle<Context> context,
355 Handle<String> name,
356 Handle<Object> value) {
357 static const ContextLookupFlags flags = FOLLOW_CONTEXT_CHAIN;
358 int index;
359 PropertyAttributes attributes;
360 BindingFlags binding;
361 Handle<Object> holder =
362 context->Lookup(name, flags, &index, &attributes, &binding);
363 if (holder.is_null()) return;
364 if (attributes & READ_ONLY) return;
365 if (index != Context::kNotFound) { // Found on context.
366 Handle<Context> context = Handle<Context>::cast(holder);
367 context->set(index, *value);
368 } else { // Found on object.
369 Handle<JSReceiver> object = Handle<JSReceiver>::cast(holder);
370 LookupIterator lookup(object, name);
371 if (lookup.state() != LookupIterator::DATA) return;
372 CHECK(JSReceiver::SetDataProperty(&lookup, value).FromJust());
373 }
374 }
375
376
377 void DebugEvaluate::ContextBuilder::UpdateContextChainFromMaterializedObject(
378 Handle<JSObject> source, Handle<Context> context) {
379 // TODO(yangguo): check whether overwriting context fields is actually safe
380 // wrt fields we consider constant.
381 for (const Handle<String>& name : non_locals_) {
382 HandleScope scope(isolate_);
383 Handle<Object> value = JSReceiver::GetDataProperty(source, name);
384 StoreToContext(context, name, value);
385 }
386 }
387
388
389 Handle<Context> DebugEvaluate::ContextBuilder::MaterializeReceiver(
390 Handle<Context> parent_context, Handle<Context> lookup_context,
391 Handle<JSFunction> local_function, Handle<JSFunction> global_function,
392 bool this_is_non_local) {
393 Handle<Object> receiver = isolate_->factory()->undefined_value();
394 Handle<String> this_string = isolate_->factory()->this_string();
395 if (this_is_non_local) {
396 bool global;
397 LoadFromContext(lookup_context, this_string, &global).ToHandle(&receiver);
398 } else if (local_function->shared()->scope_info()->HasReceiver()) {
399 receiver = handle(frame_->receiver(), isolate_);
400 }
401 return isolate_->factory()->NewCatchContext(global_function, parent_context,
402 this_string, receiver);
403 } 265 }
404 266
405 } // namespace internal 267 } // namespace internal
406 } // namespace v8 268 } // namespace v8
OLDNEW
« no previous file with comments | « src/debug/debug-evaluate.h ('k') | src/debug/debug-scopes.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698