OLD | NEW |
(Empty) | |
| 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/isolate.h" |
| 13 |
| 14 namespace v8 { |
| 15 namespace internal { |
| 16 |
| 17 |
| 18 static inline bool IsDebugContext(Isolate* isolate, Context* context) { |
| 19 // Try to unwrap script context if it exist. |
| 20 if (context->IsScriptContext()) context = context->previous(); |
| 21 DCHECK_NOT_NULL(context); |
| 22 return context == *isolate->debug()->debug_context(); |
| 23 } |
| 24 |
| 25 |
| 26 MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate, |
| 27 Handle<String> source, |
| 28 bool disable_break, |
| 29 Handle<Object> context_extension) { |
| 30 // Handle the processing of break. |
| 31 DisableBreak disable_break_scope(isolate->debug(), disable_break); |
| 32 |
| 33 // Enter the top context from before the debugger was invoked. |
| 34 SaveContext save(isolate); |
| 35 SaveContext* top = &save; |
| 36 while (top != NULL && IsDebugContext(isolate, *top->context())) { |
| 37 top = top->prev(); |
| 38 } |
| 39 if (top != NULL) isolate->set_context(*top->context()); |
| 40 |
| 41 // Get the native context now set to the top context from before the |
| 42 // debugger was invoked. |
| 43 Handle<Context> context = isolate->native_context(); |
| 44 Handle<JSObject> receiver(context->global_proxy()); |
| 45 Handle<SharedFunctionInfo> outer_info(context->closure()->shared(), isolate); |
| 46 return Evaluate(isolate, outer_info, context, context_extension, receiver, |
| 47 source); |
| 48 } |
| 49 |
| 50 |
| 51 MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate, |
| 52 StackFrame::Id frame_id, |
| 53 int inlined_jsframe_index, |
| 54 Handle<String> source, |
| 55 bool disable_break, |
| 56 Handle<Object> context_extension) { |
| 57 // Handle the processing of break. |
| 58 DisableBreak disable_break_scope(isolate->debug(), disable_break); |
| 59 |
| 60 // Get the frame where the debugging is performed. |
| 61 JavaScriptFrameIterator it(isolate, frame_id); |
| 62 JavaScriptFrame* frame = it.frame(); |
| 63 |
| 64 // Traverse the saved contexts chain to find the active context for the |
| 65 // selected frame. |
| 66 SaveContext* save = |
| 67 DebugFrameHelper::FindSavedContextForFrame(isolate, frame); |
| 68 SaveContext savex(isolate); |
| 69 isolate->set_context(*(save->context())); |
| 70 |
| 71 // Materialize stack locals and the arguments object. |
| 72 ContextBuilder context_builder(isolate, frame, inlined_jsframe_index); |
| 73 if (isolate->has_pending_exception()) return MaybeHandle<Object>(); |
| 74 |
| 75 Handle<Object> receiver(frame->receiver(), isolate); |
| 76 MaybeHandle<Object> maybe_result = Evaluate( |
| 77 isolate, context_builder.outer_info(), |
| 78 context_builder.innermost_context(), context_extension, receiver, source); |
| 79 if (!maybe_result.is_null()) context_builder.UpdateValues(); |
| 80 return maybe_result; |
| 81 } |
| 82 |
| 83 |
| 84 // Compile and evaluate source for the given context. |
| 85 MaybeHandle<Object> DebugEvaluate::Evaluate( |
| 86 Isolate* isolate, Handle<SharedFunctionInfo> outer_info, |
| 87 Handle<Context> context, Handle<Object> context_extension, |
| 88 Handle<Object> receiver, Handle<String> source) { |
| 89 if (context_extension->IsJSObject()) { |
| 90 Handle<JSObject> extension = Handle<JSObject>::cast(context_extension); |
| 91 Handle<JSFunction> closure(context->closure(), isolate); |
| 92 context = isolate->factory()->NewWithContext(closure, context, extension); |
| 93 } |
| 94 |
| 95 Handle<JSFunction> eval_fun; |
| 96 ASSIGN_RETURN_ON_EXCEPTION(isolate, eval_fun, |
| 97 Compiler::GetFunctionFromEval( |
| 98 source, outer_info, context, SLOPPY, |
| 99 NO_PARSE_RESTRICTION, RelocInfo::kNoPosition), |
| 100 Object); |
| 101 |
| 102 Handle<Object> result; |
| 103 ASSIGN_RETURN_ON_EXCEPTION( |
| 104 isolate, result, Execution::Call(isolate, eval_fun, receiver, 0, NULL), |
| 105 Object); |
| 106 |
| 107 // Skip the global proxy as it has no properties and always delegates to the |
| 108 // real global object. |
| 109 if (result->IsJSGlobalProxy()) { |
| 110 PrototypeIterator iter(isolate, result); |
| 111 // TODO(verwaest): This will crash when the global proxy is detached. |
| 112 result = Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)); |
| 113 } |
| 114 |
| 115 return result; |
| 116 } |
| 117 |
| 118 |
| 119 DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate, |
| 120 JavaScriptFrame* frame, |
| 121 int inlined_jsframe_index) |
| 122 : isolate_(isolate), |
| 123 frame_(frame), |
| 124 inlined_jsframe_index_(inlined_jsframe_index) { |
| 125 FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate); |
| 126 Handle<JSFunction> function = |
| 127 handle(JSFunction::cast(frame_inspector.GetFunction())); |
| 128 Handle<Context> outer_context = handle(function->context(), isolate); |
| 129 outer_info_ = handle(function->shared()); |
| 130 Handle<Context> inner_context; |
| 131 |
| 132 bool stop = false; |
| 133 for (ScopeIterator it(isolate, &frame_inspector); |
| 134 !it.Failed() && !it.Done() && !stop; it.Next()) { |
| 135 ScopeIterator::ScopeType scope_type = it.Type(); |
| 136 |
| 137 if (scope_type == ScopeIterator::ScopeTypeLocal) { |
| 138 Handle<Context> parent_context = |
| 139 it.HasContext() ? it.CurrentContext() : outer_context; |
| 140 |
| 141 // The "this" binding, if any, can't be bound via "with". If we need |
| 142 // to, add another node onto the outer context to bind "this". |
| 143 parent_context = MaterializeReceiver(parent_context, function); |
| 144 |
| 145 Handle<JSObject> materialized_function = NewJSObjectWithNullProto(); |
| 146 |
| 147 frame_inspector.MaterializeStackLocals(materialized_function, function); |
| 148 |
| 149 MaterializeArgumentsObject(materialized_function, function); |
| 150 |
| 151 Handle<Context> with_context = isolate->factory()->NewWithContext( |
| 152 function, parent_context, materialized_function); |
| 153 |
| 154 ContextChainElement context_chain_element; |
| 155 context_chain_element.original_context = it.CurrentContext(); |
| 156 context_chain_element.materialized_object = materialized_function; |
| 157 context_chain_element.scope_info = it.CurrentScopeInfo(); |
| 158 context_chain_.Add(context_chain_element); |
| 159 |
| 160 stop = true; |
| 161 RecordContextsInChain(&inner_context, with_context, with_context); |
| 162 } else if (scope_type == ScopeIterator::ScopeTypeCatch || |
| 163 scope_type == ScopeIterator::ScopeTypeWith) { |
| 164 Handle<Context> cloned_context = |
| 165 Handle<Context>::cast(FixedArray::CopySize( |
| 166 it.CurrentContext(), it.CurrentContext()->length())); |
| 167 |
| 168 ContextChainElement context_chain_element; |
| 169 context_chain_element.original_context = it.CurrentContext(); |
| 170 context_chain_element.cloned_context = cloned_context; |
| 171 context_chain_.Add(context_chain_element); |
| 172 |
| 173 RecordContextsInChain(&inner_context, cloned_context, cloned_context); |
| 174 } else if (scope_type == ScopeIterator::ScopeTypeBlock) { |
| 175 Handle<JSObject> materialized_object = NewJSObjectWithNullProto(); |
| 176 frame_inspector.MaterializeStackLocals(materialized_object, |
| 177 it.CurrentScopeInfo()); |
| 178 if (it.HasContext()) { |
| 179 Handle<Context> cloned_context = |
| 180 Handle<Context>::cast(FixedArray::CopySize( |
| 181 it.CurrentContext(), it.CurrentContext()->length())); |
| 182 Handle<Context> with_context = isolate->factory()->NewWithContext( |
| 183 function, cloned_context, materialized_object); |
| 184 |
| 185 ContextChainElement context_chain_element; |
| 186 context_chain_element.original_context = it.CurrentContext(); |
| 187 context_chain_element.cloned_context = cloned_context; |
| 188 context_chain_element.materialized_object = materialized_object; |
| 189 context_chain_element.scope_info = it.CurrentScopeInfo(); |
| 190 context_chain_.Add(context_chain_element); |
| 191 |
| 192 RecordContextsInChain(&inner_context, cloned_context, with_context); |
| 193 } else { |
| 194 Handle<Context> with_context = isolate->factory()->NewWithContext( |
| 195 function, outer_context, materialized_object); |
| 196 |
| 197 ContextChainElement context_chain_element; |
| 198 context_chain_element.materialized_object = materialized_object; |
| 199 context_chain_element.scope_info = it.CurrentScopeInfo(); |
| 200 context_chain_.Add(context_chain_element); |
| 201 |
| 202 RecordContextsInChain(&inner_context, with_context, with_context); |
| 203 } |
| 204 } else { |
| 205 stop = true; |
| 206 } |
| 207 } |
| 208 if (innermost_context_.is_null()) { |
| 209 innermost_context_ = outer_context; |
| 210 } |
| 211 DCHECK(!innermost_context_.is_null()); |
| 212 } |
| 213 |
| 214 |
| 215 void DebugEvaluate::ContextBuilder::UpdateValues() { |
| 216 for (int i = 0; i < context_chain_.length(); i++) { |
| 217 ContextChainElement element = context_chain_[i]; |
| 218 if (!element.original_context.is_null() && |
| 219 !element.cloned_context.is_null()) { |
| 220 Handle<Context> cloned_context = element.cloned_context; |
| 221 cloned_context->CopyTo( |
| 222 Context::MIN_CONTEXT_SLOTS, *element.original_context, |
| 223 Context::MIN_CONTEXT_SLOTS, |
| 224 cloned_context->length() - Context::MIN_CONTEXT_SLOTS); |
| 225 } |
| 226 if (!element.materialized_object.is_null()) { |
| 227 // Write back potential changes to materialized stack locals to the |
| 228 // stack. |
| 229 FrameInspector(frame_, inlined_jsframe_index_, isolate_) |
| 230 .UpdateStackLocalsFromMaterializedObject(element.materialized_object, |
| 231 element.scope_info); |
| 232 } |
| 233 } |
| 234 } |
| 235 |
| 236 |
| 237 Handle<JSObject> DebugEvaluate::ContextBuilder::NewJSObjectWithNullProto() { |
| 238 Handle<JSObject> result = |
| 239 isolate_->factory()->NewJSObject(isolate_->object_function()); |
| 240 Handle<Map> new_map = |
| 241 Map::Copy(Handle<Map>(result->map()), "ObjectWithNullProto"); |
| 242 Map::SetPrototype(new_map, isolate_->factory()->null_value()); |
| 243 JSObject::MigrateToMap(result, new_map); |
| 244 return result; |
| 245 } |
| 246 |
| 247 |
| 248 void DebugEvaluate::ContextBuilder::RecordContextsInChain( |
| 249 Handle<Context>* inner_context, Handle<Context> first, |
| 250 Handle<Context> last) { |
| 251 if (!inner_context->is_null()) { |
| 252 (*inner_context)->set_previous(*last); |
| 253 } else { |
| 254 innermost_context_ = last; |
| 255 } |
| 256 *inner_context = first; |
| 257 } |
| 258 |
| 259 |
| 260 void DebugEvaluate::ContextBuilder::MaterializeArgumentsObject( |
| 261 Handle<JSObject> target, Handle<JSFunction> function) { |
| 262 // Do not materialize the arguments object for eval or top-level code. |
| 263 // Skip if "arguments" is already taken. |
| 264 if (!function->shared()->is_function()) return; |
| 265 Maybe<bool> maybe = JSReceiver::HasOwnProperty( |
| 266 target, isolate_->factory()->arguments_string()); |
| 267 DCHECK(maybe.IsJust()); |
| 268 if (maybe.FromJust()) return; |
| 269 |
| 270 // FunctionGetArguments can't throw an exception. |
| 271 Handle<JSObject> arguments = |
| 272 Handle<JSObject>::cast(Accessors::FunctionGetArguments(function)); |
| 273 Handle<String> arguments_str = isolate_->factory()->arguments_string(); |
| 274 JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments, |
| 275 NONE) |
| 276 .Check(); |
| 277 } |
| 278 |
| 279 |
| 280 Handle<Context> DebugEvaluate::ContextBuilder::MaterializeReceiver( |
| 281 Handle<Context> target, Handle<JSFunction> function) { |
| 282 Handle<SharedFunctionInfo> shared(function->shared()); |
| 283 Handle<ScopeInfo> scope_info(shared->scope_info()); |
| 284 Handle<Object> receiver; |
| 285 switch (scope_info->scope_type()) { |
| 286 case FUNCTION_SCOPE: { |
| 287 VariableMode mode; |
| 288 VariableLocation location; |
| 289 InitializationFlag init_flag; |
| 290 MaybeAssignedFlag maybe_assigned_flag; |
| 291 |
| 292 // Don't bother creating a fake context node if "this" is in the context |
| 293 // already. |
| 294 if (ScopeInfo::ContextSlotIndex( |
| 295 scope_info, isolate_->factory()->this_string(), &mode, &location, |
| 296 &init_flag, &maybe_assigned_flag) >= 0) { |
| 297 return target; |
| 298 } |
| 299 receiver = handle(frame_->receiver(), isolate_); |
| 300 break; |
| 301 } |
| 302 case MODULE_SCOPE: |
| 303 receiver = isolate_->factory()->undefined_value(); |
| 304 break; |
| 305 case SCRIPT_SCOPE: |
| 306 receiver = handle(function->global_proxy(), isolate_); |
| 307 break; |
| 308 default: |
| 309 // For eval code, arrow functions, and the like, there's no "this" binding |
| 310 // to materialize. |
| 311 return target; |
| 312 } |
| 313 |
| 314 return isolate_->factory()->NewCatchContext( |
| 315 function, target, isolate_->factory()->this_string(), receiver); |
| 316 } |
| 317 |
| 318 } // namespace internal |
| 319 } // namespace v8 |
OLD | NEW |