| // 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/builtins/accessors.h" |
| #include "src/codegen/assembler-inl.h" |
| #include "src/codegen/compiler.h" |
| #include "src/common/globals.h" |
| #include "src/debug/debug-frames.h" |
| #include "src/debug/debug-scopes.h" |
| #include "src/debug/debug.h" |
| #include "src/execution/frames-inl.h" |
| #include "src/execution/isolate-inl.h" |
| #include "src/interpreter/bytecode-array-iterator.h" |
| #include "src/interpreter/bytecodes.h" |
| #include "src/objects/contexts.h" |
| #include "src/snapshot/snapshot.h" |
| #include "src/wasm/wasm-debug.h" |
| #include "src/wasm/wasm-js.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| namespace { |
| static MaybeHandle<SharedFunctionInfo> GetFunctionInfo(Isolate* isolate, |
| Handle<String> source, |
| REPLMode repl_mode) { |
| Compiler::ScriptDetails script_details(isolate->factory()->empty_string()); |
| script_details.repl_mode = repl_mode; |
| ScriptOriginOptions origin_options(false, true); |
| return Compiler::GetSharedFunctionInfoForScript( |
| isolate, source, script_details, origin_options, nullptr, nullptr, |
| ScriptCompiler::kNoCompileOptions, ScriptCompiler::kNoCacheNoReason, |
| NOT_NATIVES_CODE); |
| } |
| } // namespace |
| |
| MaybeHandle<Object> DebugEvaluate::Global(Isolate* isolate, |
| Handle<String> source, |
| debug::EvaluateGlobalMode mode, |
| REPLMode repl_mode) { |
| // Disable breaks in side-effect free mode. |
| DisableBreak disable_break_scope( |
| isolate->debug(), |
| mode == debug::EvaluateGlobalMode::kDisableBreaks || |
| mode == |
| debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect); |
| |
| Handle<SharedFunctionInfo> shared_info; |
| if (!GetFunctionInfo(isolate, source, repl_mode).ToHandle(&shared_info)) { |
| return MaybeHandle<Object>(); |
| } |
| |
| Handle<Context> context = isolate->native_context(); |
| Handle<JSFunction> fun = |
| isolate->factory()->NewFunctionFromSharedFunctionInfo(shared_info, |
| context); |
| if (mode == debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) { |
| isolate->debug()->StartSideEffectCheckMode(); |
| } |
| MaybeHandle<Object> result = Execution::Call( |
| isolate, fun, Handle<JSObject>(context->global_proxy(), isolate), 0, |
| nullptr); |
| if (mode == debug::EvaluateGlobalMode::kDisableBreaksAndThrowOnSideEffect) { |
| isolate->debug()->StopSideEffectCheckMode(); |
| } |
| return result; |
| } |
| |
| MaybeHandle<Object> DebugEvaluate::Local(Isolate* isolate, |
| StackFrameId 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; |
| } |
| |
| V8_EXPORT MaybeHandle<Object> DebugEvaluate::WebAssembly( |
| Handle<WasmInstanceObject> instance, StackFrameId frame_id, |
| Handle<String> source, bool throw_on_side_effect) { |
| Isolate* isolate = instance->GetIsolate(); |
| |
| StackTraceFrameIterator it(isolate, frame_id); |
| if (!it.is_wasm()) return isolate->factory()->undefined_value(); |
| WasmFrame* frame = WasmFrame::cast(it.frame()); |
| |
| Handle<JSProxy> context_extension = WasmJs::GetJSDebugProxy(frame); |
| |
| DisableBreak disable_break_scope(isolate->debug(), /*disable=*/true); |
| |
| Handle<SharedFunctionInfo> shared_info; |
| if (!GetFunctionInfo(isolate, source, REPLMode::kNo).ToHandle(&shared_info)) { |
| return {}; |
| } |
| |
| Handle<ScopeInfo> scope_info = |
| ScopeInfo::CreateForWithScope(isolate, Handle<ScopeInfo>::null()); |
| Handle<Context> context = isolate->factory()->NewWithContext( |
| isolate->native_context(), scope_info, context_extension); |
| |
| Handle<Object> result; |
| if (!DebugEvaluate::Evaluate(isolate, shared_info, context, context_extension, |
| source, throw_on_side_effect) |
| .ToHandle(&result)) { |
| return {}; |
| } |
| |
| return 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<Object> this_value(it.frame()->receiver(), isolate); |
| DCHECK_EQ(it.frame()->IsConstructor(), this_value->IsTheHole(isolate)); |
| if (!this_value->IsTheHole(isolate)) { |
| Handle<String> this_str = factory->this_string(); |
| JSObject::SetOwnPropertyIgnoreAttributes(materialized, this_str, this_value, |
| 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 success = false; |
| if (throw_on_side_effect) isolate->debug()->StartSideEffectCheckMode(); |
| success = Execution::Call(isolate, eval_fun, receiver, 0, nullptr) |
| .ToHandle(&result); |
| if (throw_on_side_effect) isolate->debug()->StopSideEffectCheckMode(); |
| if (!success) DCHECK(isolate->has_pending_exception()); |
| return success ? result : MaybeHandle<Object>(); |
| } |
| |
| 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::ReparseStrategy::kScript) { |
| 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 also wrap all contexts on the chain between the original context |
| // and the function context. |
| // - Between the function scope and the native context, we only resolve |
| // variable names that are guaranteed to not be shadowed by stack-allocated |
| // variables. Contexts between the function context and the original |
| // context have a blocklist attached to implement that. |
| // Context::Lookup has special handling for debug-evaluate contexts: |
| // - Look up in the materialized stack variables. |
| // - Check the blocklist to find out whether to abort further lookup. |
| // - Look up in the original context. |
| for (; !scope_iterator_.Done(); scope_iterator_.Next()) { |
| ScopeIterator::ScopeType scope_type = scope_iterator_.Type(); |
| if (scope_type == ScopeIterator::ScopeTypeScript) break; |
| ContextChainElement context_chain_element; |
| if (scope_iterator_.InInnerScope() && |
| (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_iterator_.InInnerScope()) { |
| context_chain_element.blocklist = scope_iterator_.GetLocals(); |
| } |
| 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.blocklist); |
| } |
| } |
| |
| 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 only the non-inlined version of an intrinsic. |
| #define INTRINSIC_ALLOWLIST(V) \ |
| /* Conversions */ \ |
| V(NumberToStringSlow) \ |
| V(ToBigInt) \ |
| V(ToLength) \ |
| V(ToNumber) \ |
| V(ToObject) \ |
| V(ToString) \ |
| /* Type checks */ \ |
| V(IsArray) \ |
| V(IsFunction) \ |
| V(IsJSProxy) \ |
| V(IsJSReceiver) \ |
| V(IsRegExp) \ |
| V(IsSmi) \ |
| /* Loads */ \ |
| V(LoadLookupSlotForCall) \ |
| V(GetProperty) \ |
| /* Arrays */ \ |
| V(ArraySpeciesConstructor) \ |
| V(HasFastPackedElements) \ |
| V(NewArray) \ |
| V(NormalizeElements) \ |
| V(TypedArrayGetBuffer) \ |
| /* Errors */ \ |
| V(NewTypeError) \ |
| V(ReThrow) \ |
| V(ThrowCalledNonCallable) \ |
| V(ThrowInvalidStringLength) \ |
| V(ThrowIteratorError) \ |
| V(ThrowIteratorResultNotAnObject) \ |
| V(ThrowPatternAssignmentNonCoercible) \ |
| V(ThrowReferenceError) \ |
| V(ThrowSymbolIteratorInvalid) \ |
| /* Strings */ \ |
| V(StringIncludes) \ |
| V(StringIndexOf) \ |
| V(StringReplaceOneCharWithString) \ |
| V(StringSubstring) \ |
| V(StringToNumber) \ |
| V(StringTrim) \ |
| /* BigInts */ \ |
| V(BigIntEqualToBigInt) \ |
| V(BigIntToBoolean) \ |
| V(BigIntToNumber) \ |
| /* Literals */ \ |
| V(CreateArrayLiteral) \ |
| V(CreateArrayLiteralWithoutAllocationSite) \ |
| V(CreateObjectLiteral) \ |
| V(CreateObjectLiteralWithoutAllocationSite) \ |
| V(CreateRegExpLiteral) \ |
| /* Called from builtins */ \ |
| V(AllocateInYoungGeneration) \ |
| V(AllocateInOldGeneration) \ |
| V(AllocateSeqOneByteString) \ |
| V(AllocateSeqTwoByteString) \ |
| V(ArrayIncludes_Slow) \ |
| V(ArrayIndexOf) \ |
| V(ArrayIsArray) \ |
| V(GetFunctionName) \ |
| V(GetOwnPropertyDescriptor) \ |
| V(GlobalPrint) \ |
| V(HasProperty) \ |
| V(ObjectCreate) \ |
| V(ObjectEntries) \ |
| V(ObjectEntriesSkipFastPath) \ |
| V(ObjectHasOwnProperty) \ |
| V(ObjectKeys) \ |
| V(ObjectValues) \ |
| V(ObjectValuesSkipFastPath) \ |
| V(ObjectGetOwnPropertyNames) \ |
| V(ObjectGetOwnPropertyNamesTryFast) \ |
| V(ObjectIsExtensible) \ |
| 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(IncrementUseCounter) \ |
| V(MaxSmi) \ |
| V(NewObject) \ |
| V(StringMaxLength) \ |
| V(StringToArray) \ |
| V(AsyncFunctionEnter) \ |
| V(AsyncFunctionReject) \ |
| V(AsyncFunctionResolve) \ |
| /* Test */ \ |
| V(GetOptimizationStatus) \ |
| V(OptimizeFunctionOnNextCall) \ |
| V(OptimizeOsr) \ |
| V(UnblockConcurrentRecompilation) |
| |
| // Intrinsics with inline versions have to be allowlisted here a second time. |
| #define INLINE_INTRINSIC_ALLOWLIST(V) \ |
| V(Call) \ |
| V(IsJSReceiver) \ |
| V(AsyncFunctionEnter) \ |
| V(AsyncFunctionReject) \ |
| V(AsyncFunctionResolve) |
| |
| #define CASE(Name) case Runtime::k##Name: |
| #define INLINE_CASE(Name) case Runtime::kInline##Name: |
| switch (id) { |
| INTRINSIC_ALLOWLIST(CASE) |
| INLINE_INTRINSIC_ALLOWLIST(INLINE_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 INLINE_CASE |
| #undef INTRINSIC_ALLOWLIST |
| #undef INLINE_INTRINSIC_ALLOWLIST |
| } |
| |
| bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) { |
| using interpreter::Bytecode; |
| using interpreter::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) { |
| // Allowlist for bytecodes. |
| // Loads. |
| case Bytecode::kLdaLookupSlot: |
| case Bytecode::kLdaGlobal: |
| case Bytecode::kLdaNamedProperty: |
| case Bytecode::kLdaNamedPropertyNoFeedback: |
| case Bytecode::kLdaKeyedProperty: |
| case Bytecode::kLdaGlobalInsideTypeof: |
| case Bytecode::kLdaLookupSlotInsideTypeof: |
| case Bytecode::kGetIterator: |
| // 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::kCreateArrayFromIterable: |
| 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::kIncBlockCounter: // Coverage counters. |
| case Bytecode::kForInEnumerate: |
| case Bytecode::kForInPrepare: |
| case Bytecode::kForInContinue: |
| case Bytecode::kForInNext: |
| case Bytecode::kForInStep: |
| case Bytecode::kJumpLoop: |
| case Bytecode::kThrow: |
| case Bytecode::kReThrow: |
| case Bytecode::kThrowReferenceErrorIfHole: |
| case Bytecode::kThrowSuperNotCalledIfHole: |
| case Bytecode::kThrowSuperAlreadyCalledIfNotHole: |
| case Bytecode::kIllegal: |
| case Bytecode::kCallJSRuntime: |
| case Bytecode::kReturn: |
| case Bytecode::kSetPendingMessage: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| DebugInfo::SideEffectState BuiltinGetSideEffectState(Builtins::Name id) { |
| switch (id) { |
| // Allowlist 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::kObjectKeys: |
| case Builtins::kObjectPrototypeValueOf: |
| case Builtins::kObjectValues: |
| case Builtins::kObjectPrototypeHasOwnProperty: |
| case Builtins::kObjectPrototypeIsPrototypeOf: |
| case Builtins::kObjectPrototypePropertyIsEnumerable: |
| case Builtins::kObjectPrototypeToString: |
| case Builtins::kObjectPrototypeToLocaleString: |
| // 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::kArrayPrototypeJoin: |
| case Builtins::kArrayPrototypeKeys: |
| case Builtins::kArrayPrototypeLastIndexOf: |
| case Builtins::kArrayPrototypeSlice: |
| case Builtins::kArrayPrototypeToLocaleString: |
| case Builtins::kArrayPrototypeToString: |
| 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::kTypedArrayPrototypeJoin: |
| case Builtins::kTypedArrayPrototypeIndexOf: |
| case Builtins::kTypedArrayPrototypeLastIndexOf: |
| case Builtins::kTypedArrayPrototypeSlice: |
| case Builtins::kTypedArrayPrototypeSubArray: |
| case Builtins::kTypedArrayPrototypeEvery: |
| case Builtins::kTypedArrayPrototypeSome: |
| case Builtins::kTypedArrayPrototypeToLocaleString: |
| 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: |
| #ifdef V8_INTL_SUPPORT |
| case Builtins::kDatePrototypeToLocaleString: |
| case Builtins::kDatePrototypeToLocaleDateString: |
| case Builtins::kDatePrototypeToLocaleTimeString: |
| #endif |
| 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::kWeakMapPrototypeHas: |
| // 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::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::kNumberPrototypeToLocaleString: |
| 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::kWeakSetPrototypeHas: |
| // 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::kStringPrototypeMatchAll: |
| 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: |
| // RegExp builtins. |
| case Builtins::kRegExpConstructor: |
| // Internal. |
| case Builtins::kStrictPoisonPillThrower: |
| case Builtins::kAllocateInYoungGeneration: |
| case Builtins::kAllocateInOldGeneration: |
| case Builtins::kAllocateRegularInYoungGeneration: |
| case Builtins::kAllocateRegularInOldGeneration: |
| 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::kArrayPrototypeReverse: |
| case Builtins::kArrayPrototypeShift: |
| case Builtins::kArrayPrototypeUnshift: |
| case Builtins::kArrayPrototypeSort: |
| case Builtins::kArrayPrototypeSplice: |
| 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::kRegExpPrototypeMatchAll: |
| 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) { |
| using interpreter::Bytecode; |
| switch (bytecode) { |
| case Bytecode::kStaNamedProperty: |
| case Bytecode::kStaNamedPropertyNoFeedback: |
| 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()); |
| DCHECK(!info->needs_script_context()); |
| if (info->HasBytecodeArray()) { |
| // Check bytecodes against allowlist. |
| 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 allowlist. |
| 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 allowlist. |
| int builtin_index = |
| info->HasBuiltinId() ? info->builtin_id() : Builtins::kNoBuiltinId; |
| if (!Builtins::IsBuiltinId(builtin_index)) |
| return DebugInfo::kHasSideEffects; |
| DebugInfo::SideEffectState state = |
| BuiltinGetSideEffectState(static_cast<Builtins::Name>(builtin_index)); |
| return state; |
| } |
| |
| return DebugInfo::kHasSideEffects; |
| } |
| |
| #ifdef DEBUG |
| static bool TransitivelyCalledBuiltinHasNoSideEffect(Builtins::Name caller, |
| Builtins::Name callee) { |
| switch (callee) { |
| // Transitively called Builtins: |
| case Builtins::kAbort: |
| case Builtins::kAbortCSAAssert: |
| case Builtins::kAdaptorWithBuiltinExitFrame: |
| case Builtins::kArrayConstructorImpl: |
| case Builtins::kArrayEveryLoopContinuation: |
| case Builtins::kArrayFilterLoopContinuation: |
| case Builtins::kArrayFindIndexLoopContinuation: |
| case Builtins::kArrayFindLoopContinuation: |
| case Builtins::kArrayForEachLoopContinuation: |
| case Builtins::kArrayIncludesHoleyDoubles: |
| case Builtins::kArrayIncludesPackedDoubles: |
| case Builtins::kArrayIncludesSmiOrObject: |
| case Builtins::kArrayIndexOfHoleyDoubles: |
| case Builtins::kArrayIndexOfPackedDoubles: |
| case Builtins::kArrayIndexOfSmiOrObject: |
| case Builtins::kArrayMapLoopContinuation: |
| case Builtins::kArrayReduceLoopContinuation: |
| case Builtins::kArrayReduceRightLoopContinuation: |
| case Builtins::kArraySomeLoopContinuation: |
| case Builtins::kArrayTimSort: |
| case Builtins::kCall_ReceiverIsAny: |
| case Builtins::kCall_ReceiverIsNotNullOrUndefined: |
| case Builtins::kCall_ReceiverIsNullOrUndefined: |
| case Builtins::kCallWithArrayLike: |
| case Builtins::kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit: |
| case Builtins::kCEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit: |
| case Builtins::kCEntry_Return1_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit: |
| case Builtins::kCEntry_Return1_SaveFPRegs_ArgvOnStack_NoBuiltinExit: |
| case Builtins::kCEntry_Return1_SaveFPRegs_ArgvOnStack_BuiltinExit: |
| case Builtins::kCEntry_Return2_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit: |
| case Builtins::kCEntry_Return2_DontSaveFPRegs_ArgvOnStack_BuiltinExit: |
| case Builtins::kCEntry_Return2_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit: |
| case Builtins::kCEntry_Return2_SaveFPRegs_ArgvOnStack_NoBuiltinExit: |
| case Builtins::kCEntry_Return2_SaveFPRegs_ArgvOnStack_BuiltinExit: |
| case Builtins::kCloneFastJSArray: |
| case Builtins::kConstruct: |
| case Builtins::kConvertToLocaleString: |
| case Builtins::kCreateTypedArray: |
| case Builtins::kDirectCEntry: |
| case Builtins::kDoubleToI: |
| case Builtins::kExtractFastJSArray: |
| case Builtins::kFastNewObject: |
| case Builtins::kFindOrderedHashMapEntry: |
| case Builtins::kFlatMapIntoArray: |
| case Builtins::kFlattenIntoArray: |
| case Builtins::kGetProperty: |
| case Builtins::kHasProperty: |
| case Builtins::kCreateHTML: |
| case Builtins::kNonNumberToNumber: |
| case Builtins::kNonPrimitiveToPrimitive_Number: |
| case Builtins::kNumberToString: |
| case Builtins::kObjectToString: |
| case Builtins::kOrderedHashTableHealIndex: |
| case Builtins::kOrdinaryToPrimitive_Number: |
| case Builtins::kOrdinaryToPrimitive_String: |
| case Builtins::kParseInt: |
| case Builtins::kProxyHasProperty: |
| case Builtins::kProxyIsExtensible: |
| case Builtins::kProxyGetPrototypeOf: |
| case Builtins::kRecordWrite: |
| case Builtins::kStringAdd_CheckNone: |
| case Builtins::kStringEqual: |
| case Builtins::kStringIndexOf: |
| case Builtins::kStringRepeat: |
| case Builtins::kToInteger: |
| case Builtins::kToLength: |
| case Builtins::kToName: |
| case Builtins::kToObject: |
| case Builtins::kToString: |
| case Builtins::kWeakMapLookupHashIndex: |
| return true; |
| case Builtins::kJoinStackPop: |
| case Builtins::kJoinStackPush: |
| switch (caller) { |
| case Builtins::kArrayPrototypeJoin: |
| case Builtins::kArrayPrototypeToLocaleString: |
| case Builtins::kTypedArrayPrototypeJoin: |
| case Builtins::kTypedArrayPrototypeToLocaleString: |
| return true; |
| default: |
| return false; |
| } |
| case Builtins::kFastCreateDataProperty: |
| switch (caller) { |
| case Builtins::kArrayPrototypeSlice: |
| case Builtins::kArrayFilter: |
| return true; |
| default: |
| return false; |
| } |
| case Builtins::kSetProperty: |
| switch (caller) { |
| case Builtins::kArrayPrototypeSlice: |
| case Builtins::kTypedArrayPrototypeMap: |
| case Builtins::kStringPrototypeMatchAll: |
| return true; |
| default: |
| return false; |
| } |
| default: |
| return false; |
| } |
| } |
| |
| // static |
| void DebugEvaluate::VerifyTransitiveBuiltins(Isolate* isolate) { |
| // TODO(yangguo): also check runtime calls. |
| bool failed = false; |
| bool sanity_check = false; |
| for (int i = 0; i < Builtins::builtin_count; i++) { |
| Builtins::Name caller = static_cast<Builtins::Name>(i); |
| DebugInfo::SideEffectState state = BuiltinGetSideEffectState(caller); |
| if (state != DebugInfo::kHasNoSideEffect) continue; |
| Code code = isolate->builtins()->builtin(caller); |
| int mode = RelocInfo::ModeMask(RelocInfo::CODE_TARGET) | |
| RelocInfo::ModeMask(RelocInfo::RELATIVE_CODE_TARGET); |
| |
| for (RelocIterator it(code, mode); !it.done(); it.next()) { |
| RelocInfo* rinfo = it.rinfo(); |
| DCHECK(RelocInfo::IsCodeTargetMode(rinfo->rmode())); |
| Code callee_code = isolate->heap()->GcSafeFindCodeForInnerPointer( |
| rinfo->target_address()); |
| if (!callee_code.is_builtin()) continue; |
| Builtins::Name callee = |
| static_cast<Builtins::Name>(callee_code.builtin_index()); |
| if (BuiltinGetSideEffectState(callee) == DebugInfo::kHasNoSideEffect) { |
| continue; |
| } |
| if (TransitivelyCalledBuiltinHasNoSideEffect(caller, callee)) { |
| sanity_check = true; |
| continue; |
| } |
| PrintF("Allowlisted builtin %s calls non-allowlisted builtin %s\n", |
| Builtins::name(caller), Builtins::name(callee)); |
| failed = true; |
| } |
| } |
| CHECK(!failed); |
| #if defined(V8_TARGET_ARCH_PPC) || defined(V8_TARGET_ARCH_PPC64) || \ |
| defined(V8_TARGET_ARCH_MIPS64) |
| // Isolate-independent builtin calls and jumps do not emit reloc infos |
| // on PPC. We try to avoid using PC relative code due to performance |
| // issue with especially older hardwares. |
| // MIPS64 doesn't have PC relative code currently. |
| // TODO(mips): Add PC relative code to MIPS64. |
| USE(sanity_check); |
| #else |
| CHECK(sanity_check); |
| #endif |
| } |
| #endif // DEBUG |
| |
| // 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)) it.ApplyDebugBreak(); |
| } |
| } |
| |
| } // namespace internal |
| } // namespace v8 |