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 18 static inline bool IsDebugContext(Isolate* isolate, Context* context) { 19 return context->native_context() == *isolate->debug()->debug_context(); 20 } 21 22 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 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.evaluation_context(); 77 Handle<JSObject> receiver(context->global_proxy()); 78 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(); 82 return maybe_result; 83 } 84 85 86 // Compile and evaluate source for the given context. 87 MaybeHandle<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; 98 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); 104 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()) { 113 PrototypeIterator iter(isolate, Handle<JSGlobalProxy>::cast(result)); 114 // 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 122 DebugEvaluate::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 = 130 Handle<JSFunction>::cast(frame_inspector.GetFunction()); 131 Handle<Context> outer_context(local_function->context()); 132 evaluation_context_ = outer_context; 133 outer_info_ = handle(local_function->shared()); 134 Factory* factory = isolate->factory(); 135 136 // 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. 152 const ScopeIterator::Option option = ScopeIterator::COLLECT_NON_LOCALS; 153 for (ScopeIterator it(isolate, &frame_inspector, option); 154 !it.Failed() && !it.Done(); it.Next()) { 155 ScopeIterator::ScopeType scope_type = it.Type(); 156 if (scope_type == ScopeIterator::ScopeTypeLocal) { 157 DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type()); 158 Handle<JSObject> materialized = factory->NewJSObjectWithNullProto(); 159 Handle<Context> local_context = 160 it.HasContext() ? it.CurrentContext() : outer_context; 161 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); 166 ContextChainElement context_chain_element; 167 context_chain_element.scope_info = it.CurrentScopeInfo(); 168 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 } 175 context_chain_.Add(context_chain_element); 176 evaluation_context_ = outer_context; 177 break; 178 } else if (scope_type == ScopeIterator::ScopeTypeCatch || 179 scope_type == ScopeIterator::ScopeTypeWith) { 180 ContextChainElement context_chain_element; 181 Handle<Context> current_context = it.CurrentContext(); 182 if (!current_context->IsDebugEvaluateContext()) { 183 context_chain_element.wrapped_context = current_context; 184 } 185 context_chain_.Add(context_chain_element); 186 } else if (scope_type == ScopeIterator::ScopeTypeBlock || 187 scope_type == ScopeIterator::ScopeTypeEval) { 188 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); 198 } else { 199 break; 200 } 201 } 202 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); 207 } 208 } 209 210 211 void 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]; 215 if (!element.materialized_object.is_null()) { 216 // Write back potential changes to materialized stack locals to the stack. 217 FrameInspector(frame_, inlined_jsframe_index_, isolate_) 218 .UpdateStackLocalsFromMaterializedObject(element.materialized_object, 219 element.scope_info); 220 } 221 } 222 } 223 224 225 void 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. 236 Handle<JSObject> arguments = Accessors::FunctionGetArguments(function); 237 Handle<String> arguments_str = isolate_->factory()->arguments_string(); 238 JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments, 239 NONE) 240 .Check(); 241 } 242 243 void 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; 252 } else if (local_function->shared()->scope_info()->HasReceiver() && 253 !frame_->receiver()->IsTheHole(isolate_)) { 254 recv = handle(frame_->receiver(), isolate_); 255 } 256 JSObject::SetOwnPropertyIgnoreAttributes(target, name, recv, NONE).Check(); 257 } 258 259 } // namespace internal 260 } // namespace v8 261