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/interpreter/bytecode-array-iterator.h" 16#include "src/interpreter/bytecodes.h" 17#include "src/isolate-inl.h" 18 19namespace v8 { 20namespace internal { 21 22static inline bool IsDebugContext(Isolate* isolate, Context* context) { 23 return context->native_context() == *isolate->debug()->debug_context(); 24} 25 26MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate, 27 Handle<String> source) { 28 // Handle the processing of break. 29 DisableBreak disable_break_scope(isolate->debug()); 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, receiver, source, false); 45} 46 47MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate, 48 StackFrame::Id frame_id, 49 int inlined_jsframe_index, 50 Handle<String> source, 51 bool throw_on_side_effect) { 52 // Handle the processing of break. 53 DisableBreak disable_break_scope(isolate->debug()); 54 55 // Get the frame where the debugging is performed. 56 StackTraceFrameIterator it(isolate, frame_id); 57 if (!it.is_javascript()) return isolate->factory()->undefined_value(); 58 JavaScriptFrame* frame = it.javascript_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, receiver, source, 80 throw_on_side_effect); 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. 87MaybeHandle<Object> DebugEvaluate::Evaluate( 88 Isolate* isolate, Handle<SharedFunctionInfo> outer_info, 89 Handle<Context> context, Handle<Object> receiver, Handle<String> source, 90 bool throw_on_side_effect) { 91 Handle<JSFunction> eval_fun; 92 ASSIGN_RETURN_ON_EXCEPTION( 93 isolate, eval_fun, 94 Compiler::GetFunctionFromEval(source, outer_info, context, SLOPPY, 95 NO_PARSE_RESTRICTION, kNoSourcePosition, 96 kNoSourcePosition, kNoSourcePosition), 97 Object); 98 99 Handle<Object> result; 100 { 101 NoSideEffectScope no_side_effect(isolate, throw_on_side_effect); 102 ASSIGN_RETURN_ON_EXCEPTION( 103 isolate, result, Execution::Call(isolate, eval_fun, receiver, 0, NULL), 104 Object); 105 } 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, Handle<JSGlobalProxy>::cast(result)); 111 // TODO(verwaest): This will crash when the global proxy is detached. 112 result = PrototypeIterator::GetCurrent<JSObject>(iter); 113 } 114 115 return result; 116} 117 118 119DebugEvaluate::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> local_function = frame_inspector.GetFunction(); 127 Handle<Context> outer_context(local_function->context()); 128 evaluation_context_ = outer_context; 129 outer_info_ = handle(local_function->shared()); 130 Factory* factory = isolate->factory(); 131 132 // To evaluate as if we were running eval at the point of the debug break, 133 // we reconstruct the context chain as follows: 134 // - To make stack-allocated variables visible, we materialize them and 135 // use a debug-evaluate context to wrap both the materialized object and 136 // the original context. 137 // - We use the original context chain from the function context to the 138 // native context. 139 // - Between the function scope and the native context, we only resolve 140 // variable names that the current function already uses. Only for these 141 // names we can be sure that they will be correctly resolved. For the 142 // rest, we only resolve to with, script, and native contexts. We use a 143 // whitelist to implement that. 144 // Context::Lookup has special handling for debug-evaluate contexts: 145 // - Look up in the materialized stack variables. 146 // - Look up in the original context. 147 // - Check the whitelist to find out whether to skip contexts during lookup. 148 const ScopeIterator::Option option = ScopeIterator::COLLECT_NON_LOCALS; 149 for (ScopeIterator it(isolate, &frame_inspector, option); !it.Done(); 150 it.Next()) { 151 ScopeIterator::ScopeType scope_type = it.Type(); 152 if (scope_type == ScopeIterator::ScopeTypeLocal) { 153 DCHECK_EQ(FUNCTION_SCOPE, it.CurrentScopeInfo()->scope_type()); 154 Handle<JSObject> materialized = factory->NewJSObjectWithNullProto(); 155 Handle<Context> local_context = 156 it.HasContext() ? it.CurrentContext() : outer_context; 157 Handle<StringSet> non_locals = it.GetNonLocals(); 158 MaterializeReceiver(materialized, local_context, local_function, 159 non_locals); 160 frame_inspector.MaterializeStackLocals(materialized, local_function); 161 MaterializeArgumentsObject(materialized, local_function); 162 ContextChainElement context_chain_element; 163 context_chain_element.scope_info = it.CurrentScopeInfo(); 164 context_chain_element.materialized_object = materialized; 165 // Non-locals that are already being referenced by the current function 166 // are guaranteed to be correctly resolved. 167 context_chain_element.whitelist = non_locals; 168 if (it.HasContext()) { 169 context_chain_element.wrapped_context = it.CurrentContext(); 170 } 171 context_chain_.Add(context_chain_element); 172 evaluation_context_ = outer_context; 173 break; 174 } else if (scope_type == ScopeIterator::ScopeTypeCatch || 175 scope_type == ScopeIterator::ScopeTypeWith) { 176 ContextChainElement context_chain_element; 177 Handle<Context> current_context = it.CurrentContext(); 178 if (!current_context->IsDebugEvaluateContext()) { 179 context_chain_element.wrapped_context = current_context; 180 } 181 context_chain_.Add(context_chain_element); 182 } else if (scope_type == ScopeIterator::ScopeTypeBlock || 183 scope_type == ScopeIterator::ScopeTypeEval) { 184 Handle<JSObject> materialized = factory->NewJSObjectWithNullProto(); 185 frame_inspector.MaterializeStackLocals(materialized, 186 it.CurrentScopeInfo()); 187 ContextChainElement context_chain_element; 188 context_chain_element.scope_info = it.CurrentScopeInfo(); 189 context_chain_element.materialized_object = materialized; 190 if (it.HasContext()) { 191 context_chain_element.wrapped_context = it.CurrentContext(); 192 } 193 context_chain_.Add(context_chain_element); 194 } else { 195 break; 196 } 197 } 198 199 for (int i = context_chain_.length() - 1; i >= 0; i--) { 200 Handle<ScopeInfo> scope_info(ScopeInfo::CreateForWithScope( 201 isolate, evaluation_context_->IsNativeContext() 202 ? Handle<ScopeInfo>::null() 203 : Handle<ScopeInfo>(evaluation_context_->scope_info()))); 204 scope_info->SetIsDebugEvaluateScope(); 205 evaluation_context_ = factory->NewDebugEvaluateContext( 206 evaluation_context_, scope_info, context_chain_[i].materialized_object, 207 context_chain_[i].wrapped_context, context_chain_[i].whitelist); 208 } 209} 210 211 212void DebugEvaluate::ContextBuilder::UpdateValues() { 213 // TODO(yangguo): remove updating values. 214 for (int i = 0; i < context_chain_.length(); i++) { 215 ContextChainElement element = context_chain_[i]; 216 if (!element.materialized_object.is_null()) { 217 // Write back potential changes to materialized stack locals to the stack. 218 FrameInspector(frame_, inlined_jsframe_index_, isolate_) 219 .UpdateStackLocalsFromMaterializedObject(element.materialized_object, 220 element.scope_info); 221 } 222 } 223} 224 225 226void DebugEvaluate::ContextBuilder::MaterializeArgumentsObject( 227 Handle<JSObject> target, Handle<JSFunction> function) { 228 // Do not materialize the arguments object for eval or top-level code. 229 // Skip if "arguments" is already taken. 230 if (function->shared()->is_toplevel()) return; 231 Maybe<bool> maybe = JSReceiver::HasOwnProperty( 232 target, isolate_->factory()->arguments_string()); 233 DCHECK(maybe.IsJust()); 234 if (maybe.FromJust()) return; 235 236 // FunctionGetArguments can't throw an exception. 237 Handle<JSObject> arguments = Accessors::FunctionGetArguments(function); 238 Handle<String> arguments_str = isolate_->factory()->arguments_string(); 239 JSObject::SetOwnPropertyIgnoreAttributes(target, arguments_str, arguments, 240 NONE) 241 .Check(); 242} 243 244void DebugEvaluate::ContextBuilder::MaterializeReceiver( 245 Handle<JSObject> target, Handle<Context> local_context, 246 Handle<JSFunction> local_function, Handle<StringSet> non_locals) { 247 Handle<Object> recv = isolate_->factory()->undefined_value(); 248 Handle<String> name = isolate_->factory()->this_string(); 249 if (non_locals->Has(name)) { 250 // 'this' is allocated in an outer context and is is already being 251 // referenced by the current function, so it can be correctly resolved. 252 return; 253 } else if (local_function->shared()->scope_info()->HasReceiver() && 254 !frame_->receiver()->IsTheHole(isolate_)) { 255 recv = handle(frame_->receiver(), isolate_); 256 } 257 JSObject::SetOwnPropertyIgnoreAttributes(target, name, recv, NONE).Check(); 258} 259 260namespace { 261 262bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) { 263 switch (id) { 264 // Whitelist for intrinsics amd runtime functions. 265 // Conversions. 266 case Runtime::kToInteger: 267 case Runtime::kInlineToInteger: 268 case Runtime::kToObject: 269 case Runtime::kInlineToObject: 270 case Runtime::kToString: 271 case Runtime::kInlineToString: 272 case Runtime::kToLength: 273 case Runtime::kInlineToLength: 274 case Runtime::kToNumber: 275 // Type checks. 276 case Runtime::kIsJSReceiver: 277 case Runtime::kInlineIsJSReceiver: 278 case Runtime::kIsSmi: 279 case Runtime::kInlineIsSmi: 280 case Runtime::kIsArray: 281 case Runtime::kInlineIsArray: 282 case Runtime::kIsFunction: 283 case Runtime::kIsDate: 284 case Runtime::kIsJSProxy: 285 case Runtime::kIsRegExp: 286 case Runtime::kIsTypedArray: 287 // Loads. 288 case Runtime::kLoadLookupSlotForCall: 289 // Arrays. 290 case Runtime::kArraySpeciesConstructor: 291 case Runtime::kNormalizeElements: 292 case Runtime::kGetArrayKeys: 293 case Runtime::kHasComplexElements: 294 case Runtime::kEstimateNumberOfElements: 295 // Errors. 296 case Runtime::kReThrow: 297 case Runtime::kThrowReferenceError: 298 case Runtime::kThrowSymbolIteratorInvalid: 299 case Runtime::kThrowIteratorResultNotAnObject: 300 case Runtime::kNewTypeError: 301 // Strings. 302 case Runtime::kInlineStringCharCodeAt: 303 case Runtime::kStringCharCodeAt: 304 case Runtime::kStringIndexOf: 305 case Runtime::kStringReplaceOneCharWithString: 306 case Runtime::kSubString: 307 case Runtime::kInlineSubString: 308 case Runtime::kRegExpInternalReplace: 309 // Literals. 310 case Runtime::kCreateArrayLiteral: 311 case Runtime::kCreateObjectLiteral: 312 case Runtime::kCreateRegExpLiteral: 313 // Misc. 314 case Runtime::kForInPrepare: 315 case Runtime::kInlineCall: 316 case Runtime::kCall: 317 case Runtime::kInlineMaxSmi: 318 case Runtime::kMaxSmi: 319 return true; 320 default: 321 if (FLAG_trace_side_effect_free_debug_evaluate) { 322 PrintF("[debug-evaluate] intrinsic %s may cause side effect.\n", 323 Runtime::FunctionForId(id)->name); 324 } 325 return false; 326 } 327} 328 329bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) { 330 typedef interpreter::Bytecode Bytecode; 331 typedef interpreter::Bytecodes Bytecodes; 332 if (Bytecodes::IsWithoutExternalSideEffects(bytecode)) return true; 333 if (Bytecodes::IsCallOrConstruct(bytecode)) return true; 334 if (Bytecodes::WritesBooleanToAccumulator(bytecode)) return true; 335 if (Bytecodes::IsJumpIfToBoolean(bytecode)) return true; 336 if (Bytecodes::IsPrefixScalingBytecode(bytecode)) return true; 337 switch (bytecode) { 338 // Whitelist for bytecodes. 339 // Loads. 340 case Bytecode::kLdaLookupSlot: 341 case Bytecode::kLdaGlobal: 342 case Bytecode::kLdaNamedProperty: 343 case Bytecode::kLdaKeyedProperty: 344 // Arithmetics. 345 case Bytecode::kAdd: 346 case Bytecode::kAddSmi: 347 case Bytecode::kSub: 348 case Bytecode::kSubSmi: 349 case Bytecode::kMul: 350 case Bytecode::kDiv: 351 case Bytecode::kMod: 352 case Bytecode::kBitwiseAnd: 353 case Bytecode::kBitwiseAndSmi: 354 case Bytecode::kBitwiseOr: 355 case Bytecode::kBitwiseOrSmi: 356 case Bytecode::kBitwiseXor: 357 case Bytecode::kShiftLeft: 358 case Bytecode::kShiftLeftSmi: 359 case Bytecode::kShiftRight: 360 case Bytecode::kShiftRightSmi: 361 case Bytecode::kShiftRightLogical: 362 case Bytecode::kInc: 363 case Bytecode::kDec: 364 case Bytecode::kLogicalNot: 365 case Bytecode::kToBooleanLogicalNot: 366 case Bytecode::kTypeOf: 367 // Contexts. 368 case Bytecode::kCreateBlockContext: 369 case Bytecode::kCreateCatchContext: 370 case Bytecode::kCreateFunctionContext: 371 case Bytecode::kCreateEvalContext: 372 case Bytecode::kCreateWithContext: 373 // Literals. 374 case Bytecode::kCreateArrayLiteral: 375 case Bytecode::kCreateObjectLiteral: 376 case Bytecode::kCreateRegExpLiteral: 377 // Allocations. 378 case Bytecode::kCreateClosure: 379 case Bytecode::kCreateUnmappedArguments: 380 // Conversions. 381 case Bytecode::kToObject: 382 case Bytecode::kToNumber: 383 // Misc. 384 case Bytecode::kForInPrepare: 385 case Bytecode::kForInContinue: 386 case Bytecode::kForInNext: 387 case Bytecode::kForInStep: 388 case Bytecode::kThrow: 389 case Bytecode::kReThrow: 390 case Bytecode::kIllegal: 391 case Bytecode::kCallJSRuntime: 392 case Bytecode::kStackCheck: 393 case Bytecode::kReturn: 394 case Bytecode::kSetPendingMessage: 395 return true; 396 default: 397 if (FLAG_trace_side_effect_free_debug_evaluate) { 398 PrintF("[debug-evaluate] bytecode %s may cause side effect.\n", 399 Bytecodes::ToString(bytecode)); 400 } 401 return false; 402 } 403} 404 405bool BuiltinHasNoSideEffect(Builtins::Name id) { 406 switch (id) { 407 // Whitelist for builtins. 408 // Array builtins. 409 case Builtins::kArrayCode: 410 case Builtins::kArrayIndexOf: 411 case Builtins::kArrayPrototypeValues: 412 case Builtins::kArrayIncludes: 413 case Builtins::kArrayPrototypeEntries: 414 case Builtins::kArrayPrototypeKeys: 415 case Builtins::kArrayForEach: 416 // Math builtins. 417 case Builtins::kMathAbs: 418 case Builtins::kMathAcos: 419 case Builtins::kMathAcosh: 420 case Builtins::kMathAsin: 421 case Builtins::kMathAsinh: 422 case Builtins::kMathAtan: 423 case Builtins::kMathAtanh: 424 case Builtins::kMathAtan2: 425 case Builtins::kMathCeil: 426 case Builtins::kMathCbrt: 427 case Builtins::kMathExpm1: 428 case Builtins::kMathClz32: 429 case Builtins::kMathCos: 430 case Builtins::kMathCosh: 431 case Builtins::kMathExp: 432 case Builtins::kMathFloor: 433 case Builtins::kMathFround: 434 case Builtins::kMathHypot: 435 case Builtins::kMathImul: 436 case Builtins::kMathLog: 437 case Builtins::kMathLog1p: 438 case Builtins::kMathLog2: 439 case Builtins::kMathLog10: 440 case Builtins::kMathMax: 441 case Builtins::kMathMin: 442 case Builtins::kMathPow: 443 case Builtins::kMathRandom: 444 case Builtins::kMathRound: 445 case Builtins::kMathSign: 446 case Builtins::kMathSin: 447 case Builtins::kMathSinh: 448 case Builtins::kMathSqrt: 449 case Builtins::kMathTan: 450 case Builtins::kMathTanh: 451 case Builtins::kMathTrunc: 452 // Number builtins. 453 case Builtins::kNumberConstructor: 454 case Builtins::kNumberIsFinite: 455 case Builtins::kNumberIsInteger: 456 case Builtins::kNumberIsNaN: 457 case Builtins::kNumberIsSafeInteger: 458 case Builtins::kNumberParseFloat: 459 case Builtins::kNumberParseInt: 460 case Builtins::kNumberPrototypeToExponential: 461 case Builtins::kNumberPrototypeToFixed: 462 case Builtins::kNumberPrototypeToPrecision: 463 case Builtins::kNumberPrototypeToString: 464 case Builtins::kNumberPrototypeValueOf: 465 // String builtins. Strings are immutable. 466 case Builtins::kStringFromCharCode: 467 case Builtins::kStringFromCodePoint: 468 case Builtins::kStringConstructor: 469 case Builtins::kStringPrototypeCharAt: 470 case Builtins::kStringPrototypeCharCodeAt: 471 case Builtins::kStringPrototypeEndsWith: 472 case Builtins::kStringPrototypeIncludes: 473 case Builtins::kStringPrototypeIndexOf: 474 case Builtins::kStringPrototypeLastIndexOf: 475 case Builtins::kStringPrototypeStartsWith: 476 case Builtins::kStringPrototypeSubstr: 477 case Builtins::kStringPrototypeSubstring: 478 case Builtins::kStringPrototypeToString: 479 case Builtins::kStringPrototypeToLowerCase: 480 case Builtins::kStringPrototypeToUpperCase: 481 case Builtins::kStringPrototypeTrim: 482 case Builtins::kStringPrototypeTrimLeft: 483 case Builtins::kStringPrototypeTrimRight: 484 case Builtins::kStringPrototypeValueOf: 485 // JSON builtins. 486 case Builtins::kJsonParse: 487 case Builtins::kJsonStringify: 488 // Error builtins. 489 case Builtins::kMakeError: 490 case Builtins::kMakeTypeError: 491 case Builtins::kMakeSyntaxError: 492 case Builtins::kMakeRangeError: 493 case Builtins::kMakeURIError: 494 return true; 495 default: 496 if (FLAG_trace_side_effect_free_debug_evaluate) { 497 PrintF("[debug-evaluate] built-in %s may cause side effect.\n", 498 Builtins::name(id)); 499 } 500 return false; 501 } 502} 503 504static const Address accessors_with_no_side_effect[] = { 505 // Whitelist for accessors. 506 FUNCTION_ADDR(Accessors::StringLengthGetter), 507 FUNCTION_ADDR(Accessors::ArrayLengthGetter)}; 508 509} // anonymous namespace 510 511// static 512bool DebugEvaluate::FunctionHasNoSideEffect(Handle<SharedFunctionInfo> info) { 513 if (FLAG_trace_side_effect_free_debug_evaluate) { 514 PrintF("[debug-evaluate] Checking function %s for side effect.\n", 515 info->DebugName()->ToCString().get()); 516 } 517 518 DCHECK(info->is_compiled()); 519 520 if (info->HasBytecodeArray()) { 521 // Check bytecodes against whitelist. 522 Handle<BytecodeArray> bytecode_array(info->bytecode_array()); 523 if (FLAG_trace_side_effect_free_debug_evaluate) bytecode_array->Print(); 524 for (interpreter::BytecodeArrayIterator it(bytecode_array); !it.done(); 525 it.Advance()) { 526 interpreter::Bytecode bytecode = it.current_bytecode(); 527 528 if (interpreter::Bytecodes::IsCallRuntime(bytecode)) { 529 Runtime::FunctionId id = 530 (bytecode == interpreter::Bytecode::kInvokeIntrinsic) 531 ? it.GetIntrinsicIdOperand(0) 532 : it.GetRuntimeIdOperand(0); 533 if (IntrinsicHasNoSideEffect(id)) continue; 534 return false; 535 } 536 537 if (BytecodeHasNoSideEffect(bytecode)) continue; 538 539 // Did not match whitelist. 540 return false; 541 } 542 return true; 543 } else { 544 // Check built-ins against whitelist. 545 int builtin_index = info->code()->builtin_index(); 546 if (builtin_index >= 0 && builtin_index < Builtins::builtin_count && 547 BuiltinHasNoSideEffect(static_cast<Builtins::Name>(builtin_index))) { 548 return true; 549 } 550 } 551 552 return false; 553} 554 555// static 556bool DebugEvaluate::CallbackHasNoSideEffect(Address function_addr) { 557 for (size_t i = 0; i < arraysize(accessors_with_no_side_effect); i++) { 558 if (function_addr == accessors_with_no_side_effect[i]) return true; 559 } 560 561 if (FLAG_trace_side_effect_free_debug_evaluate) { 562 PrintF("[debug-evaluate] API Callback at %p may cause side effect.\n", 563 reinterpret_cast<void*>(function_addr)); 564 } 565 return false; 566} 567 568} // namespace internal 569} // namespace v8 570