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/compiler.h" 9#include "src/contexts.h" 10#include "src/debug/debug-frames.h" 11#include "src/debug/debug-scopes.h" 12#include "src/debug/debug.h" 13#include "src/frames-inl.h" 14#include "src/globals.h" 15#include "src/isolate-inl.h" 16 17namespace v8 { 18namespace internal { 19 20static inline bool IsDebugContext(Isolate* isolate, Context* context) { 21 return context->native_context() == *isolate->debug()->debug_context(); 22} 23 24 25MaybeHandle<Object> DebugEvaluate::Global( 26 Isolate* isolate, Handle<String> source, bool disable_break, 27 Handle<HeapObject> context_extension) { 28 // Handle the processing of break. 29 DisableBreak disable_break_scope(isolate->debug(), disable_break); 30 31 // Enter the top context from before the debugger was invoked. 32 SaveContext save(isolate); 33 SaveContext* top = &save; 34 while (top != NULL && IsDebugContext(isolate, *top->context())) { 35 top = top->prev(); 36 } 37 if (top != NULL) isolate->set_context(*top->context()); 38 39 // Get the native context now set to the top context from before the 40 // debugger was invoked. 41 Handle<Context> context = isolate->native_context(); 42 Handle<JSObject> receiver(context->global_proxy()); 43 Handle<SharedFunctionInfo> outer_info(context->closure()->shared(), isolate); 44 return Evaluate(isolate, outer_info, context, context_extension, receiver, 45 source); 46} 47 48 49MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate, 50 StackFrame::Id frame_id, 51 int inlined_jsframe_index, 52 Handle<String> source, 53 bool disable_break, 54 Handle<HeapObject> context_extension) { 55 // Handle the processing of break. 56 DisableBreak disable_break_scope(isolate->debug(), disable_break); 57 58 // Get the frame where the debugging is performed. 59 StackTraceFrameIterator it(isolate, frame_id); 60 if (!it.is_javascript()) return isolate->factory()->undefined_value(); 61 JavaScriptFrame* frame = it.javascript_frame(); 62 63 // Traverse the saved contexts chain to find the active context for the 64 // selected frame. 65 SaveContext* save = 66 DebugFrameHelper::FindSavedContextForFrame(isolate, frame); 67 SaveContext savex(isolate); 68 isolate->set_context(*(save->context())); 69 70 // This is not a lot different than DebugEvaluate::Global, except that 71 // variables accessible by the function we are evaluating from are 72 // materialized and included on top of the native context. Changes to 73 // the materialized object are written back afterwards. 74 // Note that the native context is taken from the original context chain, 75 // which may not be the current native context of the isolate. 76 ContextBuilder context_builder(isolate, frame, inlined_jsframe_index); 77 if (isolate->has_pending_exception()) return MaybeHandle<Object>(); 78 79 Handle<Context> context = context_builder.evaluation_context(); 80 Handle<JSObject> receiver(context->global_proxy()); 81 MaybeHandle<Object> maybe_result = 82 Evaluate(isolate, context_builder.outer_info(), context, 83 context_extension, receiver, source); 84 if (!maybe_result.is_null()) context_builder.UpdateValues(); 85 return maybe_result; 86} 87 88 89// Compile and evaluate source for the given context. 90MaybeHandle<Object> DebugEvaluate::Evaluate( 91 Isolate* isolate, Handle<SharedFunctionInfo> outer_info, 92 Handle<Context> context, Handle<HeapObject> context_extension, 93 Handle<Object> receiver, Handle<String> source) { 94 if (context_extension->IsJSObject()) { 95 Handle<JSObject> extension = Handle<JSObject>::cast(context_extension); 96 Handle<JSFunction> closure(context->closure(), isolate); 97 context = isolate->factory()->NewWithContext( 98 closure, context, 99 ScopeInfo::CreateForWithScope( 100 isolate, context->IsNativeContext() 101 ? Handle<ScopeInfo>::null() 102 : Handle<ScopeInfo>(context->scope_info())), 103 extension); 104 } 105 106 Handle<JSFunction> eval_fun; 107 ASSIGN_RETURN_ON_EXCEPTION( 108 isolate, eval_fun, 109 Compiler::GetFunctionFromEval(source, outer_info, context, SLOPPY, 110 NO_PARSE_RESTRICTION, kNoSourcePosition, 111 kNoSourcePosition), 112 Object); 113 114 Handle<Object> result; 115 ASSIGN_RETURN_ON_EXCEPTION( 116 isolate, result, Execution::Call(isolate, eval_fun, receiver, 0, NULL), 117 Object); 118 119 // Skip the global proxy as it has no properties and always delegates to the 120 // real global object. 121 if (result->IsJSGlobalProxy()) { 122 PrototypeIterator iter(isolate, Handle<JSGlobalProxy>::cast(result)); 123 // TODO(verwaest): This will crash when the global proxy is detached. 124 result = PrototypeIterator::GetCurrent<JSObject>(iter); 125 } 126 127 return result; 128} 129 130 131DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate, 132 JavaScriptFrame* frame, 133 int inlined_jsframe_index) 134 : isolate_(isolate), 135 frame_(frame), 136 inlined_jsframe_index_(inlined_jsframe_index) { 137 FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate); 138 Handle<JSFunction> local_function = frame_inspector.GetFunction(); 139 Handle<Context> outer_context(local_function->context()); 140 evaluation_context_ = outer_context; 141 outer_info_ = handle(local_function->shared()); 142 Factory* factory = isolate->factory(); 143 144 // To evaluate as if we were running eval at the point of the debug break, 145 // we reconstruct the context chain as follows: 146 // - To make stack-allocated variables visible, we materialize them and 147 // use a debug-evaluate context to wrap both the materialized object and 148 // the original context. 149 // - We use the original context chain from the function context to the 150 // native context. 151 // - Between the function scope and the native context, we only resolve 152 // variable names that the current function already uses. Only for these 153 // names we can be sure that they will be correctly resolved. For the 154 // rest, we only resolve to with, script, and native contexts. We use a 155 // whitelist to implement that. 156 // Context::Lookup has special handling for debug-evaluate contexts: 157 // - Look up in the materialized stack variables. 158 // - Look up in the original context. 159 // - Check the whitelist to find out whether to skip contexts during lookup. 160 const ScopeIterator::Option option = ScopeIterator::COLLECT_NON_LOCALS; 161 for (ScopeIterator it(isolate, &frame_inspector, option); 162 !it.Failed() && !it.Done(); it.Next()) { 163 ScopeIterator::ScopeType scope_type = it.Type(); 164 if (scope_type == ScopeIterator::ScopeTypeLocal) { 165 DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type()); 166 Handle<JSObject> materialized = factory->NewJSObjectWithNullProto(); 167 Handle<Context> local_context = 168 it.HasContext() ? it.CurrentContext() : outer_context; 169 Handle<StringSet> non_locals = it.GetNonLocals(); 170 MaterializeReceiver(materialized, local_context, local_function, 171 non_locals); 172 frame_inspector.MaterializeStackLocals(materialized, local_function); 173 MaterializeArgumentsObject(materialized, local_function); 174 ContextChainElement context_chain_element; 175 context_chain_element.scope_info = it.CurrentScopeInfo(); 176 context_chain_element.materialized_object = materialized; 177 // Non-locals that are already being referenced by the current function 178 // are guaranteed to be correctly resolved. 179 context_chain_element.whitelist = non_locals; 180 if (it.HasContext()) { 181 context_chain_element.wrapped_context = it.CurrentContext(); 182 } 183 context_chain_.Add(context_chain_element); 184 evaluation_context_ = outer_context; 185 break; 186 } else if (scope_type == ScopeIterator::ScopeTypeCatch || 187 scope_type == ScopeIterator::ScopeTypeWith) { 188 ContextChainElement context_chain_element; 189 Handle<Context> current_context = it.CurrentContext(); 190 if (!current_context->IsDebugEvaluateContext()) { 191 context_chain_element.wrapped_context = current_context; 192 } 193 context_chain_.Add(context_chain_element); 194 } else if (scope_type == ScopeIterator::ScopeTypeBlock || 195 scope_type == ScopeIterator::ScopeTypeEval) { 196 Handle<JSObject> materialized = factory->NewJSObjectWithNullProto(); 197 frame_inspector.MaterializeStackLocals(materialized, 198 it.CurrentScopeInfo()); 199 ContextChainElement context_chain_element; 200 context_chain_element.scope_info = it.CurrentScopeInfo(); 201 context_chain_element.materialized_object = materialized; 202 if (it.HasContext()) { 203 context_chain_element.wrapped_context = it.CurrentContext(); 204 } 205 context_chain_.Add(context_chain_element); 206 } else { 207 break; 208 } 209 } 210 211 for (int i = context_chain_.length() - 1; i >= 0; i--) { 212 Handle<ScopeInfo> scope_info(ScopeInfo::CreateForWithScope( 213 isolate, evaluation_context_->IsNativeContext() 214 ? Handle<ScopeInfo>::null() 215 : Handle<ScopeInfo>(evaluation_context_->scope_info()))); 216 scope_info->SetIsDebugEvaluateScope(); 217 evaluation_context_ = factory->NewDebugEvaluateContext( 218 evaluation_context_, scope_info, context_chain_[i].materialized_object, 219 context_chain_[i].wrapped_context, context_chain_[i].whitelist); 220 } 221} 222 223 224void DebugEvaluate::ContextBuilder::UpdateValues() { 225 // TODO(yangguo): remove updating values. 226 for (int i = 0; i < context_chain_.length(); i++) { 227 ContextChainElement element = context_chain_[i]; 228 if (!element.materialized_object.is_null()) { 229 // Write back potential changes to materialized stack locals to the stack. 230 FrameInspector(frame_, inlined_jsframe_index_, isolate_) 231 .UpdateStackLocalsFromMaterializedObject(element.materialized_object, 232 element.scope_info); 233 } 234 } 235} 236 237 238void DebugEvaluate::ContextBuilder::MaterializeArgumentsObject( 239 Handle<JSObject> target, Handle<JSFunction> function) { 240 // Do not materialize the arguments object for eval or top-level code. 241 // Skip if "arguments" is already taken. 242 if (!function->shared()->is_function()) return; 243 Maybe<bool> maybe = JSReceiver::HasOwnProperty( 244 target, isolate_->factory()->arguments_string()); 245 DCHECK(maybe.IsJust()); 246 if (maybe.FromJust()) return; 247 248 // FunctionGetArguments can't throw an exception. 249 Handle<JSObject> arguments = Accessors::FunctionGetArguments(function); 250 Handle<String> arguments_str = isolate_->factory()->arguments_string(); 251 JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments, 252 NONE) 253 .Check(); 254} 255 256void DebugEvaluate::ContextBuilder::MaterializeReceiver( 257 Handle<JSObject> target, Handle<Context> local_context, 258 Handle<JSFunction> local_function, Handle<StringSet> non_locals) { 259 Handle<Object> recv = isolate_->factory()->undefined_value(); 260 Handle<String> name = isolate_->factory()->this_string(); 261 if (non_locals->Has(name)) { 262 // 'this' is allocated in an outer context and is is already being 263 // referenced by the current function, so it can be correctly resolved. 264 return; 265 } else if (local_function->shared()->scope_info()->HasReceiver() && 266 !frame_->receiver()->IsTheHole(isolate_)) { 267 recv = handle(frame_->receiver(), isolate_); 268 } 269 JSObject::SetOwnPropertyIgnoreAttributes(target, name, recv, NONE).Check(); 270} 271 272} // namespace internal 273} // namespace v8 274