blob: 4a89d14647702119dddc746e1a0d8e22398b9c1e [file] [log] [blame]
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/debug/debug-evaluate.h"
#include "src/accessors.h"
#include "src/assembler-inl.h"
#include "src/compiler.h"
#include "src/contexts.h"
#include "src/debug/debug-frames.h"
#include "src/debug/debug-scopes.h"
#include "src/debug/debug.h"
#include "src/frames-inl.h"
#include "src/globals.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecodes.h"
#include "src/isolate-inl.h"
#include "src/objects/api-callbacks.h"
#include "src/snapshot/snapshot.h"
namespace v8 {
namespace internal {
MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate,
Handle<String> source,
bool throw_on_side_effect) {
// Disable breaks in side-effect free mode.
DisableBreak disable_break_scope(isolate->debug(), throw_on_side_effect);
Handle<Context> context = isolate->native_context();
ScriptOriginOptions origin_options(false, true);
MaybeHandle<SharedFunctionInfo> maybe_function_info =
Compiler::GetSharedFunctionInfoForScript(
isolate, source,
Compiler::ScriptDetails(isolate->factory()->empty_string()),
origin_options, nullptr, nullptr, ScriptCompiler::kNoCompileOptions,
ScriptCompiler::kNoCacheNoReason, NOT_NATIVES_CODE);
Handle<SharedFunctionInfo> shared_info;
if (!maybe_function_info.ToHandle(&shared_info)) return MaybeHandle<Object>();
Handle<JSFunction> fun =
isolate->factory()->NewFunctionFromSharedFunctionInfo(shared_info,
context);
if (throw_on_side_effect) isolate->debug()->StartSideEffectCheckMode();
MaybeHandle<Object> result = Execution::Call(
isolate, fun, Handle<JSObject>(context->global_proxy(), isolate), 0,
nullptr);
if (throw_on_side_effect) isolate->debug()->StopSideEffectCheckMode();
return result;
}
MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate,
StackFrame::Id frame_id,
int inlined_jsframe_index,
Handle<String> source,
bool throw_on_side_effect) {
// Handle the processing of break.
DisableBreak disable_break_scope(isolate->debug());
// Get the frame where the debugging is performed.
StackTraceFrameIterator it(isolate, frame_id);
if (!it.is_javascript()) return isolate->factory()->undefined_value();
JavaScriptFrame* frame = it.javascript_frame();
// This is not a lot different than DebugEvaluate::Global, except that
// variables accessible by the function we are evaluating from are
// materialized and included on top of the native context. Changes to
// the materialized object are written back afterwards.
// Note that the native context is taken from the original context chain,
// which may not be the current native context of the isolate.
ContextBuilder context_builder(isolate, frame, inlined_jsframe_index);
if (isolate->has_pending_exception()) return MaybeHandle<Object>();
Handle<Context> context = context_builder.evaluation_context();
Handle<JSObject> receiver(context->global_proxy(), isolate);
MaybeHandle<Object> maybe_result =
Evaluate(isolate, context_builder.outer_info(), context, receiver, source,
throw_on_side_effect);
if (!maybe_result.is_null()) context_builder.UpdateValues();
return maybe_result;
}
MaybeHandle<Object> DebugEvaluate::WithTopmostArguments(Isolate* isolate,
Handle<String> source) {
// Handle the processing of break.
DisableBreak disable_break_scope(isolate->debug());
Factory* factory = isolate->factory();
JavaScriptFrameIterator it(isolate);
// Get context and receiver.
Handle<Context> native_context(
Context::cast(it.frame()->context())->native_context(), isolate);
// Materialize arguments as property on an extension object.
Handle<JSObject> materialized = factory->NewJSObjectWithNullProto();
Handle<String> arguments_str = factory->arguments_string();
JSObject::SetOwnPropertyIgnoreAttributes(
materialized, arguments_str,
Accessors::FunctionGetArguments(it.frame(), 0), NONE)
.Check();
// Materialize receiver.
Handle<String> this_str = factory->this_string();
JSObject::SetOwnPropertyIgnoreAttributes(
materialized, this_str, Handle<Object>(it.frame()->receiver(), isolate),
NONE)
.Check();
// Use extension object in a debug-evaluate scope.
Handle<ScopeInfo> scope_info =
ScopeInfo::CreateForWithScope(isolate, Handle<ScopeInfo>::null());
scope_info->SetIsDebugEvaluateScope();
Handle<Context> evaluation_context =
factory->NewDebugEvaluateContext(native_context, scope_info, materialized,
Handle<Context>(), Handle<StringSet>());
Handle<SharedFunctionInfo> outer_info(
native_context->empty_function()->shared(), isolate);
Handle<JSObject> receiver(native_context->global_proxy(), isolate);
const bool throw_on_side_effect = false;
MaybeHandle<Object> maybe_result =
Evaluate(isolate, outer_info, evaluation_context, receiver, source,
throw_on_side_effect);
return maybe_result;
}
// Compile and evaluate source for the given context.
MaybeHandle<Object> DebugEvaluate::Evaluate(
Isolate* isolate, Handle<SharedFunctionInfo> outer_info,
Handle<Context> context, Handle<Object> receiver, Handle<String> source,
bool throw_on_side_effect) {
Handle<JSFunction> eval_fun;
ASSIGN_RETURN_ON_EXCEPTION(
isolate, eval_fun,
Compiler::GetFunctionFromEval(source, outer_info, context,
LanguageMode::kSloppy, NO_PARSE_RESTRICTION,
kNoSourcePosition, kNoSourcePosition,
kNoSourcePosition),
Object);
Handle<Object> result;
bool sucess = false;
if (throw_on_side_effect) isolate->debug()->StartSideEffectCheckMode();
sucess = Execution::Call(isolate, eval_fun, receiver, 0, nullptr)
.ToHandle(&result);
if (throw_on_side_effect) isolate->debug()->StopSideEffectCheckMode();
if (!sucess) {
DCHECK(isolate->has_pending_exception());
return MaybeHandle<Object>();
}
// Skip the global proxy as it has no properties and always delegates to the
// real global object.
if (result->IsJSGlobalProxy()) {
PrototypeIterator iter(isolate, Handle<JSGlobalProxy>::cast(result));
// TODO(verwaest): This will crash when the global proxy is detached.
result = PrototypeIterator::GetCurrent<JSObject>(iter);
}
return result;
}
Handle<SharedFunctionInfo> DebugEvaluate::ContextBuilder::outer_info() const {
return handle(frame_inspector_.GetFunction()->shared(), isolate_);
}
DebugEvaluate::ContextBuilder::ContextBuilder(Isolate* isolate,
JavaScriptFrame* frame,
int inlined_jsframe_index)
: isolate_(isolate),
frame_inspector_(frame, inlined_jsframe_index, isolate),
scope_iterator_(isolate, &frame_inspector_,
ScopeIterator::COLLECT_NON_LOCALS) {
Handle<Context> outer_context(frame_inspector_.GetFunction()->context(),
isolate);
evaluation_context_ = outer_context;
Factory* factory = isolate->factory();
if (scope_iterator_.Done()) return;
// To evaluate as if we were running eval at the point of the debug break,
// we reconstruct the context chain as follows:
// - To make stack-allocated variables visible, we materialize them and
// use a debug-evaluate context to wrap both the materialized object and
// the original context.
// - We use the original context chain from the function context to the
// native context.
// - Between the function scope and the native context, we only resolve
// variable names that the current function already uses. Only for these
// names we can be sure that they will be correctly resolved. For the
// rest, we only resolve to with, script, and native contexts. We use a
// whitelist to implement that.
// Context::Lookup has special handling for debug-evaluate contexts:
// - Look up in the materialized stack variables.
// - Look up in the original context.
// - Check the whitelist to find out whether to skip contexts during lookup.
for (; scope_iterator_.InInnerScope(); scope_iterator_.Next()) {
ScopeIterator::ScopeType scope_type = scope_iterator_.Type();
if (scope_type == ScopeIterator::ScopeTypeScript) break;
ContextChainElement context_chain_element;
if (scope_type == ScopeIterator::ScopeTypeLocal ||
scope_iterator_.DeclaresLocals(ScopeIterator::Mode::STACK)) {
context_chain_element.materialized_object =
scope_iterator_.ScopeObject(ScopeIterator::Mode::STACK);
}
if (scope_iterator_.HasContext()) {
context_chain_element.wrapped_context = scope_iterator_.CurrentContext();
}
if (scope_type == ScopeIterator::ScopeTypeLocal) {
context_chain_element.whitelist = scope_iterator_.GetNonLocals();
}
context_chain_.push_back(context_chain_element);
}
Handle<ScopeInfo> scope_info =
evaluation_context_->IsNativeContext()
? Handle<ScopeInfo>::null()
: handle(evaluation_context_->scope_info(), isolate);
for (auto rit = context_chain_.rbegin(); rit != context_chain_.rend();
rit++) {
ContextChainElement element = *rit;
scope_info = ScopeInfo::CreateForWithScope(isolate, scope_info);
scope_info->SetIsDebugEvaluateScope();
evaluation_context_ = factory->NewDebugEvaluateContext(
evaluation_context_, scope_info, element.materialized_object,
element.wrapped_context, element.whitelist);
}
}
void DebugEvaluate::ContextBuilder::UpdateValues() {
scope_iterator_.Restart();
for (ContextChainElement& element : context_chain_) {
if (!element.materialized_object.is_null()) {
Handle<FixedArray> keys =
KeyAccumulator::GetKeys(element.materialized_object,
KeyCollectionMode::kOwnOnly,
ENUMERABLE_STRINGS)
.ToHandleChecked();
for (int i = 0; i < keys->length(); i++) {
DCHECK(keys->get(i)->IsString());
Handle<String> key(String::cast(keys->get(i)), isolate_);
Handle<Object> value =
JSReceiver::GetDataProperty(element.materialized_object, key);
scope_iterator_.SetVariableValue(key, value);
}
}
scope_iterator_.Next();
}
}
namespace {
bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) {
// Use macro to include both inlined and non-inlined version of an intrinsic.
#define INTRINSIC_WHITELIST(V) \
/* Conversions */ \
V(NumberToStringSkipCache) \
V(ToBigInt) \
V(ToInteger) \
V(ToLength) \
V(ToNumber) \
V(ToObject) \
V(ToString) \
/* Type checks */ \
V(IsArray) \
V(IsDate) \
V(IsFunction) \
V(IsJSProxy) \
V(IsJSReceiver) \
V(IsJSWeakMap) \
V(IsJSWeakSet) \
V(IsRegExp) \
V(IsSmi) \
V(IsTypedArray) \
/* Loads */ \
V(LoadLookupSlotForCall) \
V(GetProperty) \
/* Arrays */ \
V(ArraySpeciesConstructor) \
V(EstimateNumberOfElements) \
V(GetArrayKeys) \
V(HasComplexElements) \
V(HasFastPackedElements) \
V(NewArray) \
V(NormalizeElements) \
V(PrepareElementsForSort) \
V(TrySliceSimpleNonFastElements) \
V(TypedArrayGetBuffer) \
/* Errors */ \
V(NewTypeError) \
V(ReThrow) \
V(ThrowCalledNonCallable) \
V(ThrowInvalidStringLength) \
V(ThrowIteratorResultNotAnObject) \
V(ThrowReferenceError) \
V(ThrowSymbolIteratorInvalid) \
/* Strings */ \
V(RegExpInternalReplace) \
V(StringIncludes) \
V(StringIndexOf) \
V(StringReplaceOneCharWithString) \
V(StringSubstring) \
V(StringToNumber) \
V(StringTrim) \
/* BigInts */ \
V(BigIntEqualToBigInt) \
V(BigIntToBoolean) \
V(BigIntToNumber) \
/* Literals */ \
V(CreateArrayLiteral) \
V(CreateObjectLiteral) \
V(CreateObjectLiteralWithoutAllocationSite) \
V(CreateRegExpLiteral) \
/* Called from builtins */ \
V(AllocateInNewSpace) \
V(AllocateInTargetSpace) \
V(AllocateSeqOneByteString) \
V(AllocateSeqTwoByteString) \
V(ArrayIncludes_Slow) \
V(ArrayIndexOf) \
V(ArrayIsArray) \
V(ClassOf) \
V(GenerateRandomNumbers) \
V(GetFunctionName) \
V(GetOwnPropertyDescriptor) \
V(GlobalPrint) \
V(HasProperty) \
V(ObjectCreate) \
V(ObjectEntries) \
V(ObjectEntriesSkipFastPath) \
V(ObjectHasOwnProperty) \
V(ObjectValues) \
V(ObjectValuesSkipFastPath) \
V(ObjectGetOwnPropertyNames) \
V(ObjectGetOwnPropertyNamesTryFast) \
V(RegExpInitializeAndCompile) \
V(StackGuard) \
V(StringAdd) \
V(StringCharCodeAt) \
V(StringEqual) \
V(StringIndexOfUnchecked) \
V(StringParseFloat) \
V(StringParseInt) \
V(SymbolDescriptiveString) \
V(ThrowRangeError) \
V(ThrowTypeError) \
V(ToName) \
V(TransitionElementsKind) \
/* Misc. */ \
V(Call) \
V(CompleteInobjectSlackTrackingForMap) \
V(HasInPrototypeChain) \
V(MaxSmi) \
V(NewObject) \
V(SmiLexicographicCompare) \
V(StringMaxLength) \
V(StringToArray) \
/* Test */ \
V(GetOptimizationStatus) \
V(OptimizeFunctionOnNextCall) \
V(OptimizeOsr) \
V(UnblockConcurrentRecompilation)
#define CASE(Name) \
case Runtime::k##Name: \
case Runtime::kInline##Name:
switch (id) {
INTRINSIC_WHITELIST(CASE)
return true;
default:
if (FLAG_trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] intrinsic %s may cause side effect.\n",
Runtime::FunctionForId(id)->name);
}
return false;
}
#undef CASE
#undef INTRINSIC_WHITELIST
}
#ifdef DEBUG
bool BuiltinToIntrinsicHasNoSideEffect(Builtins::Name builtin_id,
Runtime::FunctionId intrinsic_id) {
// First check the intrinsic whitelist.
if (IntrinsicHasNoSideEffect(intrinsic_id)) return true;
// Whitelist intrinsics called from specific builtins.
#define BUILTIN_INTRINSIC_WHITELIST(V, W) \
/* Arrays */ \
V(Builtins::kArrayFilter, W(CreateDataProperty)) \
V(Builtins::kArrayMap, W(CreateDataProperty)) \
V(Builtins::kArrayPrototypeSlice, W(CreateDataProperty) W(SetProperty)) \
/* TypedArrays */ \
V(Builtins::kTypedArrayConstructor, \
W(TypedArrayCopyElements) W(ThrowInvalidTypedArrayAlignment)) \
V(Builtins::kTypedArrayPrototypeFilter, W(TypedArrayCopyElements)) \
V(Builtins::kTypedArrayPrototypeMap, W(SetProperty))
#define CASE(Builtin, ...) \
case Builtin: \
return (__VA_ARGS__ false);
#define MATCH(Intrinsic) \
intrinsic_id == Runtime::k##Intrinsic || \
intrinsic_id == Runtime::kInline##Intrinsic ||
switch (builtin_id) {
BUILTIN_INTRINSIC_WHITELIST(CASE, MATCH)
default:
return false;
}
#undef MATCH
#undef CASE
#undef BUILTIN_INTRINSIC_WHITELIST
}
#endif // DEBUG
bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) {
typedef interpreter::Bytecode Bytecode;
typedef interpreter::Bytecodes Bytecodes;
if (Bytecodes::IsWithoutExternalSideEffects(bytecode)) return true;
if (Bytecodes::IsCallOrConstruct(bytecode)) return true;
if (Bytecodes::IsJumpIfToBoolean(bytecode)) return true;
if (Bytecodes::IsPrefixScalingBytecode(bytecode)) return true;
switch (bytecode) {
// Whitelist for bytecodes.
// Loads.
case Bytecode::kLdaLookupSlot:
case Bytecode::kLdaGlobal:
case Bytecode::kLdaNamedProperty:
case Bytecode::kLdaKeyedProperty:
case Bytecode::kLdaGlobalInsideTypeof:
case Bytecode::kLdaLookupSlotInsideTypeof:
// Arithmetics.
case Bytecode::kAdd:
case Bytecode::kAddSmi:
case Bytecode::kSub:
case Bytecode::kSubSmi:
case Bytecode::kMul:
case Bytecode::kMulSmi:
case Bytecode::kDiv:
case Bytecode::kDivSmi:
case Bytecode::kMod:
case Bytecode::kModSmi:
case Bytecode::kExp:
case Bytecode::kExpSmi:
case Bytecode::kNegate:
case Bytecode::kBitwiseAnd:
case Bytecode::kBitwiseAndSmi:
case Bytecode::kBitwiseNot:
case Bytecode::kBitwiseOr:
case Bytecode::kBitwiseOrSmi:
case Bytecode::kBitwiseXor:
case Bytecode::kBitwiseXorSmi:
case Bytecode::kShiftLeft:
case Bytecode::kShiftLeftSmi:
case Bytecode::kShiftRight:
case Bytecode::kShiftRightSmi:
case Bytecode::kShiftRightLogical:
case Bytecode::kShiftRightLogicalSmi:
case Bytecode::kInc:
case Bytecode::kDec:
case Bytecode::kLogicalNot:
case Bytecode::kToBooleanLogicalNot:
case Bytecode::kTypeOf:
// Contexts.
case Bytecode::kCreateBlockContext:
case Bytecode::kCreateCatchContext:
case Bytecode::kCreateFunctionContext:
case Bytecode::kCreateEvalContext:
case Bytecode::kCreateWithContext:
// Literals.
case Bytecode::kCreateArrayLiteral:
case Bytecode::kCreateEmptyArrayLiteral:
case Bytecode::kCreateObjectLiteral:
case Bytecode::kCreateEmptyObjectLiteral:
case Bytecode::kCreateRegExpLiteral:
// Allocations.
case Bytecode::kCreateClosure:
case Bytecode::kCreateUnmappedArguments:
case Bytecode::kCreateRestParameter:
// Comparisons.
case Bytecode::kTestEqual:
case Bytecode::kTestEqualStrict:
case Bytecode::kTestLessThan:
case Bytecode::kTestLessThanOrEqual:
case Bytecode::kTestGreaterThan:
case Bytecode::kTestGreaterThanOrEqual:
case Bytecode::kTestInstanceOf:
case Bytecode::kTestIn:
case Bytecode::kTestReferenceEqual:
case Bytecode::kTestUndetectable:
case Bytecode::kTestTypeOf:
case Bytecode::kTestUndefined:
case Bytecode::kTestNull:
// Conversions.
case Bytecode::kToObject:
case Bytecode::kToName:
case Bytecode::kToNumber:
case Bytecode::kToNumeric:
case Bytecode::kToString:
// Misc.
case Bytecode::kForInEnumerate:
case Bytecode::kForInPrepare:
case Bytecode::kForInContinue:
case Bytecode::kForInNext:
case Bytecode::kForInStep:
case Bytecode::kThrow:
case Bytecode::kReThrow:
case Bytecode::kThrowReferenceErrorIfHole:
case Bytecode::kThrowSuperNotCalledIfHole:
case Bytecode::kThrowSuperAlreadyCalledIfNotHole:
case Bytecode::kIllegal:
case Bytecode::kCallJSRuntime:
case Bytecode::kStackCheck:
case Bytecode::kReturn:
case Bytecode::kSetPendingMessage:
return true;
default:
return false;
}
}
DebugInfo::SideEffectState BuiltinGetSideEffectState(Builtins::Name id) {
switch (id) {
// Whitelist for builtins.
// Object builtins.
case Builtins::kObjectConstructor:
case Builtins::kObjectCreate:
case Builtins::kObjectEntries:
case Builtins::kObjectGetOwnPropertyDescriptor:
case Builtins::kObjectGetOwnPropertyDescriptors:
case Builtins::kObjectGetOwnPropertyNames:
case Builtins::kObjectGetOwnPropertySymbols:
case Builtins::kObjectGetPrototypeOf:
case Builtins::kObjectIs:
case Builtins::kObjectIsExtensible:
case Builtins::kObjectIsFrozen:
case Builtins::kObjectIsSealed:
case Builtins::kObjectPrototypeValueOf:
case Builtins::kObjectValues:
case Builtins::kObjectPrototypeHasOwnProperty:
case Builtins::kObjectPrototypeIsPrototypeOf:
case Builtins::kObjectPrototypePropertyIsEnumerable:
case Builtins::kObjectPrototypeToString:
// Array builtins.
case Builtins::kArrayIsArray:
case Builtins::kArrayConstructor:
case Builtins::kArrayIndexOf:
case Builtins::kArrayPrototypeValues:
case Builtins::kArrayIncludes:
case Builtins::kArrayPrototypeEntries:
case Builtins::kArrayPrototypeFill:
case Builtins::kArrayPrototypeFind:
case Builtins::kArrayPrototypeFindIndex:
case Builtins::kArrayPrototypeFlat:
case Builtins::kArrayPrototypeFlatMap:
case Builtins::kArrayPrototypeKeys:
case Builtins::kArrayPrototypeSlice:
case Builtins::kArrayPrototypeSort:
case Builtins::kArrayForEach:
case Builtins::kArrayEvery:
case Builtins::kArraySome:
case Builtins::kArrayConcat:
case Builtins::kArrayFilter:
case Builtins::kArrayMap:
case Builtins::kArrayReduce:
case Builtins::kArrayReduceRight:
// Trace builtins.
case Builtins::kIsTraceCategoryEnabled:
case Builtins::kTrace:
// TypedArray builtins.
case Builtins::kTypedArrayConstructor:
case Builtins::kTypedArrayPrototypeBuffer:
case Builtins::kTypedArrayPrototypeByteLength:
case Builtins::kTypedArrayPrototypeByteOffset:
case Builtins::kTypedArrayPrototypeLength:
case Builtins::kTypedArrayPrototypeEntries:
case Builtins::kTypedArrayPrototypeKeys:
case Builtins::kTypedArrayPrototypeValues:
case Builtins::kTypedArrayPrototypeFind:
case Builtins::kTypedArrayPrototypeFindIndex:
case Builtins::kTypedArrayPrototypeIncludes:
case Builtins::kTypedArrayPrototypeIndexOf:
case Builtins::kTypedArrayPrototypeLastIndexOf:
case Builtins::kTypedArrayPrototypeSlice:
case Builtins::kTypedArrayPrototypeSubArray:
case Builtins::kTypedArrayPrototypeEvery:
case Builtins::kTypedArrayPrototypeSome:
case Builtins::kTypedArrayPrototypeFilter:
case Builtins::kTypedArrayPrototypeMap:
case Builtins::kTypedArrayPrototypeReduce:
case Builtins::kTypedArrayPrototypeReduceRight:
case Builtins::kTypedArrayPrototypeForEach:
// ArrayBuffer builtins.
case Builtins::kArrayBufferConstructor:
case Builtins::kArrayBufferPrototypeGetByteLength:
case Builtins::kArrayBufferIsView:
case Builtins::kArrayBufferPrototypeSlice:
case Builtins::kReturnReceiver:
// DataView builtins.
case Builtins::kDataViewConstructor:
case Builtins::kDataViewPrototypeGetBuffer:
case Builtins::kDataViewPrototypeGetByteLength:
case Builtins::kDataViewPrototypeGetByteOffset:
case Builtins::kDataViewPrototypeGetInt8:
case Builtins::kDataViewPrototypeGetUint8:
case Builtins::kDataViewPrototypeGetInt16:
case Builtins::kDataViewPrototypeGetUint16:
case Builtins::kDataViewPrototypeGetInt32:
case Builtins::kDataViewPrototypeGetUint32:
case Builtins::kDataViewPrototypeGetFloat32:
case Builtins::kDataViewPrototypeGetFloat64:
case Builtins::kDataViewPrototypeGetBigInt64:
case Builtins::kDataViewPrototypeGetBigUint64:
// Boolean bulitins.
case Builtins::kBooleanConstructor:
case Builtins::kBooleanPrototypeToString:
case Builtins::kBooleanPrototypeValueOf:
// Date builtins.
case Builtins::kDateConstructor:
case Builtins::kDateNow:
case Builtins::kDateParse:
case Builtins::kDatePrototypeGetDate:
case Builtins::kDatePrototypeGetDay:
case Builtins::kDatePrototypeGetFullYear:
case Builtins::kDatePrototypeGetHours:
case Builtins::kDatePrototypeGetMilliseconds:
case Builtins::kDatePrototypeGetMinutes:
case Builtins::kDatePrototypeGetMonth:
case Builtins::kDatePrototypeGetSeconds:
case Builtins::kDatePrototypeGetTime:
case Builtins::kDatePrototypeGetTimezoneOffset:
case Builtins::kDatePrototypeGetUTCDate:
case Builtins::kDatePrototypeGetUTCDay:
case Builtins::kDatePrototypeGetUTCFullYear:
case Builtins::kDatePrototypeGetUTCHours:
case Builtins::kDatePrototypeGetUTCMilliseconds:
case Builtins::kDatePrototypeGetUTCMinutes:
case Builtins::kDatePrototypeGetUTCMonth:
case Builtins::kDatePrototypeGetUTCSeconds:
case Builtins::kDatePrototypeGetYear:
case Builtins::kDatePrototypeToDateString:
case Builtins::kDatePrototypeToISOString:
case Builtins::kDatePrototypeToUTCString:
case Builtins::kDatePrototypeToString:
case Builtins::kDatePrototypeToTimeString:
case Builtins::kDatePrototypeToJson:
case Builtins::kDatePrototypeToPrimitive:
case Builtins::kDatePrototypeValueOf:
// Map builtins.
case Builtins::kMapConstructor:
case Builtins::kMapPrototypeForEach:
case Builtins::kMapPrototypeGet:
case Builtins::kMapPrototypeHas:
case Builtins::kMapPrototypeEntries:
case Builtins::kMapPrototypeGetSize:
case Builtins::kMapPrototypeKeys:
case Builtins::kMapPrototypeValues:
// WeakMap builtins.
case Builtins::kWeakMapConstructor:
case Builtins::kWeakMapGet:
case Builtins::kWeakMapHas:
// Math builtins.
case Builtins::kMathAbs:
case Builtins::kMathAcos:
case Builtins::kMathAcosh:
case Builtins::kMathAsin:
case Builtins::kMathAsinh:
case Builtins::kMathAtan:
case Builtins::kMathAtanh:
case Builtins::kMathAtan2:
case Builtins::kMathCeil:
case Builtins::kMathCbrt:
case Builtins::kMathExpm1:
case Builtins::kMathClz32:
case Builtins::kMathCos:
case Builtins::kMathCosh:
case Builtins::kMathExp:
case Builtins::kMathFloor:
case Builtins::kMathFround:
case Builtins::kMathHypot:
case Builtins::kMathImul:
case Builtins::kMathLog:
case Builtins::kMathLog1p:
case Builtins::kMathLog2:
case Builtins::kMathLog10:
case Builtins::kMathMax:
case Builtins::kMathMin:
case Builtins::kMathPow:
case Builtins::kMathRandom:
case Builtins::kMathRound:
case Builtins::kMathSign:
case Builtins::kMathSin:
case Builtins::kMathSinh:
case Builtins::kMathSqrt:
case Builtins::kMathTan:
case Builtins::kMathTanh:
case Builtins::kMathTrunc:
// Number builtins.
case Builtins::kNumberConstructor:
case Builtins::kNumberIsFinite:
case Builtins::kNumberIsInteger:
case Builtins::kNumberIsNaN:
case Builtins::kNumberIsSafeInteger:
case Builtins::kNumberParseFloat:
case Builtins::kNumberParseInt:
case Builtins::kNumberPrototypeToExponential:
case Builtins::kNumberPrototypeToFixed:
case Builtins::kNumberPrototypeToPrecision:
case Builtins::kNumberPrototypeToString:
case Builtins::kNumberPrototypeValueOf:
// BigInt builtins.
case Builtins::kBigIntConstructor:
case Builtins::kBigIntAsIntN:
case Builtins::kBigIntAsUintN:
case Builtins::kBigIntPrototypeToString:
case Builtins::kBigIntPrototypeValueOf:
// Set builtins.
case Builtins::kSetConstructor:
case Builtins::kSetPrototypeEntries:
case Builtins::kSetPrototypeForEach:
case Builtins::kSetPrototypeGetSize:
case Builtins::kSetPrototypeHas:
case Builtins::kSetPrototypeValues:
// WeakSet builtins.
case Builtins::kWeakSetConstructor:
case Builtins::kWeakSetHas:
// String builtins. Strings are immutable.
case Builtins::kStringFromCharCode:
case Builtins::kStringFromCodePoint:
case Builtins::kStringConstructor:
case Builtins::kStringPrototypeAnchor:
case Builtins::kStringPrototypeBig:
case Builtins::kStringPrototypeBlink:
case Builtins::kStringPrototypeBold:
case Builtins::kStringPrototypeCharAt:
case Builtins::kStringPrototypeCharCodeAt:
case Builtins::kStringPrototypeCodePointAt:
case Builtins::kStringPrototypeConcat:
case Builtins::kStringPrototypeEndsWith:
case Builtins::kStringPrototypeFixed:
case Builtins::kStringPrototypeFontcolor:
case Builtins::kStringPrototypeFontsize:
case Builtins::kStringPrototypeIncludes:
case Builtins::kStringPrototypeIndexOf:
case Builtins::kStringPrototypeItalics:
case Builtins::kStringPrototypeLastIndexOf:
case Builtins::kStringPrototypeLink:
case Builtins::kStringPrototypePadEnd:
case Builtins::kStringPrototypePadStart:
case Builtins::kStringPrototypeRepeat:
case Builtins::kStringPrototypeSlice:
case Builtins::kStringPrototypeSmall:
case Builtins::kStringPrototypeStartsWith:
case Builtins::kStringPrototypeStrike:
case Builtins::kStringPrototypeSub:
case Builtins::kStringPrototypeSubstr:
case Builtins::kStringPrototypeSubstring:
case Builtins::kStringPrototypeSup:
case Builtins::kStringPrototypeToString:
#ifndef V8_INTL_SUPPORT
case Builtins::kStringPrototypeToLowerCase:
case Builtins::kStringPrototypeToUpperCase:
#endif
case Builtins::kStringPrototypeTrim:
case Builtins::kStringPrototypeTrimEnd:
case Builtins::kStringPrototypeTrimStart:
case Builtins::kStringPrototypeValueOf:
case Builtins::kStringToNumber:
case Builtins::kStringSubstring:
// Symbol builtins.
case Builtins::kSymbolConstructor:
case Builtins::kSymbolKeyFor:
case Builtins::kSymbolPrototypeToString:
case Builtins::kSymbolPrototypeValueOf:
case Builtins::kSymbolPrototypeToPrimitive:
// JSON builtins.
case Builtins::kJsonParse:
case Builtins::kJsonStringify:
// Global function builtins.
case Builtins::kGlobalDecodeURI:
case Builtins::kGlobalDecodeURIComponent:
case Builtins::kGlobalEncodeURI:
case Builtins::kGlobalEncodeURIComponent:
case Builtins::kGlobalEscape:
case Builtins::kGlobalUnescape:
case Builtins::kGlobalIsFinite:
case Builtins::kGlobalIsNaN:
// Function builtins.
case Builtins::kFunctionPrototypeToString:
case Builtins::kFunctionPrototypeBind:
case Builtins::kFastFunctionPrototypeBind:
case Builtins::kFunctionPrototypeCall:
case Builtins::kFunctionPrototypeApply:
// Error builtins.
case Builtins::kErrorConstructor:
case Builtins::kMakeError:
case Builtins::kMakeTypeError:
case Builtins::kMakeSyntaxError:
case Builtins::kMakeRangeError:
case Builtins::kMakeURIError:
// RegExp builtins.
case Builtins::kRegExpConstructor:
return DebugInfo::kHasNoSideEffect;
// Set builtins.
case Builtins::kSetIteratorPrototypeNext:
case Builtins::kSetPrototypeAdd:
case Builtins::kSetPrototypeClear:
case Builtins::kSetPrototypeDelete:
// Array builtins.
case Builtins::kArrayIteratorPrototypeNext:
case Builtins::kArrayPrototypePop:
case Builtins::kArrayPrototypePush:
case Builtins::kArrayPrototypeShift:
case Builtins::kArraySplice:
case Builtins::kArrayUnshift:
// Map builtins.
case Builtins::kMapIteratorPrototypeNext:
case Builtins::kMapPrototypeClear:
case Builtins::kMapPrototypeDelete:
case Builtins::kMapPrototypeSet:
// RegExp builtins.
case Builtins::kRegExpPrototypeTest:
case Builtins::kRegExpPrototypeExec:
case Builtins::kRegExpPrototypeSplit:
case Builtins::kRegExpPrototypeFlagsGetter:
case Builtins::kRegExpPrototypeGlobalGetter:
case Builtins::kRegExpPrototypeIgnoreCaseGetter:
case Builtins::kRegExpPrototypeMultilineGetter:
case Builtins::kRegExpPrototypeDotAllGetter:
case Builtins::kRegExpPrototypeUnicodeGetter:
case Builtins::kRegExpPrototypeStickyGetter:
return DebugInfo::kRequiresRuntimeChecks;
default:
if (FLAG_trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] built-in %s may cause side effect.\n",
Builtins::name(id));
}
return DebugInfo::kHasSideEffects;
}
}
bool BytecodeRequiresRuntimeCheck(interpreter::Bytecode bytecode) {
typedef interpreter::Bytecode Bytecode;
switch (bytecode) {
case Bytecode::kStaNamedProperty:
case Bytecode::kStaNamedOwnProperty:
case Bytecode::kStaKeyedProperty:
case Bytecode::kStaInArrayLiteral:
case Bytecode::kStaDataPropertyInLiteral:
case Bytecode::kStaCurrentContextSlot:
return true;
default:
return false;
}
}
} // anonymous namespace
// static
DebugInfo::SideEffectState DebugEvaluate::FunctionGetSideEffectState(
Isolate* isolate, Handle<SharedFunctionInfo> info) {
if (FLAG_trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] Checking function %s for side effect.\n",
info->DebugName()->ToCString().get());
}
DCHECK(info->is_compiled());
if (info->HasBytecodeArray()) {
// Check bytecodes against whitelist.
Handle<BytecodeArray> bytecode_array(info->GetBytecodeArray(), isolate);
if (FLAG_trace_side_effect_free_debug_evaluate) {
bytecode_array->Print();
}
bool requires_runtime_checks = false;
for (interpreter::BytecodeArrayIterator it(bytecode_array); !it.done();
it.Advance()) {
interpreter::Bytecode bytecode = it.current_bytecode();
if (interpreter::Bytecodes::IsCallRuntime(bytecode)) {
Runtime::FunctionId id =
(bytecode == interpreter::Bytecode::kInvokeIntrinsic)
? it.GetIntrinsicIdOperand(0)
: it.GetRuntimeIdOperand(0);
if (IntrinsicHasNoSideEffect(id)) continue;
return DebugInfo::kHasSideEffects;
}
if (BytecodeHasNoSideEffect(bytecode)) continue;
if (BytecodeRequiresRuntimeCheck(bytecode)) {
requires_runtime_checks = true;
continue;
}
if (FLAG_trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] bytecode %s may cause side effect.\n",
interpreter::Bytecodes::ToString(bytecode));
}
// Did not match whitelist.
return DebugInfo::kHasSideEffects;
}
return requires_runtime_checks ? DebugInfo::kRequiresRuntimeChecks
: DebugInfo::kHasNoSideEffect;
} else if (info->IsApiFunction()) {
if (info->GetCode()->is_builtin()) {
return info->GetCode()->builtin_index() == Builtins::kHandleApiCall
? DebugInfo::kHasNoSideEffect
: DebugInfo::kHasSideEffects;
}
} else {
// Check built-ins against whitelist.
int builtin_index =
info->HasBuiltinId() ? info->builtin_id() : Builtins::kNoBuiltinId;
DCHECK_NE(Builtins::kDeserializeLazy, builtin_index);
if (!Builtins::IsBuiltinId(builtin_index))
return DebugInfo::kHasSideEffects;
DebugInfo::SideEffectState state =
BuiltinGetSideEffectState(static_cast<Builtins::Name>(builtin_index));
#ifdef DEBUG
if (state == DebugInfo::kHasNoSideEffect) {
Code* code = isolate->builtins()->builtin(builtin_index);
if (code->builtin_index() == Builtins::kDeserializeLazy) {
// Target builtin is not yet deserialized. Deserialize it now.
DCHECK(Builtins::IsLazy(builtin_index));
DCHECK_EQ(Builtins::TFJ, Builtins::KindOf(builtin_index));
code = Snapshot::DeserializeBuiltin(isolate, builtin_index);
DCHECK_NE(Builtins::kDeserializeLazy, code->builtin_index());
}
// TODO(yangguo): Check builtin-to-builtin calls too.
int mode = RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE);
bool failed = false;
for (RelocIterator it(code, mode); !it.done(); it.next()) {
RelocInfo* rinfo = it.rinfo();
Address address = rinfo->target_external_reference();
const Runtime::Function* function = Runtime::FunctionForEntry(address);
if (function == nullptr) continue;
if (!BuiltinToIntrinsicHasNoSideEffect(
static_cast<Builtins::Name>(builtin_index),
function->function_id)) {
PrintF("Whitelisted builtin %s calls non-whitelisted intrinsic %s\n",
Builtins::name(builtin_index), function->name);
failed = true;
}
DCHECK(!failed);
}
}
#endif // DEBUG
return state;
}
return DebugInfo::kHasSideEffects;
}
// static
bool DebugEvaluate::CallbackHasNoSideEffect(Object* callback_info) {
DisallowHeapAllocation no_gc;
if (callback_info->IsAccessorInfo()) {
// List of whitelisted internal accessors can be found in accessors.h.
AccessorInfo* info = AccessorInfo::cast(callback_info);
if (info->has_no_side_effect()) return true;
if (FLAG_trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] API Callback '");
info->name()->ShortPrint();
PrintF("' may cause side effect.\n");
}
} else if (callback_info->IsInterceptorInfo()) {
InterceptorInfo* info = InterceptorInfo::cast(callback_info);
if (info->has_no_side_effect()) return true;
if (FLAG_trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] API Interceptor may cause side effect.\n");
}
} else if (callback_info->IsCallHandlerInfo()) {
CallHandlerInfo* info = CallHandlerInfo::cast(callback_info);
if (info->IsSideEffectFreeCallHandlerInfo()) return true;
if (FLAG_trace_side_effect_free_debug_evaluate) {
PrintF("[debug-evaluate] API CallHandlerInfo may cause side effect.\n");
}
}
return false;
}
// static
void DebugEvaluate::ApplySideEffectChecks(
Handle<BytecodeArray> bytecode_array) {
for (interpreter::BytecodeArrayIterator it(bytecode_array); !it.done();
it.Advance()) {
interpreter::Bytecode bytecode = it.current_bytecode();
if (BytecodeRequiresRuntimeCheck(bytecode)) {
interpreter::Bytecode debugbreak =
interpreter::Bytecodes::GetDebugBreak(bytecode);
bytecode_array->set(it.current_offset(),
interpreter::Bytecodes::ToByte(debugbreak));
}
}
}
} // namespace internal
} // namespace v8