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