/* * Copyright (C) 2013-2022 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "JITOperations.h" #if ENABLE(JIT) #include "ArithProfile.h" #include "ArrayConstructor.h" #include "CacheableIdentifierInlines.h" #include "CodeBlockInlines.h" #include "CommonSlowPathsInlines.h" #include "DFGDriver.h" #include "DFGOSREntry.h" #include "DFGThunks.h" #include "Debugger.h" #include "ExceptionFuzz.h" #include "FrameTracers.h" #include "GetterSetter.h" #include "ICStats.h" #include "Interpreter.h" #include "JIT.h" #include "JITExceptions.h" #include "JITToDFGDeferredCompilationCallback.h" #include "JITWorklist.h" #include "JSAsyncFunction.h" #include "JSAsyncGenerator.h" #include "JSAsyncGeneratorFunction.h" #include "JSCInlines.h" #include "JSCPtrTag.h" #include "JSGeneratorFunction.h" #include "JSGlobalObjectFunctions.h" #include "JSInternalPromise.h" #include "JSLexicalEnvironment.h" #include "JSRemoteFunction.h" #include "JSWithScope.h" #include "LLIntEntrypoint.h" #include "ObjectConstructor.h" #include "PropertyName.h" #include "RegExpObject.h" #include "RepatchInlines.h" #include "ShadowChicken.h" #include "StructureStubInfo.h" #include "SuperSampler.h" #include "ThunkGenerators.h" #include "TypeProfilerLog.h" #include "VMInlines.h" #include "VMTrapsInlines.h" IGNORE_WARNINGS_BEGIN("frame-address") namespace JSC { ALWAYS_INLINE JSValue profiledAdd(JSGlobalObject* globalObject, JSValue op1, JSValue op2, BinaryArithProfile& arithProfile) { arithProfile.observeLHSAndRHS(op1, op2); JSValue result = jsAdd(globalObject, op1, op2); arithProfile.observeResult(result); return result; } #if COMPILER(MSVC) extern "C" void * _ReturnAddress(void); #pragma intrinsic(_ReturnAddress) #define OUR_RETURN_ADDRESS _ReturnAddress() #else // FIXME (see rdar://72897291): Work around a Clang bug where __builtin_return_address() // sometimes gives us a signed pointer, and sometimes does not. #define OUR_RETURN_ADDRESS removeCodePtrTag(__builtin_return_address(0)) #endif JSC_DEFINE_JIT_OPERATION(operationThrowStackOverflowError, void, (CodeBlock* codeBlock)) { // We pass in our own code block, because the callframe hasn't been populated. VM& vm = codeBlock->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); callFrame->convertToStackOverflowFrame(vm, codeBlock); throwStackOverflowError(codeBlock->globalObject(), scope); } JSC_DEFINE_JIT_OPERATION(operationThrowStackOverflowErrorFromThunk, void, (JSGlobalObject* globalObject)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); throwStackOverflowError(globalObject, scope); genericUnwind(vm, callFrame); ASSERT(vm.targetMachinePCForThrow); } static JSValue getWrappedValue(JSGlobalObject* globalObject, JSGlobalObject* targetGlobalObject, JSValue value) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); if (!value.isObject()) RELEASE_AND_RETURN(scope, value); if (value.isCallable()) RELEASE_AND_RETURN(scope, JSRemoteFunction::tryCreate(targetGlobalObject, vm, static_cast(value.asCell()))); throwTypeError(globalObject, scope, "value passing between realms must be callable or primitive"_s); return jsUndefined(); } JSC_DEFINE_JIT_OPERATION(operationGetWrappedValueForTarget, EncodedJSValue, (JSRemoteFunction* callee, EncodedJSValue encodedValue)) { JSGlobalObject* globalObject = callee->globalObject(); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); ASSERT(isRemoteFunction(callee)); JSGlobalObject* targetGlobalObject = callee->targetFunction()->globalObject(); return JSValue::encode(getWrappedValue(globalObject, targetGlobalObject, JSValue::decode(encodedValue))); } JSC_DEFINE_JIT_OPERATION(operationGetWrappedValueForCaller, EncodedJSValue, (JSRemoteFunction* callee, EncodedJSValue encodedValue)) { JSGlobalObject* globalObject = callee->globalObject(); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); ASSERT(isRemoteFunction(callee)); auto scope = DECLARE_THROW_SCOPE(vm); RELEASE_AND_RETURN(scope, JSValue::encode(getWrappedValue(globalObject, globalObject, JSValue::decode(encodedValue)))); } JSC_DEFINE_JIT_OPERATION(operationMaterializeRemoteFunctionTargetCode, SlowPathReturnType, (JSRemoteFunction* callee)) { JSGlobalObject* globalObject = callee->globalObject(); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto throwScope = DECLARE_THROW_SCOPE(vm); ASSERT(isRemoteFunction(callee)); auto* targetFunction = jsCast(callee->targetFunction()); // We call this function only when JSRemoteFunction's target is JSFunction. ExecutableBase* executable = targetFunction->executable(); // Force the executable to cache its arity entrypoint. { DeferTraps deferTraps(vm); // We can't jettison any code until after we link the call. CodeBlock* codeBlockSlot = nullptr; if (!executable->isHostFunction()) { JSScope* scope = targetFunction->scopeUnchecked(); FunctionExecutable* functionExecutable = static_cast(executable); functionExecutable->prepareForExecution(vm, targetFunction, scope, CodeForCall, codeBlockSlot); RETURN_IF_EXCEPTION(throwScope, encodeResult(nullptr, nullptr)); } return encodeResult(executable->entrypointFor(CodeForCall, MustCheckArity).taggedPtr(), codeBlockSlot); } } JSC_DEFINE_JIT_OPERATION(operationThrowRemoteFunctionException, EncodedJSValue, (JSRemoteFunction* callee)) { JSGlobalObject* globalObject = callee->globalObject(); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); ASSERT(isRemoteFunction(callee)); auto scope = DECLARE_THROW_SCOPE(vm); Exception* exception = scope.exception(); // We should only be here when "rethrowing" an exception RELEASE_ASSERT(exception); if (UNLIKELY(vm.isTerminationException(exception))) { scope.release(); return { }; } JSValue exceptionValue = exception->value(); scope.clearException(); String exceptionString = exceptionValue.toWTFString(globalObject); Exception* toStringException = scope.exception(); if (UNLIKELY(toStringException && vm.isTerminationException(toStringException))) { scope.release(); return { }; } scope.clearException(); if (exceptionString.length()) return throwVMTypeError(globalObject, scope, exceptionString); return throwVMTypeError(globalObject, scope); } JSC_DEFINE_JIT_OPERATION(operationThrowIteratorResultIsNotObject, void, (JSGlobalObject* globalObject)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); throwTypeError(globalObject, scope, "Iterator result interface is not an object."_s); } JSC_DEFINE_JIT_OPERATION(operationTryGetById, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); stubInfo->tookSlowPath = true; JSValue baseValue = JSValue::decode(base); PropertySlot slot(baseValue, PropertySlot::InternalMethodType::VMInquiry, &vm); baseValue.getPropertySlot(globalObject, ident, slot); return JSValue::encode(slot.getPureResult()); } JSC_DEFINE_JIT_OPERATION(operationTryGetByIdGeneric, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); JSValue baseValue = JSValue::decode(base); PropertySlot slot(baseValue, PropertySlot::InternalMethodType::VMInquiry, &vm); baseValue.getPropertySlot(globalObject, ident, slot); return JSValue::encode(slot.getPureResult()); } JSC_DEFINE_JIT_OPERATION(operationTryGetByIdOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); JSValue baseValue = JSValue::decode(base); PropertySlot slot(baseValue, PropertySlot::InternalMethodType::VMInquiry, &vm); baseValue.getPropertySlot(globalObject, ident, slot); RETURN_IF_EXCEPTION(scope, encodedJSValue()); CodeBlock* codeBlock = callFrame->codeBlock(); if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(), identifier) && !slot.isTaintedByOpaqueObject() && (slot.isCacheableValue() || slot.isCacheableGetter() || slot.isUnset())) repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::TryById); return JSValue::encode(slot.getPureResult()); } JSC_DEFINE_JIT_OPERATION(operationGetByIdDirect, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); stubInfo->tookSlowPath = true; JSValue baseValue = JSValue::decode(base); PropertySlot slot(baseValue, PropertySlot::InternalMethodType::GetOwnProperty); bool found = baseValue.getOwnPropertySlot(globalObject, ident, slot); RETURN_IF_EXCEPTION(scope, encodedJSValue()); RELEASE_AND_RETURN(scope, JSValue::encode(found ? slot.getValue(globalObject, ident) : jsUndefined())); } JSC_DEFINE_JIT_OPERATION(operationGetByIdDirectGeneric, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); JSValue baseValue = JSValue::decode(base); PropertySlot slot(baseValue, PropertySlot::InternalMethodType::GetOwnProperty); bool found = baseValue.getOwnPropertySlot(globalObject, ident, slot); RETURN_IF_EXCEPTION(scope, encodedJSValue()); RELEASE_AND_RETURN(scope, JSValue::encode(found ? slot.getValue(globalObject, ident) : jsUndefined())); } JSC_DEFINE_JIT_OPERATION(operationGetByIdDirectOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); JSValue baseValue = JSValue::decode(base); PropertySlot slot(baseValue, PropertySlot::InternalMethodType::GetOwnProperty); bool found = baseValue.getOwnPropertySlot(globalObject, ident, slot); RETURN_IF_EXCEPTION(scope, encodedJSValue()); CodeBlock* codeBlock = callFrame->codeBlock(); if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(), identifier)) repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::ByIdDirect); RELEASE_AND_RETURN(scope, JSValue::encode(found ? slot.getValue(globalObject, ident) : jsUndefined())); } JSC_DEFINE_JIT_OPERATION(operationGetById, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); stubInfo->tookSlowPath = true; JSValue baseValue = JSValue::decode(base); PropertySlot slot(baseValue, PropertySlot::InternalMethodType::Get); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); JSValue result = baseValue.get(globalObject, ident, slot); LOG_IC((ICEvent::OperationGetById, baseValue.classInfoOrNull(), ident, baseValue == slot.slotBase())); return JSValue::encode(result); } JSC_DEFINE_JIT_OPERATION(operationGetByIdGeneric, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue baseValue = JSValue::decode(base); PropertySlot slot(baseValue, PropertySlot::InternalMethodType::Get); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); JSValue result = baseValue.get(globalObject, ident, slot); LOG_IC((ICEvent::OperationGetByIdGeneric, baseValue.classInfoOrNull(), ident, baseValue == slot.slotBase())); return JSValue::encode(result); } JSC_DEFINE_JIT_OPERATION(operationGetByIdOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); JSValue baseValue = JSValue::decode(base); return JSValue::encode(baseValue.getPropertySlot(globalObject, ident, [&] (bool found, PropertySlot& slot) -> JSValue { LOG_IC((ICEvent::OperationGetByIdOptimize, baseValue.classInfoOrNull(), ident, baseValue == slot.slotBase())); CodeBlock* codeBlock = callFrame->codeBlock(); if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(), identifier)) repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::ById); return found ? slot.getValue(globalObject, ident) : jsUndefined(); })); } JSC_DEFINE_JIT_OPERATION(operationGetByIdWithThis, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, EncodedJSValue thisEncoded, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); stubInfo->tookSlowPath = true; JSValue baseValue = JSValue::decode(base); JSValue thisValue = JSValue::decode(thisEncoded); PropertySlot slot(thisValue, PropertySlot::InternalMethodType::Get); return JSValue::encode(baseValue.get(globalObject, ident, slot)); } JSC_DEFINE_JIT_OPERATION(operationGetByIdWithThisGeneric, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, EncodedJSValue thisEncoded, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); JSValue baseValue = JSValue::decode(base); JSValue thisValue = JSValue::decode(thisEncoded); PropertySlot slot(thisValue, PropertySlot::InternalMethodType::Get); return JSValue::encode(baseValue.get(globalObject, ident, slot)); } JSC_DEFINE_JIT_OPERATION(operationGetByIdWithThisOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, EncodedJSValue thisEncoded, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); JSValue baseValue = JSValue::decode(base); JSValue thisValue = JSValue::decode(thisEncoded); PropertySlot slot(thisValue, PropertySlot::InternalMethodType::Get); return JSValue::encode(baseValue.getPropertySlot(globalObject, ident, slot, [&] (bool found, PropertySlot& slot) -> JSValue { LOG_IC((ICEvent::OperationGetByIdWithThisOptimize, baseValue.classInfoOrNull(), ident, baseValue == slot.slotBase())); CodeBlock* codeBlock = callFrame->codeBlock(); if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(), identifier)) repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::ByIdWithThis); return found ? slot.getValue(globalObject, ident) : jsUndefined(); })); } JSC_DEFINE_JIT_OPERATION(operationInByIdGeneric, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); stubInfo->tookSlowPath = true; CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); JSValue baseValue = JSValue::decode(base); if (!baseValue.isObject()) { throwException(globalObject, scope, createInvalidInParameterError(globalObject, baseValue)); return JSValue::encode(jsUndefined()); } JSObject* baseObject = asObject(baseValue); LOG_IC((ICEvent::OperationInByIdGeneric, baseObject->classInfo(), ident)); scope.release(); PropertySlot slot(baseObject, PropertySlot::InternalMethodType::HasProperty); return JSValue::encode(jsBoolean(baseObject->getPropertySlot(globalObject, ident, slot))); } JSC_DEFINE_JIT_OPERATION(operationInByIdOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); JSValue baseValue = JSValue::decode(base); if (!baseValue.isObject()) { throwException(globalObject, scope, createInvalidInParameterError(globalObject, baseValue)); return JSValue::encode(jsUndefined()); } JSObject* baseObject = asObject(baseValue); LOG_IC((ICEvent::OperationInByIdOptimize, baseObject->classInfo(), ident)); PropertySlot slot(baseObject, PropertySlot::InternalMethodType::HasProperty); bool found = baseObject->getPropertySlot(globalObject, ident, slot); RETURN_IF_EXCEPTION(scope, { }); CodeBlock* codeBlock = callFrame->codeBlock(); if (stubInfo->considerCachingBy(vm, codeBlock, baseObject->structure(), identifier)) repatchInBy(globalObject, codeBlock, baseObject, identifier, found, slot, *stubInfo, InByKind::ById); return JSValue::encode(jsBoolean(found)); } JSC_DEFINE_JIT_OPERATION(operationInByValOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, ArrayProfile* arrayProfile, EncodedJSValue encodedBase, EncodedJSValue encodedKey)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); JSValue baseValue = JSValue::decode(encodedBase); if (!baseValue.isObject()) { throwException(globalObject, scope, createInvalidInParameterError(globalObject, baseValue)); return encodedJSValue(); } JSObject* baseObject = asObject(baseValue); if (arrayProfile) arrayProfile->observeStructure(baseObject->structure()); JSValue key = JSValue::decode(encodedKey); uint32_t i; if (key.getUInt32(i)) { // FIXME: InByVal should have inline caching for integer indices too, as GetByVal does. // https://bugs.webkit.org/show_bug.cgi?id=226619 if (arrayProfile) arrayProfile->observeIndexedRead(baseObject, i); RELEASE_AND_RETURN(scope, JSValue::encode(jsBoolean(baseObject->hasProperty(globalObject, i)))); } const Identifier propertyName = key.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, encodedJSValue()); PropertySlot slot(baseObject, PropertySlot::InternalMethodType::HasProperty); bool found = baseObject->getPropertySlot(globalObject, propertyName, slot); RETURN_IF_EXCEPTION(scope, encodedJSValue()); if (CacheableIdentifier::isCacheableIdentifierCell(key) && (key.isSymbol() || !parseIndex(propertyName))) { CodeBlock* codeBlock = callFrame->codeBlock(); CacheableIdentifier identifier = CacheableIdentifier::createFromCell(key.asCell()); if (stubInfo->considerCachingBy(vm, codeBlock, baseObject->structure(), identifier)) repatchInBy(globalObject, codeBlock, baseObject, identifier, found, slot, *stubInfo, InByKind::ByVal); } return JSValue::encode(jsBoolean(found)); } JSC_DEFINE_JIT_OPERATION(operationInByValGeneric, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, ArrayProfile* arrayProfile, EncodedJSValue base, EncodedJSValue key)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); stubInfo->tookSlowPath = true; return JSValue::encode(jsBoolean(CommonSlowPaths::opInByVal(globalObject, JSValue::decode(base), JSValue::decode(key), arrayProfile))); } JSC_DEFINE_JIT_OPERATION(operationHasPrivateNameOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBase, EncodedJSValue encodedProperty)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); JSValue baseValue = JSValue::decode(encodedBase); if (!baseValue.isObject()) { throwException(globalObject, scope, createInvalidInParameterError(globalObject, baseValue)); return encodedJSValue(); } JSObject* baseObject = asObject(baseValue); JSValue propertyValue = JSValue::decode(encodedProperty); ASSERT(propertyValue.isSymbol()); auto property = propertyValue.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, { }); PropertySlot slot(baseObject, PropertySlot::InternalMethodType::HasProperty); bool found = JSObject::getPrivateFieldSlot(baseObject, globalObject, property, slot); ASSERT(CacheableIdentifier::isCacheableIdentifierCell(propertyValue)); CodeBlock* codeBlock = callFrame->codeBlock(); CacheableIdentifier identifier = CacheableIdentifier::createFromCell(propertyValue.asCell()); if (stubInfo->considerCachingBy(vm, codeBlock, baseObject->structure(), identifier)) repatchInBy(globalObject, codeBlock, baseObject, identifier, found, slot, *stubInfo, InByKind::PrivateName); return JSValue::encode(jsBoolean(found)); } JSC_DEFINE_JIT_OPERATION(operationHasPrivateNameGeneric, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBase, EncodedJSValue encodedProperty)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); stubInfo->tookSlowPath = true; JSValue baseValue = JSValue::decode(encodedBase); if (!baseValue.isObject()) { throwException(globalObject, scope, createInvalidInParameterError(globalObject, baseValue)); return encodedJSValue(); } JSValue propertyValue = JSValue::decode(encodedProperty); ASSERT(propertyValue.isSymbol()); auto property = propertyValue.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, { }); return JSValue::encode(jsBoolean(asObject(baseValue)->hasPrivateField(globalObject, property))); } JSC_DEFINE_JIT_OPERATION(operationHasPrivateBrandOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBase, EncodedJSValue encodedBrand)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); JSValue baseValue = JSValue::decode(encodedBase); if (!baseValue.isObject()) { throwException(globalObject, scope, createInvalidInParameterError(globalObject, baseValue)); return encodedJSValue(); } JSObject* baseObject = asObject(baseValue); JSValue brand = JSValue::decode(encodedBrand); bool found = asObject(baseValue)->hasPrivateBrand(globalObject, brand); ASSERT(CacheableIdentifier::isCacheableIdentifierCell(brand)); CodeBlock* codeBlock = callFrame->codeBlock(); CacheableIdentifier identifier = CacheableIdentifier::createFromCell(brand.asCell()); if (stubInfo->considerCachingBy(vm, codeBlock, baseObject->structure(), identifier)) repatchHasPrivateBrand(globalObject, codeBlock, baseObject, identifier, found, *stubInfo); return JSValue::encode(jsBoolean(found)); } JSC_DEFINE_JIT_OPERATION(operationHasPrivateBrandGeneric, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBase, EncodedJSValue encodedBrand)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); stubInfo->tookSlowPath = true; JSValue baseValue = JSValue::decode(encodedBase); if (!baseValue.isObject()) { throwException(globalObject, scope, createInvalidInParameterError(globalObject, baseValue)); return encodedJSValue(); } return JSValue::encode(jsBoolean(asObject(baseValue)->hasPrivateBrand(globalObject, JSValue::decode(encodedBrand)))); } JSC_DEFINE_JIT_OPERATION(operationPutByIdStrict, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); stubInfo->tookSlowPath = true; JSValue baseValue = JSValue::decode(encodedBase); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); PutPropertySlot slot(baseValue, true, callFrame->codeBlock()->putByIdContext()); baseValue.putInline(globalObject, ident, JSValue::decode(encodedValue), slot); LOG_IC((ICEvent::OperationPutByIdStrict, baseValue.classInfoOrNull(), ident, slot.base() == baseValue)); } JSC_DEFINE_JIT_OPERATION(operationPutByIdNonStrict, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); stubInfo->tookSlowPath = true; JSValue baseValue = JSValue::decode(encodedBase); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); PutPropertySlot slot(baseValue, false, callFrame->codeBlock()->putByIdContext()); baseValue.putInline(globalObject, ident, JSValue::decode(encodedValue), slot); LOG_IC((ICEvent::OperationPutByIdNonStrict, baseValue.classInfoOrNull(), ident, slot.base() == baseValue)); } JSC_DEFINE_JIT_OPERATION(operationPutByIdDirectStrict, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); stubInfo->tookSlowPath = true; JSValue baseValue = JSValue::decode(encodedBase); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); PutPropertySlot slot(baseValue, true, callFrame->codeBlock()->putByIdContext()); CommonSlowPaths::putDirectWithReify(vm, globalObject, asObject(baseValue), ident, JSValue::decode(encodedValue), slot); LOG_IC((ICEvent::OperationPutByIdDirectStrict, baseValue.classInfoOrNull(), ident, slot.base() == baseValue)); } JSC_DEFINE_JIT_OPERATION(operationPutByIdDirectNonStrict, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); stubInfo->tookSlowPath = true; JSValue baseValue = JSValue::decode(encodedBase); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); PutPropertySlot slot(baseValue, false, callFrame->codeBlock()->putByIdContext()); CommonSlowPaths::putDirectWithReify(vm, globalObject, asObject(baseValue), ident, JSValue::decode(encodedValue), slot); LOG_IC((ICEvent::OperationPutByIdDirectNonStrict, baseValue.classInfoOrNull(), ident, slot.base() == baseValue)); } JSC_DEFINE_JIT_OPERATION(operationPutByIdStrictOptimize, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); AccessType accessType = static_cast(stubInfo->accessType); JSValue value = JSValue::decode(encodedValue); JSValue baseValue = JSValue::decode(encodedBase); CodeBlock* codeBlock = callFrame->codeBlock(); PutPropertySlot slot(baseValue, true, codeBlock->putByIdContext()); Structure* structure = CommonSlowPaths::originalStructureBeforePut(baseValue); baseValue.putInline(globalObject, ident, value, slot); LOG_IC((ICEvent::OperationPutByIdStrictOptimize, baseValue.classInfoOrNull(), ident, slot.base() == baseValue)); RETURN_IF_EXCEPTION(scope, void()); if (accessType != static_cast(stubInfo->accessType)) return; if (stubInfo->considerCachingBy(vm, codeBlock, structure, identifier)) repatchPutBy(globalObject, codeBlock, baseValue, structure, identifier, slot, *stubInfo, PutByKind::ById, PutKind::NotDirect); } JSC_DEFINE_JIT_OPERATION(operationPutByIdNonStrictOptimize, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); AccessType accessType = static_cast(stubInfo->accessType); JSValue value = JSValue::decode(encodedValue); JSValue baseValue = JSValue::decode(encodedBase); CodeBlock* codeBlock = callFrame->codeBlock(); PutPropertySlot slot(baseValue, false, codeBlock->putByIdContext()); Structure* structure = CommonSlowPaths::originalStructureBeforePut(baseValue); baseValue.putInline(globalObject, ident, value, slot); LOG_IC((ICEvent::OperationPutByIdNonStrictOptimize, baseValue.classInfoOrNull(), ident, slot.base() == baseValue)); RETURN_IF_EXCEPTION(scope, void()); if (accessType != static_cast(stubInfo->accessType)) return; if (stubInfo->considerCachingBy(vm, codeBlock, structure, identifier)) repatchPutBy(globalObject, codeBlock, baseValue, structure, identifier, slot, *stubInfo, PutByKind::ById, PutKind::NotDirect); } JSC_DEFINE_JIT_OPERATION(operationPutByIdDirectStrictOptimize, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); AccessType accessType = static_cast(stubInfo->accessType); JSValue value = JSValue::decode(encodedValue); JSObject* baseObject = asObject(JSValue::decode(encodedBase)); CodeBlock* codeBlock = callFrame->codeBlock(); PutPropertySlot slot(baseObject, true, codeBlock->putByIdContext()); Structure* structure = nullptr; CommonSlowPaths::putDirectWithReify(vm, globalObject, baseObject, ident, value, slot, &structure); LOG_IC((ICEvent::OperationPutByIdDirectStrictOptimize, baseObject->classInfo(), ident, slot.base() == baseObject)); RETURN_IF_EXCEPTION(scope, void()); if (accessType != static_cast(stubInfo->accessType)) return; if (stubInfo->considerCachingBy(vm, codeBlock, structure, identifier)) repatchPutBy(globalObject, codeBlock, baseObject, structure, identifier, slot, *stubInfo, PutByKind::ById, PutKind::Direct); } JSC_DEFINE_JIT_OPERATION(operationPutByIdDirectNonStrictOptimize, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); AccessType accessType = static_cast(stubInfo->accessType); JSValue value = JSValue::decode(encodedValue); JSObject* baseObject = asObject(JSValue::decode(encodedBase)); CodeBlock* codeBlock = callFrame->codeBlock(); PutPropertySlot slot(baseObject, false, codeBlock->putByIdContext()); Structure* structure = nullptr; CommonSlowPaths::putDirectWithReify(vm, globalObject, baseObject, ident, value, slot, &structure); LOG_IC((ICEvent::OperationPutByIdDirectNonStrictOptimize, baseObject->classInfo(), ident, slot.base() == baseObject)); RETURN_IF_EXCEPTION(scope, void()); if (accessType != static_cast(stubInfo->accessType)) return; if (stubInfo->considerCachingBy(vm, codeBlock, structure, identifier)) repatchPutBy(globalObject, codeBlock, baseObject, structure, identifier, slot, *stubInfo, PutByKind::ById, PutKind::Direct); } template ALWAYS_INLINE static void setPrivateField(VM& vm, JSGlobalObject* globalObject, CallFrame* callFrame, JSValue baseValue, CacheableIdentifier identifier, JSValue value, PutPrivateFieldCallback callback) { auto scope = DECLARE_THROW_SCOPE(vm); Identifier ident = Identifier::fromUid(vm, identifier.uid()); ASSERT(ident.isPrivateName()); JSObject* baseObject = baseValue.toObject(globalObject); RETURN_IF_EXCEPTION(scope, void()); CodeBlock* codeBlock = callFrame->codeBlock(); Structure* oldStructure = baseObject->structure(); PutPropertySlot putSlot(baseObject, true, codeBlock->putByIdContext()); baseObject->setPrivateField(globalObject, ident, value, putSlot); RETURN_IF_EXCEPTION(scope, void()); callback(vm, codeBlock, oldStructure, putSlot, ident); } template ALWAYS_INLINE static void definePrivateField(VM& vm, JSGlobalObject* globalObject, CallFrame* callFrame, JSValue baseValue, CacheableIdentifier identifier, JSValue value, PutPrivateFieldCallback callback) { auto scope = DECLARE_THROW_SCOPE(vm); Identifier ident = Identifier::fromUid(vm, identifier.uid()); ASSERT(ident.isPrivateName()); JSObject* baseObject = baseValue.toObject(globalObject); RETURN_IF_EXCEPTION(scope, void()); CodeBlock* codeBlock = callFrame->codeBlock(); Structure* oldStructure = baseObject->structure(); PutPropertySlot putSlot(baseObject, true, codeBlock->putByIdContext()); baseObject->definePrivateField(globalObject, ident, value, putSlot); RETURN_IF_EXCEPTION(scope, void()); callback(vm, codeBlock, oldStructure, putSlot, ident); } JSC_DEFINE_JIT_OPERATION(operationPutByIdDefinePrivateFieldStrict, void, (JSGlobalObject* globalObject, StructureStubInfo*, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); JSValue value = JSValue::decode(encodedValue); JSValue baseValue = JSValue::decode(encodedBase); definePrivateField(vm, globalObject, callFrame, baseValue, identifier, value, [](VM&, CodeBlock*, Structure*, PutPropertySlot&, const Identifier&) { }); } JSC_DEFINE_JIT_OPERATION(operationPutByIdDefinePrivateFieldStrictOptimize, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); AccessType accessType = static_cast(stubInfo->accessType); JSValue value = JSValue::decode(encodedValue); JSValue baseValue = JSValue::decode(encodedBase); definePrivateField(vm, globalObject, callFrame, baseValue, identifier, value, [=](VM& vm, CodeBlock* codeBlock, Structure* oldStructure, PutPropertySlot& putSlot, const Identifier& ident) { JSObject* baseObject = asObject(baseValue); LOG_IC((ICEvent::OperationPutByIdDefinePrivateFieldStrictOptimize, baseObject->classInfo(), ident, putSlot.base() == baseObject)); ASSERT_UNUSED(accessType, accessType == static_cast(stubInfo->accessType)); if (stubInfo->considerCachingBy(vm, codeBlock, oldStructure, identifier)) repatchPutBy(globalObject, codeBlock, baseObject, oldStructure, identifier, putSlot, *stubInfo, PutByKind::ById, PutKind::DirectPrivateFieldDefine); }); } JSC_DEFINE_JIT_OPERATION(operationPutByIdSetPrivateFieldStrict, void, (JSGlobalObject* globalObject, StructureStubInfo*, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); JSValue value = JSValue::decode(encodedValue); JSValue baseValue = JSValue::decode(encodedBase); setPrivateField(vm, globalObject, callFrame, baseValue, identifier, value, [](VM&, CodeBlock*, Structure*, PutPropertySlot&, const Identifier&) { }); } JSC_DEFINE_JIT_OPERATION(operationPutByIdSetPrivateFieldStrictOptimize, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); AccessType accessType = static_cast(stubInfo->accessType); JSValue value = JSValue::decode(encodedValue); JSValue baseValue = JSValue::decode(encodedBase); setPrivateField(vm, globalObject, callFrame, baseValue, identifier, value, [&](VM& vm, CodeBlock* codeBlock, Structure* oldStructure, PutPropertySlot& putSlot, const Identifier& ident) { JSObject* baseObject = asObject(baseValue); LOG_IC((ICEvent::OperationPutByIdSetPrivateFieldStrictOptimize, baseObject->classInfo(), ident, putSlot.base() == baseObject)); ASSERT_UNUSED(accessType, accessType == static_cast(stubInfo->accessType)); if (stubInfo->considerCachingBy(vm, codeBlock, oldStructure, identifier)) repatchPutBy(globalObject, codeBlock, baseObject, oldStructure, identifier, putSlot, *stubInfo, PutByKind::ById, PutKind::DirectPrivateFieldSet); }); } static void putByVal(JSGlobalObject* globalObject, JSValue baseValue, JSValue subscript, JSValue value, ArrayProfile* arrayProfile, ECMAMode ecmaMode) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); if (std::optional index = subscript.tryGetAsUint32Index()) { uint32_t i = *index; if (baseValue.isObject()) { JSObject* object = asObject(baseValue); if (object->trySetIndexQuickly(vm, i, value, arrayProfile)) return; if (arrayProfile) arrayProfile->setOutOfBounds(); scope.release(); object->methodTable()->putByIndex(object, globalObject, i, value, ecmaMode.isStrict()); return; } scope.release(); baseValue.putByIndex(globalObject, i, value, ecmaMode.isStrict()); return; } if (subscript.isNumber()) { if (baseValue.isObject()) { if (arrayProfile) arrayProfile->setOutOfBounds(); } } auto property = subscript.toPropertyKey(globalObject); // Don't put to an object if toString threw an exception. RETURN_IF_EXCEPTION(scope, void()); scope.release(); PutPropertySlot slot(baseValue, ecmaMode.isStrict()); baseValue.putInline(globalObject, property, value, slot); } static void directPutByVal(JSGlobalObject* globalObject, JSObject* baseObject, JSValue subscript, JSValue value, ArrayProfile* arrayProfile, ECMAMode ecmaMode) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); if (std::optional maybeIndex = subscript.tryGetAsUint32Index()) { uint32_t index = *maybeIndex; switch (baseObject->indexingType()) { case ALL_INT32_INDEXING_TYPES: case ALL_DOUBLE_INDEXING_TYPES: case ALL_CONTIGUOUS_INDEXING_TYPES: case ALL_ARRAY_STORAGE_INDEXING_TYPES: if (index < baseObject->butterfly()->vectorLength()) break; FALLTHROUGH; default: if (arrayProfile) arrayProfile->setOutOfBounds(); break; } scope.release(); baseObject->putDirectIndex(globalObject, index, value, 0, ecmaMode.isStrict() ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow); return; } // Don't put to an object if toString threw an exception. auto property = subscript.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, void()); if (std::optional index = parseIndex(property)) { scope.release(); baseObject->putDirectIndex(globalObject, index.value(), value, 0, ecmaMode.isStrict() ? PutDirectIndexShouldThrow : PutDirectIndexShouldNotThrow); return; } scope.release(); PutPropertySlot slot(baseObject, ecmaMode.isStrict()); CommonSlowPaths::putDirectWithReify(vm, globalObject, baseObject, property, value, slot); } enum class OptimizationResult { NotOptimized, SeenOnce, Optimized, GiveUp, }; static ALWAYS_INLINE void putByValOptimize(JSGlobalObject* globalObject, CodeBlock* codeBlock, JSValue baseValue, JSValue subscript, JSValue value, StructureStubInfo* stubInfo, ArrayProfile* profile, ECMAMode ecmaMode) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); if (baseValue.isObject()) { JSObject* baseObject = asObject(baseValue); if (!isCopyOnWrite(baseObject->indexingMode()) && subscript.isInt32()) { Structure* structure = baseObject->structure(); if (stubInfo->considerCachingGeneric(vm, codeBlock, structure)) { if (profile) { ConcurrentJSLocker locker(codeBlock->m_lock); profile->computeUpdatedPrediction(locker, codeBlock, structure); } repatchArrayPutByVal(globalObject, codeBlock, baseValue, subscript, *stubInfo, PutKind::NotDirect, ecmaMode); } } if (CacheableIdentifier::isCacheableIdentifierCell(subscript)) { const Identifier propertyName = subscript.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, void()); if (subscript.isSymbol() || !parseIndex(propertyName)) { AccessType accessType = static_cast(stubInfo->accessType); PutPropertySlot slot(baseValue, ecmaMode.isStrict(), codeBlock->putByIdContext()); Structure* structure = CommonSlowPaths::originalStructureBeforePut(baseValue); baseObject->putInline(globalObject, propertyName, value, slot); RETURN_IF_EXCEPTION(scope, void()); if (accessType != static_cast(stubInfo->accessType)) return; CacheableIdentifier identifier = CacheableIdentifier::createFromCell(subscript.asCell()); if (stubInfo->considerCachingBy(vm, codeBlock, structure, identifier)) repatchPutBy(globalObject, codeBlock, baseValue, structure, identifier, slot, *stubInfo, PutByKind::ByVal, PutKind::NotDirect); return; } } } RELEASE_AND_RETURN(scope, putByVal(globalObject, baseValue, subscript, value, profile, ecmaMode)); } JSC_DEFINE_JIT_OPERATION(operationPutByValStrictOptimize, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile* profile)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue baseValue = JSValue::decode(encodedBaseValue); JSValue subscript = JSValue::decode(encodedSubscript); JSValue value = JSValue::decode(encodedValue); putByValOptimize(globalObject, callFrame->codeBlock(), baseValue, subscript, value, stubInfo, profile, ECMAMode::strict()); } JSC_DEFINE_JIT_OPERATION(operationPutByValNonStrictOptimize, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile* profile)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue baseValue = JSValue::decode(encodedBaseValue); JSValue subscript = JSValue::decode(encodedSubscript); JSValue value = JSValue::decode(encodedValue); putByValOptimize(globalObject, callFrame->codeBlock(), baseValue, subscript, value, stubInfo, profile, ECMAMode::sloppy()); } static ALWAYS_INLINE void directPutByValOptimize(JSGlobalObject* globalObject, CodeBlock* codeBlock, JSValue baseValue, JSValue subscript, JSValue value, StructureStubInfo* stubInfo, ArrayProfile* profile, ECMAMode ecmaMode) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); RELEASE_ASSERT(baseValue.isObject()); JSObject* baseObject = asObject(baseValue); if (!isCopyOnWrite(baseObject->indexingMode()) && subscript.isInt32()) { Structure* structure = baseObject->structure(); if (stubInfo->considerCachingGeneric(vm, codeBlock, structure)) { if (profile) { ConcurrentJSLocker locker(codeBlock->m_lock); profile->computeUpdatedPrediction(locker, codeBlock, structure); } repatchArrayPutByVal(globalObject, codeBlock, baseValue, subscript, *stubInfo, PutKind::Direct, ecmaMode); } } if (CacheableIdentifier::isCacheableIdentifierCell(subscript)) { const Identifier propertyName = subscript.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, void()); if (subscript.isSymbol() || !parseIndex(propertyName)) { AccessType accessType = static_cast(stubInfo->accessType); PutPropertySlot slot(baseValue, ecmaMode.isStrict(), codeBlock->putByIdContext()); Structure* structure = CommonSlowPaths::originalStructureBeforePut(baseValue); CommonSlowPaths::putDirectWithReify(vm, globalObject, baseObject, propertyName, value, slot); RETURN_IF_EXCEPTION(scope, void()); if (accessType != static_cast(stubInfo->accessType)) return; CacheableIdentifier identifier = CacheableIdentifier::createFromCell(subscript.asCell()); if (stubInfo->considerCachingBy(vm, codeBlock, structure, identifier)) repatchPutBy(globalObject, codeBlock, baseValue, structure, identifier, slot, *stubInfo, PutByKind::ByVal, PutKind::Direct); return; } } RELEASE_AND_RETURN(scope, directPutByVal(globalObject, baseObject, subscript, value, profile, ecmaMode)); } JSC_DEFINE_JIT_OPERATION(operationDirectPutByValStrictOptimize, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile* profile)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue baseValue = JSValue::decode(encodedBaseValue); JSValue subscript = JSValue::decode(encodedSubscript); JSValue value = JSValue::decode(encodedValue); directPutByValOptimize(globalObject, callFrame->codeBlock(), baseValue, subscript, value, stubInfo, profile, ECMAMode::strict()); } JSC_DEFINE_JIT_OPERATION(operationDirectPutByValNonStrictOptimize, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile* profile)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue baseValue = JSValue::decode(encodedBaseValue); JSValue subscript = JSValue::decode(encodedSubscript); JSValue value = JSValue::decode(encodedValue); directPutByValOptimize(globalObject, callFrame->codeBlock(), baseValue, subscript, value, stubInfo, profile, ECMAMode::sloppy()); } JSC_DEFINE_JIT_OPERATION(operationPutByValStrictGeneric, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile* profile)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue baseValue = JSValue::decode(encodedBaseValue); JSValue subscript = JSValue::decode(encodedSubscript); JSValue value = JSValue::decode(encodedValue); stubInfo->tookSlowPath = true; putByVal(globalObject, baseValue, subscript, value, profile, ECMAMode::strict()); } JSC_DEFINE_JIT_OPERATION(operationPutByValNonStrictGeneric, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile* profile)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue baseValue = JSValue::decode(encodedBaseValue); JSValue subscript = JSValue::decode(encodedSubscript); JSValue value = JSValue::decode(encodedValue); stubInfo->tookSlowPath = true; putByVal(globalObject, baseValue, subscript, value, profile, ECMAMode::sloppy()); } JSC_DEFINE_JIT_OPERATION(operationDirectPutByValStrictGeneric, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile* profile)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue baseValue = JSValue::decode(encodedBaseValue); JSValue subscript = JSValue::decode(encodedSubscript); JSValue value = JSValue::decode(encodedValue); RELEASE_ASSERT(baseValue.isObject()); stubInfo->tookSlowPath = true; directPutByVal(globalObject, asObject(baseValue), subscript, value, profile, ECMAMode::strict()); } JSC_DEFINE_JIT_OPERATION(operationDirectPutByValNonStrictGeneric, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile* profile)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue baseValue = JSValue::decode(encodedBaseValue); JSValue subscript = JSValue::decode(encodedSubscript); JSValue value = JSValue::decode(encodedValue); RELEASE_ASSERT(baseValue.isObject()); stubInfo->tookSlowPath = true; directPutByVal(globalObject, asObject(baseValue), subscript, value, profile, ECMAMode::sloppy()); } JSC_DEFINE_JIT_OPERATION(operationSetPrivateBrandOptimize, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBaseValue, EncodedJSValue encodedBrand)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); JSValue baseValue = JSValue::decode(encodedBaseValue); JSValue brand = JSValue::decode(encodedBrand); ASSERT(baseValue.isObject()); ASSERT(brand.isSymbol()); JSObject* baseObject = asObject(baseValue); Structure* oldStructure = baseObject->structure(); baseObject->setPrivateBrand(globalObject, brand); RETURN_IF_EXCEPTION(scope, void()); CodeBlock* codeBlock = callFrame->codeBlock(); if (CacheableIdentifier::isCacheableIdentifierCell(brand)) { CacheableIdentifier identifier = CacheableIdentifier::createFromCell(brand.asCell()); if (stubInfo->considerCachingBy(vm, codeBlock, baseObject->structure(), identifier)) repatchSetPrivateBrand(globalObject, codeBlock, baseObject, oldStructure, identifier, *stubInfo); } } JSC_DEFINE_JIT_OPERATION(operationSetPrivateBrandGeneric, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBaseValue, EncodedJSValue encodedBrand)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); JSValue baseValue = JSValue::decode(encodedBaseValue); JSValue brand = JSValue::decode(encodedBrand); if (stubInfo) stubInfo->tookSlowPath = true; ASSERT(baseValue.isObject()); ASSERT(brand.isSymbol()); JSObject* baseObject = asObject(baseValue); baseObject->setPrivateBrand(globalObject, brand); RETURN_IF_EXCEPTION(scope, void()); } JSC_DEFINE_JIT_OPERATION(operationCheckPrivateBrandOptimize, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBaseValue, EncodedJSValue encodedBrand)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); JSValue baseValue = JSValue::decode(encodedBaseValue); JSValue brand = JSValue::decode(encodedBrand); JSObject* baseObject = baseValue.toObject(globalObject); RETURN_IF_EXCEPTION(scope, void()); ASSERT(brand.isSymbol()); baseObject->checkPrivateBrand(globalObject, brand); RETURN_IF_EXCEPTION(scope, void()); CodeBlock* codeBlock = callFrame->codeBlock(); if (CacheableIdentifier::isCacheableIdentifierCell(brand)) { CacheableIdentifier identifier = CacheableIdentifier::createFromCell(brand.asCell()); if (stubInfo->considerCachingBy(vm, codeBlock, baseObject->structure(), identifier)) repatchCheckPrivateBrand(globalObject, codeBlock, baseObject, identifier, *stubInfo); } } JSC_DEFINE_JIT_OPERATION(operationCheckPrivateBrandGeneric, void, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBaseValue, EncodedJSValue encodedBrand)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); JSValue baseValue = JSValue::decode(encodedBaseValue); JSValue brand = JSValue::decode(encodedBrand); stubInfo->tookSlowPath = true; JSObject* baseObject = baseValue.toObject(globalObject); RETURN_IF_EXCEPTION(scope, void()); ASSERT(brand.isSymbol()); baseObject->checkPrivateBrand(globalObject, brand); RETURN_IF_EXCEPTION(scope, void()); } template static ALWAYS_INLINE void putPrivateNameOptimize(JSGlobalObject* globalObject, CodeBlock* codeBlock, JSValue baseValue, JSValue subscript, JSValue value, StructureStubInfo* stubInfo) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); auto baseObject = baseValue.toObject(globalObject); RETURN_IF_EXCEPTION(scope, void()); auto propertyName = subscript.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, void()); // Private fields can only be accessed within class lexical scope // and class methods are always in strict mode AccessType accessType = static_cast(stubInfo->accessType); Structure* structure = CommonSlowPaths::originalStructureBeforePut(baseValue); constexpr bool isStrictMode = true; PutPropertySlot slot(baseObject, isStrictMode); if constexpr (define) baseObject->definePrivateField(globalObject, propertyName, value, slot); else baseObject->setPrivateField(globalObject, propertyName, value, slot); RETURN_IF_EXCEPTION(scope, void()); if (accessType != static_cast(stubInfo->accessType)) return; if (baseValue.isObject() && CacheableIdentifier::isCacheableIdentifierCell(subscript)) { CacheableIdentifier identifier = CacheableIdentifier::createFromCell(subscript.asCell()); if (stubInfo->considerCachingBy(vm, codeBlock, structure, identifier)) repatchPutBy(globalObject, codeBlock, baseValue, structure, identifier, slot, *stubInfo, PutByKind::ByVal, define ? PutKind::DirectPrivateFieldDefine : PutKind::DirectPrivateFieldSet); } } template static ALWAYS_INLINE void putPrivateName(JSGlobalObject* globalObject, JSValue baseValue, JSValue subscript, JSValue value) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); auto baseObject = baseValue.toObject(globalObject); RETURN_IF_EXCEPTION(scope, void()); auto propertyName = subscript.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, void()); scope.release(); // Private fields can only be accessed within class lexical scope // and class methods are always in strict mode constexpr bool isStrictMode = true; PutPropertySlot slot(baseObject, isStrictMode); if constexpr (define) baseObject->definePrivateField(globalObject, propertyName, value, slot); else baseObject->setPrivateField(globalObject, propertyName, value, slot); } JSC_DEFINE_JIT_OPERATION(operationPutByValDefinePrivateFieldOptimize, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile*)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); CodeBlock* codeBlock = callFrame->codeBlock(); JSValue baseValue = JSValue::decode(encodedBaseValue); JSValue subscript = JSValue::decode(encodedSubscript); JSValue value = JSValue::decode(encodedValue); putPrivateNameOptimize(globalObject, codeBlock, baseValue, subscript, value, stubInfo); } JSC_DEFINE_JIT_OPERATION(operationPutByValSetPrivateFieldOptimize, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile*)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); CodeBlock* codeBlock = callFrame->codeBlock(); JSValue baseValue = JSValue::decode(encodedBaseValue); JSValue subscript = JSValue::decode(encodedSubscript); JSValue value = JSValue::decode(encodedValue); putPrivateNameOptimize(globalObject, codeBlock, baseValue, subscript, value, stubInfo); } JSC_DEFINE_JIT_OPERATION(operationPutByValDefinePrivateFieldGeneric, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile*)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue baseValue = JSValue::decode(encodedBaseValue); JSValue subscript = JSValue::decode(encodedSubscript); JSValue value = JSValue::decode(encodedValue); if (stubInfo) stubInfo->tookSlowPath = true; putPrivateName(globalObject, baseValue, subscript, value); } JSC_DEFINE_JIT_OPERATION(operationPutByValSetPrivateFieldGeneric, void, (JSGlobalObject* globalObject, EncodedJSValue encodedBaseValue, EncodedJSValue encodedSubscript, EncodedJSValue encodedValue, StructureStubInfo* stubInfo, ArrayProfile*)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue baseValue = JSValue::decode(encodedBaseValue); JSValue subscript = JSValue::decode(encodedSubscript); JSValue value = JSValue::decode(encodedValue); if (stubInfo) stubInfo->tookSlowPath = true; putPrivateName(globalObject, baseValue, subscript, value); } JSC_DEFINE_JIT_OPERATION(operationCallDirectEvalSloppy, EncodedJSValue, (void* frame, JSScope* callerScopeChain, EncodedJSValue encodedThisValue)) { CallFrame* calleeFrame = bitwise_cast(frame); calleeFrame->setCodeBlock(nullptr); if (!isHostFunction(calleeFrame->guaranteedJSValueCallee(), globalFuncEval)) return JSValue::encode(JSValue()); return JSValue::encode(eval(calleeFrame, JSValue::decode(encodedThisValue), callerScopeChain, ECMAMode::sloppy())); } JSC_DEFINE_JIT_OPERATION(operationCallDirectEvalStrict, EncodedJSValue, (void* frame, JSScope* callerScopeChain, EncodedJSValue encodedThisValue)) { CallFrame* calleeFrame = bitwise_cast(frame); calleeFrame->setCodeBlock(nullptr); if (!isHostFunction(calleeFrame->guaranteedJSValueCallee(), globalFuncEval)) return JSValue::encode(JSValue()); return JSValue::encode(eval(calleeFrame, JSValue::decode(encodedThisValue), callerScopeChain, ECMAMode::strict())); } JSC_DEFINE_JIT_OPERATION(operationLinkCall, SlowPathReturnType, (CallFrame* calleeFrame, JSGlobalObject* globalObject, CallLinkInfo* callLinkInfo)) { sanitizeStackForVM(globalObject->vm()); return linkFor(calleeFrame, globalObject, callLinkInfo); } JSC_DEFINE_JIT_OPERATION(operationLinkPolymorphicCall, SlowPathReturnType, (CallFrame* calleeFrame, JSGlobalObject* globalObject, CallLinkInfo* callLinkInfo)) { sanitizeStackForVM(globalObject->vm()); ASSERT(callLinkInfo->specializationKind() == CodeForCall); JSCell* calleeAsFunctionCell; SlowPathReturnType result = virtualForWithFunction(globalObject, calleeFrame, callLinkInfo, calleeAsFunctionCell); linkPolymorphicCall(globalObject, calleeFrame, *callLinkInfo, CallVariant(calleeAsFunctionCell)); return result; } JSC_DEFINE_JIT_OPERATION(operationVirtualCall, SlowPathReturnType, (CallFrame* calleeFrame, JSGlobalObject* globalObject, CallLinkInfo* callLinkInfo)) { sanitizeStackForVM(globalObject->vm()); JSCell* calleeAsFunctionCellIgnored; return virtualForWithFunction(globalObject, calleeFrame, callLinkInfo, calleeAsFunctionCellIgnored); } JSC_DEFINE_JIT_OPERATION(operationCompareLess, size_t, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return jsLess(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)); } JSC_DEFINE_JIT_OPERATION(operationCompareLessEq, size_t, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return jsLessEq(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)); } JSC_DEFINE_JIT_OPERATION(operationCompareGreater, size_t, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return jsLess(globalObject, JSValue::decode(encodedOp2), JSValue::decode(encodedOp1)); } JSC_DEFINE_JIT_OPERATION(operationCompareGreaterEq, size_t, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return jsLessEq(globalObject, JSValue::decode(encodedOp2), JSValue::decode(encodedOp1)); } JSC_DEFINE_JIT_OPERATION(operationCompareEq, size_t, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return JSValue::equalSlowCaseInline(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)); } #if USE(JSVALUE64) JSC_DEFINE_JIT_OPERATION(operationCompareStringEq, EncodedJSValue, (JSGlobalObject* globalObject, JSCell* left, JSCell* right)) #else JSC_DEFINE_JIT_OPERATION(operationCompareStringEq, size_t, (JSGlobalObject* globalObject, JSCell* left, JSCell* right)) #endif { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); bool result = asString(left)->equalInline(globalObject, asString(right)); #if USE(JSVALUE64) return JSValue::encode(jsBoolean(result)); #else return result; #endif } JSC_DEFINE_JIT_OPERATION(operationCompareStrictEq, size_t, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue src1 = JSValue::decode(encodedOp1); JSValue src2 = JSValue::decode(encodedOp2); return JSValue::strictEqual(globalObject, src1, src2); } #if USE(BIGINT32) JSC_DEFINE_JIT_OPERATION(operationCompareEqHeapBigIntToInt32, size_t, (JSGlobalObject* globalObject, JSCell* heapBigInt, int32_t smallInt)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); ASSERT(heapBigInt->isHeapBigInt()); return static_cast(heapBigInt)->equalsToInt32(smallInt); } #endif JSC_DEFINE_JIT_OPERATION(operationNewArrayWithProfile, EncodedJSValue, (JSGlobalObject* globalObject, ArrayAllocationProfile* profile, const JSValue* values, int size)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return JSValue::encode(constructArrayNegativeIndexed(globalObject, profile, values, size)); } JSC_DEFINE_JIT_OPERATION(operationNewArrayWithSizeAndProfile, EncodedJSValue, (JSGlobalObject* globalObject, ArrayAllocationProfile* profile, EncodedJSValue size)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue sizeValue = JSValue::decode(size); return JSValue::encode(constructArrayWithSizeQuirk(globalObject, profile, sizeValue)); } template static EncodedJSValue newFunctionCommon(VM& vm, JSScope* scope, JSCell* functionExecutable, bool isInvalidated) { ASSERT(functionExecutable->inherits()); if (isInvalidated) return JSValue::encode(FunctionType::createWithInvalidatedReallocationWatchpoint(vm, static_cast(functionExecutable), scope)); return JSValue::encode(FunctionType::create(vm, static_cast(functionExecutable), scope)); } JSC_DEFINE_JIT_OPERATION(operationNewFunction, EncodedJSValue, (VM* vmPointer, JSScope* scope, JSCell* functionExecutable)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return newFunctionCommon(vm, scope, functionExecutable, false); } JSC_DEFINE_JIT_OPERATION(operationNewFunctionWithInvalidatedReallocationWatchpoint, EncodedJSValue, (VM* vmPointer, JSScope* scope, JSCell* functionExecutable)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return newFunctionCommon(vm, scope, functionExecutable, true); } JSC_DEFINE_JIT_OPERATION(operationNewGeneratorFunction, EncodedJSValue, (VM* vmPointer, JSScope* scope, JSCell* functionExecutable)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return newFunctionCommon(vm, scope, functionExecutable, false); } JSC_DEFINE_JIT_OPERATION(operationNewGeneratorFunctionWithInvalidatedReallocationWatchpoint, EncodedJSValue, (VM* vmPointer, JSScope* scope, JSCell* functionExecutable)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return newFunctionCommon(vm, scope, functionExecutable, true); } JSC_DEFINE_JIT_OPERATION(operationNewAsyncFunction, EncodedJSValue, (VM* vmPointer, JSScope* scope, JSCell* functionExecutable)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return newFunctionCommon(vm, scope, functionExecutable, false); } JSC_DEFINE_JIT_OPERATION(operationNewAsyncFunctionWithInvalidatedReallocationWatchpoint, EncodedJSValue, (VM* vmPointer, JSScope* scope, JSCell* functionExecutable)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return newFunctionCommon(vm, scope, functionExecutable, true); } JSC_DEFINE_JIT_OPERATION(operationNewAsyncGeneratorFunction, EncodedJSValue, (VM* vmPointer, JSScope* scope, JSCell* functionExecutable)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return newFunctionCommon(vm, scope, functionExecutable, false); } JSC_DEFINE_JIT_OPERATION(operationNewAsyncGeneratorFunctionWithInvalidatedReallocationWatchpoint, EncodedJSValue, (VM* vmPointer, JSScope* scope, JSCell* functionExecutable)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return newFunctionCommon(vm, scope, functionExecutable, true); } JSC_DEFINE_JIT_OPERATION(operationSetFunctionName, void, (JSGlobalObject* globalObject, JSCell* funcCell, EncodedJSValue encodedName)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSFunction* func = jsCast(funcCell); JSValue name = JSValue::decode(encodedName); func->setFunctionName(globalObject, name); } JSC_DEFINE_JIT_OPERATION(operationNewObject, JSCell*, (VM* vmPointer, Structure* structure)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return constructEmptyObject(vm, structure); } JSC_DEFINE_JIT_OPERATION(operationNewPromise, JSCell*, (VM* vmPointer, Structure* structure)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return JSPromise::create(vm, structure); } JSC_DEFINE_JIT_OPERATION(operationNewInternalPromise, JSCell*, (VM* vmPointer, Structure* structure)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return JSInternalPromise::create(vm, structure); } JSC_DEFINE_JIT_OPERATION(operationNewGenerator, JSCell*, (VM* vmPointer, Structure* structure)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return JSGenerator::create(vm, structure); } JSC_DEFINE_JIT_OPERATION(operationNewAsyncGenerator, JSCell*, (VM* vmPointer, Structure* structure)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return JSAsyncGenerator::create(vm, structure); } JSC_DEFINE_JIT_OPERATION(operationNewRegexp, JSCell*, (JSGlobalObject* globalObject, JSCell* regexpPtr)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); RegExp* regexp = static_cast(regexpPtr); static constexpr bool areLegacyFeaturesEnabled = true; return RegExpObject::create(vm, globalObject->regExpStructure(), regexp, areLegacyFeaturesEnabled); } // The only reason for returning an UnusedPtr (instead of void) is so that we can reuse the // existing DFG slow path generator machinery when creating the slow path for CheckTraps // in the DFG. If a DFG slow path generator that supports a void return type is added in the // future, we can switch to using that then. JSC_DEFINE_JIT_OPERATION(operationHandleTraps, UnusedPtr, (JSGlobalObject* globalObject)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); ASSERT(vm.traps().needHandling(VMTraps::AsyncEvents)); vm.traps().handleTraps(VMTraps::AsyncEvents); return nullptr; } JSC_DEFINE_JIT_OPERATION(operationDebug, void, (VM* vmPointer, int32_t debugHookType)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); vm.interpreter.debug(callFrame, static_cast(debugHookType)); } #if ENABLE(DFG_JIT) static void updateAllPredictionsAndOptimizeAfterWarmUp(CodeBlock* codeBlock) { codeBlock->updateAllPredictions(); codeBlock->optimizeAfterWarmUp(); } JSC_DEFINE_JIT_OPERATION(operationOptimize, SlowPathReturnType, (VM* vmPointer, uint32_t bytecodeIndexBits)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); BytecodeIndex bytecodeIndex = BytecodeIndex::fromBits(bytecodeIndexBits); // Defer GC for a while so that it doesn't run between when we enter into this // slow path and when we figure out the state of our code block. This prevents // a number of awkward reentrancy scenarios, including: // // - The optimized version of our code block being jettisoned by GC right after // we concluded that we wanted to use it, but have not planted it into the JS // stack yet. // // - An optimized version of our code block being installed just as we decided // that it wasn't ready yet. // // Note that jettisoning won't happen if we already initiated OSR, because in // that case we would have already planted the optimized code block into the JS // stack. DeferGCForAWhile deferGC(vm); CodeBlock* codeBlock = callFrame->codeBlock(); if (UNLIKELY(codeBlock->jitType() != JITType::BaselineJIT)) { dataLog("Unexpected code block in Baseline->DFG tier-up: ", *codeBlock, "\n"); RELEASE_ASSERT_NOT_REACHED(); } if (bytecodeIndex) { // If we're attempting to OSR from a loop, assume that this should be // separately optimized. codeBlock->m_shouldAlwaysBeInlined = false; } if (UNLIKELY(Options::verboseOSR())) { dataLog( *codeBlock, ": Entered optimize with bytecodeIndex = ", bytecodeIndex, ", executeCounter = ", codeBlock->jitExecuteCounter(), ", optimizationDelayCounter = ", codeBlock->reoptimizationRetryCounter(), ", exitCounter = "); if (codeBlock->hasOptimizedReplacement()) dataLog(codeBlock->replacement()->osrExitCounter()); else dataLog("N/A"); dataLog("\n"); } if (!codeBlock->checkIfOptimizationThresholdReached()) { CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("counter = ", codeBlock->jitExecuteCounter())); codeBlock->updateAllPredictions(); dataLogLnIf(Options::verboseOSR(), "Choosing not to optimize ", *codeBlock, " yet, because the threshold hasn't been reached."); return encodeResult(nullptr, nullptr); } if (UNLIKELY(vm.terminationInProgress())) { // If termination of the current stack of execution is in progress, // then we need to hold off on optimized compiles so that termination // checks will be called, and we can unwind out of the current stack. CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("Terminating current execution")); updateAllPredictionsAndOptimizeAfterWarmUp(codeBlock); return encodeResult(nullptr, nullptr); } Debugger* debugger = codeBlock->globalObject()->debugger(); if (UNLIKELY(debugger && (debugger->isStepping() || codeBlock->baselineAlternative()->hasDebuggerRequests()))) { CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("debugger is stepping or has requests")); updateAllPredictionsAndOptimizeAfterWarmUp(codeBlock); return encodeResult(nullptr, nullptr); } if (codeBlock->m_shouldAlwaysBeInlined) { CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("should always be inlined")); updateAllPredictionsAndOptimizeAfterWarmUp(codeBlock); dataLogLnIf(Options::verboseOSR(), "Choosing not to optimize ", *codeBlock, " yet, because m_shouldAlwaysBeInlined == true."); return encodeResult(nullptr, nullptr); } // The call to JITWorklist::completeAllReadyPlansForVM() will complete all ready // (i.e. compiled) code blocks. But if it completes ours, we also need to know // what the result was so that we don't plow ahead and attempt OSR or immediate // reoptimization. This will have already also set the appropriate JIT execution // count threshold depending on what happened, so if the compilation was anything // but successful we just want to return early. See the case for worklistState == // JITWorklist::Compiled, below. // Note that we could have alternatively just called Worklist::compilationState() // here, and if it returned Compiled, we could have then called // completeAndScheduleOSR() below. But that would have meant that it could take // longer for code blocks to be completed: they would only complete when *their* // execution count trigger fired; but that could take a while since the firing is // racy. It could also mean that code blocks that never run again after being // compiled would sit on the worklist until next GC. That's fine, but it's // probably a waste of memory. Our goal here is to complete code blocks as soon as // possible in order to minimize the chances of us executing baseline code after // optimized code is already available. JITWorklist::State worklistState = JITWorklist::ensureGlobalWorklist().completeAllReadyPlansForVM( vm, JITCompilationKey(codeBlock, Options::forceUnlinkedDFG() ? JITCompilationMode::UnlinkedDFG : JITCompilationMode::DFG)); if (worklistState == JITWorklist::Compiling) { CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("compiling")); // We cannot be in the process of asynchronous compilation and also have an optimized // replacement. RELEASE_ASSERT(!codeBlock->hasOptimizedReplacement()); codeBlock->setOptimizationThresholdBasedOnCompilationResult(CompilationDeferred); return encodeResult(nullptr, nullptr); } if (worklistState == JITWorklist::Compiled) { // If we don't have an optimized replacement but we did just get compiled, then // the compilation failed or was invalidated, in which case the execution count // thresholds have already been set appropriately by // CodeBlock::setOptimizationThresholdBasedOnCompilationResult() and we have // nothing left to do. if (!codeBlock->hasOptimizedReplacement()) { CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("compiled and failed")); codeBlock->updateAllPredictions(); dataLogLnIf(Options::verboseOSR(), "Code block ", *codeBlock, " was compiled but it doesn't have an optimized replacement."); return encodeResult(nullptr, nullptr); } } else if (codeBlock->hasOptimizedReplacement()) { CodeBlock* replacement = codeBlock->replacement(); dataLogLnIf(Options::verboseOSR(), "Considering OSR ", codeBlock, " -> ", replacement, "."); // If we have an optimized replacement, then it must be the case that we entered // cti_optimize from a loop. That's because if there's an optimized replacement, // then all calls to this function will be relinked to the replacement and so // the prologue OSR will never fire. // This is an interesting threshold check. Consider that a function OSR exits // in the middle of a loop, while having a relatively low exit count. The exit // will reset the execution counter to some target threshold, meaning that this // code won't be reached until that loop heats up for >=1000 executions. But then // we do a second check here, to see if we should either reoptimize, or just // attempt OSR entry. Hence it might even be correct for // shouldReoptimizeFromLoopNow() to always return true. But we make it do some // additional checking anyway, to reduce the amount of recompilation thrashing. if (replacement->shouldReoptimizeFromLoopNow()) { CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("should reoptimize from loop now")); dataLogLnIf(Options::verboseOSR(), "Triggering reoptimization of ", codeBlock, "(", replacement, ") (in loop)."); replacement->jettison(Profiler::JettisonDueToBaselineLoopReoptimizationTrigger, CountReoptimization); return encodeResult(nullptr, nullptr); } } else { if (!codeBlock->shouldOptimizeNow()) { CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("insufficient profiling")); dataLogLnIf(Options::verboseOSR(), "Delaying optimization for ", *codeBlock, " because of insufficient profiling."); return encodeResult(nullptr, nullptr); } dataLogLnIf(Options::verboseOSR(), "Triggering optimized compilation of ", *codeBlock); unsigned numVarsWithValues = 0; if (bytecodeIndex) numVarsWithValues = codeBlock->numCalleeLocals(); Operands> mustHandleValues(codeBlock->numParameters(), numVarsWithValues, 0); int localsUsedForCalleeSaves = static_cast(CodeBlock::llintBaselineCalleeSaveSpaceAsVirtualRegisters()); for (size_t i = 0; i < mustHandleValues.size(); ++i) { Operand operand = mustHandleValues.operandForIndex(i); if (operand.isLocal() && operand.toLocal() < localsUsedForCalleeSaves) continue; mustHandleValues[i] = callFrame->uncheckedR(operand.virtualRegister()).jsValue(); } CodeBlock* replacementCodeBlock = codeBlock->newReplacement(); CompilationResult result = DFG::compile( vm, replacementCodeBlock, nullptr, Options::forceUnlinkedDFG() ? JITCompilationMode::UnlinkedDFG : JITCompilationMode::DFG, bytecodeIndex, mustHandleValues, JITToDFGDeferredCompilationCallback::create()); if (result != CompilationSuccessful) { CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("compilation failed")); return encodeResult(nullptr, nullptr); } } CodeBlock* optimizedCodeBlock = codeBlock->replacement(); ASSERT(optimizedCodeBlock && JITCode::isOptimizingJIT(optimizedCodeBlock->jitType())); if (void* dataBuffer = DFG::prepareOSREntry(vm, callFrame, optimizedCodeBlock, bytecodeIndex)) { CODEBLOCK_LOG_EVENT(optimizedCodeBlock, "osrEntry", ("at bc#", bytecodeIndex)); dataLogLnIf(Options::verboseOSR(), "Performing OSR ", codeBlock, " -> ", optimizedCodeBlock); codeBlock->optimizeSoon(); codeBlock->unlinkedCodeBlock()->setDidOptimize(TriState::True); void* targetPC = untagCodePtr(vm.getCTIStub(DFG::osrEntryThunkGenerator).code().taggedPtr()); targetPC = tagCodePtrWithStackPointerForJITCall(targetPC, callFrame); return encodeResult(targetPC, dataBuffer); } dataLogLnIf(Options::verboseOSR(), "Optimizing ", codeBlock, " -> ", codeBlock->replacement(), " succeeded, OSR failed, after a delay of ", codeBlock->optimizationDelayCounter()); // Count the OSR failure as a speculation failure. If this happens a lot, then // reoptimize. optimizedCodeBlock->countOSRExit(); // We are a lot more conservative about triggering reoptimization after OSR failure than // before it. If we enter the optimize_from_loop trigger with a bucket full of fail // already, then we really would like to reoptimize immediately. But this case covers // something else: there weren't many (or any) speculation failures before, but we just // failed to enter the speculative code because some variable had the wrong value or // because the OSR code decided for any spurious reason that it did not want to OSR // right now. So, we only trigger reoptimization only upon the more conservative (non-loop) // reoptimization trigger. if (optimizedCodeBlock->shouldReoptimizeNow()) { CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("should reoptimize now")); dataLogLnIf(Options::verboseOSR(), "Triggering reoptimization of ", codeBlock, " -> ", codeBlock->replacement(), " (after OSR fail)."); optimizedCodeBlock->jettison(Profiler::JettisonDueToBaselineLoopReoptimizationTriggerOnOSREntryFail, CountReoptimization); return encodeResult(nullptr, nullptr); } // OSR failed this time, but it might succeed next time! Let the code run a bit // longer and then try again. codeBlock->optimizeAfterWarmUp(); CODEBLOCK_LOG_EVENT(codeBlock, "delayOptimizeToDFG", ("OSR failed")); return encodeResult(nullptr, nullptr); } JSC_DEFINE_JIT_OPERATION(operationTryOSREnterAtCatchAndValueProfile, SlowPathReturnType, (VM* vmPointer, uint32_t bytecodeIndexBits)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); BytecodeIndex bytecodeIndex = BytecodeIndex::fromBits(bytecodeIndexBits); CodeBlock* codeBlock = callFrame->codeBlock(); CodeBlock* optimizedReplacement = codeBlock->replacement(); if (UNLIKELY(!optimizedReplacement)) return encodeResult(nullptr, nullptr); switch (optimizedReplacement->jitType()) { case JITType::DFGJIT: case JITType::FTLJIT: { CodePtr entry = DFG::prepareCatchOSREntry(vm, callFrame, codeBlock, optimizedReplacement, bytecodeIndex); return encodeResult(entry.taggedPtr(), optimizedReplacement); } default: break; } codeBlock->ensureCatchLivenessIsComputedForBytecodeIndex(bytecodeIndex); auto bytecode = codeBlock->instructions().at(bytecodeIndex)->as(); auto& metadata = bytecode.metadata(codeBlock); metadata.m_buffer->forEach([&] (ValueProfileAndVirtualRegister& profile) { profile.m_buckets[0] = JSValue::encode(callFrame->uncheckedR(profile.m_operand).jsValue()); }); return encodeResult(nullptr, nullptr); } #endif enum class AccessorType { Getter, Setter }; static void putAccessorByVal(JSGlobalObject* globalObject, JSObject* base, JSValue subscript, int32_t attribute, JSObject* accessor, AccessorType accessorType) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); auto propertyKey = subscript.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, void()); scope.release(); if (accessorType == AccessorType::Getter) base->putGetter(globalObject, propertyKey, accessor, attribute); else base->putSetter(globalObject, propertyKey, accessor, attribute); } JSC_DEFINE_JIT_OPERATION(operationPutGetterById, void, (JSGlobalObject* globalObject, JSCell* object, UniquedStringImpl* uid, int32_t options, JSCell* getter)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); ASSERT(object && object->isObject()); JSObject* baseObj = object->getObject(); ASSERT(getter->isObject()); baseObj->putGetter(globalObject, uid, getter, options); } JSC_DEFINE_JIT_OPERATION(operationPutSetterById, void, (JSGlobalObject* globalObject, JSCell* object, UniquedStringImpl* uid, int32_t options, JSCell* setter)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); ASSERT(object && object->isObject()); JSObject* baseObj = object->getObject(); ASSERT(setter->isObject()); baseObj->putSetter(globalObject, uid, setter, options); } JSC_DEFINE_JIT_OPERATION(operationPutGetterByVal, void, (JSGlobalObject* globalObject, JSCell* base, EncodedJSValue encodedSubscript, int32_t attribute, JSCell* getter)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); putAccessorByVal(globalObject, asObject(base), JSValue::decode(encodedSubscript), attribute, asObject(getter), AccessorType::Getter); } JSC_DEFINE_JIT_OPERATION(operationPutSetterByVal, void, (JSGlobalObject* globalObject, JSCell* base, EncodedJSValue encodedSubscript, int32_t attribute, JSCell* setter)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); putAccessorByVal(globalObject, asObject(base), JSValue::decode(encodedSubscript), attribute, asObject(setter), AccessorType::Setter); } #if USE(JSVALUE64) JSC_DEFINE_JIT_OPERATION(operationPutGetterSetter, void, (JSGlobalObject* globalObject, JSCell* object, UniquedStringImpl* uid, int32_t attribute, EncodedJSValue encodedGetterValue, EncodedJSValue encodedSetterValue)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); ASSERT(object && object->isObject()); JSObject* baseObject = asObject(object); JSValue getter = JSValue::decode(encodedGetterValue); JSValue setter = JSValue::decode(encodedSetterValue); ASSERT(getter.isObject() || setter.isObject()); GetterSetter* accessor = GetterSetter::create(vm, globalObject, getter, setter); CommonSlowPaths::putDirectAccessorWithReify(vm, globalObject, baseObject, uid, accessor, attribute); } #else JSC_DEFINE_JIT_OPERATION(operationPutGetterSetter, void, (JSGlobalObject* globalObject, JSCell* object, UniquedStringImpl* uid, int32_t attribute, JSCell* getterCell, JSCell* setterCell)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); ASSERT(object && object->isObject()); JSObject* baseObject = asObject(object); ASSERT(getterCell || setterCell); JSObject* getter = getterCell ? getterCell->getObject() : nullptr; JSObject* setter = setterCell ? setterCell->getObject() : nullptr; GetterSetter* accessor = GetterSetter::create(vm, globalObject, getter, setter); CommonSlowPaths::putDirectAccessorWithReify(vm, globalObject, baseObject, uid, accessor, attribute); } #endif JSC_DEFINE_JIT_OPERATION(operationInstanceOfCustom, size_t, (JSGlobalObject* globalObject, EncodedJSValue encodedValue, JSObject* constructor, EncodedJSValue encodedHasInstance)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue value = JSValue::decode(encodedValue); JSValue hasInstanceValue = JSValue::decode(encodedHasInstance); if (constructor->hasInstance(globalObject, value, hasInstanceValue)) return 1; return 0; } ALWAYS_INLINE static JSValue getByVal(JSGlobalObject* globalObject, CallFrame* callFrame, ArrayProfile* arrayProfile, JSValue baseValue, JSValue subscript) { UNUSED_PARAM(callFrame); VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); if (LIKELY(baseValue.isCell() && subscript.isString())) { Structure& structure = *baseValue.asCell()->structure(); if (JSCell::canUseFastGetOwnProperty(structure)) { auto existingAtomString = asString(subscript)->toExistingAtomString(globalObject); RETURN_IF_EXCEPTION(scope, JSValue()); if (!existingAtomString.isNull()) { if (JSValue result = baseValue.asCell()->fastGetOwnProperty(vm, structure, existingAtomString.impl())) { ASSERT(callFrame->bytecodeIndex() != BytecodeIndex(0)); return result; } } } } if (std::optional index = subscript.tryGetAsUint32Index()) { uint32_t i = *index; if (isJSString(baseValue)) { if (asString(baseValue)->canGetIndex(i)) RELEASE_AND_RETURN(scope, asString(baseValue)->getIndex(globalObject, i)); if (arrayProfile) arrayProfile->setOutOfBounds(); } else if (baseValue.isObject()) { JSObject* object = asObject(baseValue); if (JSValue result = object->tryGetIndexQuickly(i, arrayProfile)) return result; bool skipMarkingOutOfBounds = false; if (object->indexingType() == ArrayWithContiguous && static_cast(i) < object->butterfly()->publicLength()) { // FIXME: expand this to ArrayStorage, Int32, and maybe Double: // https://bugs.webkit.org/show_bug.cgi?id=182940 auto* globalObject = object->globalObject(); skipMarkingOutOfBounds = globalObject->isOriginalArrayStructure(object->structure()) && globalObject->arrayPrototypeChainIsSane(); } if (!skipMarkingOutOfBounds && !CommonSlowPaths::canAccessArgumentIndexQuickly(*object, i)) { // FIXME: This will make us think that in-bounds typed array accesses are actually // out-of-bounds. // https://bugs.webkit.org/show_bug.cgi?id=149886 if (arrayProfile) arrayProfile->setOutOfBounds(); } } RELEASE_AND_RETURN(scope, baseValue.get(globalObject, i)); } else if (subscript.isNumber() && baseValue.isCell() && arrayProfile) arrayProfile->setOutOfBounds(); baseValue.requireObjectCoercible(globalObject); RETURN_IF_EXCEPTION(scope, JSValue()); auto property = subscript.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, JSValue()); ASSERT(callFrame->bytecodeIndex() != BytecodeIndex(0)); RELEASE_AND_RETURN(scope, baseValue.get(globalObject, property)); } JSC_DEFINE_JIT_OPERATION(operationGetByValGeneric, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, ArrayProfile* profile, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue baseValue = JSValue::decode(encodedBase); JSValue subscript = JSValue::decode(encodedSubscript); stubInfo->tookSlowPath = true; return JSValue::encode(getByVal(globalObject, callFrame, profile, baseValue, subscript)); } JSC_DEFINE_JIT_OPERATION(operationGetByValOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, ArrayProfile* profile, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); JSValue baseValue = JSValue::decode(encodedBase); JSValue subscript = JSValue::decode(encodedSubscript); CodeBlock* codeBlock = callFrame->codeBlock(); if (baseValue.isCell() && subscript.isInt32()) { Structure* structure = baseValue.asCell()->structure(); if (stubInfo->considerCachingGeneric(vm, codeBlock, structure)) { if (profile) { ConcurrentJSLocker locker(codeBlock->m_lock); profile->computeUpdatedPrediction(locker, codeBlock, structure); } repatchArrayGetByVal(globalObject, codeBlock, baseValue, subscript, *stubInfo, GetByKind::ByVal); } } if (baseValue.isCell() && CacheableIdentifier::isCacheableIdentifierCell(subscript)) { const Identifier propertyName = subscript.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, encodedJSValue()); if (subscript.isSymbol() || !parseIndex(propertyName)) { scope.release(); return JSValue::encode(baseValue.getPropertySlot(globalObject, propertyName, [&] (bool found, PropertySlot& slot) -> JSValue { LOG_IC((ICEvent::OperationGetByValOptimize, baseValue.classInfoOrNull(), propertyName, baseValue == slot.slotBase())); CacheableIdentifier identifier = CacheableIdentifier::createFromCell(subscript.asCell()); if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(), identifier)) repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::ByVal); return found ? slot.getValue(globalObject, propertyName) : jsUndefined(); })); } } RELEASE_AND_RETURN(scope, JSValue::encode(getByVal(globalObject, callFrame, profile, baseValue, subscript))); } JSC_DEFINE_JIT_OPERATION(operationGetByVal, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedBase, EncodedJSValue encodedProperty)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); JSValue baseValue = JSValue::decode(encodedBase); JSValue property = JSValue::decode(encodedProperty); if (LIKELY(baseValue.isCell())) { JSCell* base = baseValue.asCell(); if (std::optional index = property.tryGetAsUint32Index()) RELEASE_AND_RETURN(scope, getByValWithIndex(globalObject, base, *index)); if (property.isString()) { Structure& structure = *base->structure(); if (JSCell::canUseFastGetOwnProperty(structure)) { auto existingAtomString = asString(property)->toExistingAtomString(globalObject); RETURN_IF_EXCEPTION(scope, encodedJSValue()); if (!existingAtomString.isNull()) { if (JSValue result = base->fastGetOwnProperty(vm, structure, existingAtomString.impl())) return JSValue::encode(result); } } } } baseValue.requireObjectCoercible(globalObject); RETURN_IF_EXCEPTION(scope, encodedJSValue()); auto propertyName = property.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, encodedJSValue()); RELEASE_AND_RETURN(scope, JSValue::encode(baseValue.get(globalObject, propertyName))); } ALWAYS_INLINE static JSValue getByValWithThis(JSGlobalObject* globalObject, CallFrame* callFrame, ArrayProfile* arrayProfile, JSValue baseValue, JSValue subscript, JSValue thisValue) { UNUSED_PARAM(callFrame); VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); if (LIKELY(baseValue.isCell() && subscript.isString())) { Structure& structure = *baseValue.asCell()->structure(); if (JSCell::canUseFastGetOwnProperty(structure)) { auto existingAtomString = asString(subscript)->toExistingAtomString(globalObject); RETURN_IF_EXCEPTION(scope, { }); if (!existingAtomString.isNull()) { if (JSValue result = baseValue.asCell()->fastGetOwnProperty(vm, structure, existingAtomString.impl())) { ASSERT(callFrame->bytecodeIndex() != BytecodeIndex(0)); return result; } } } } PropertySlot slot(thisValue, PropertySlot::PropertySlot::InternalMethodType::Get); if (std::optional index = subscript.tryGetAsUint32Index()) { uint32_t i = *index; if (isJSString(baseValue)) { if (asString(baseValue)->canGetIndex(i)) RELEASE_AND_RETURN(scope, asString(baseValue)->getIndex(globalObject, i)); if (arrayProfile) arrayProfile->setOutOfBounds(); } else if (baseValue.isObject()) { JSObject* object = asObject(baseValue); if (JSValue result = object->tryGetIndexQuickly(i, arrayProfile)) return result; bool skipMarkingOutOfBounds = false; if (object->indexingType() == ArrayWithContiguous && static_cast(i) < object->butterfly()->publicLength()) { // FIXME: expand this to ArrayStorage, Int32, and maybe Double: // https://bugs.webkit.org/show_bug.cgi?id=182940 auto* globalObject = object->globalObject(); skipMarkingOutOfBounds = globalObject->isOriginalArrayStructure(object->structure()) && globalObject->arrayPrototypeChainIsSane(); } if (!skipMarkingOutOfBounds && !CommonSlowPaths::canAccessArgumentIndexQuickly(*object, i)) { // FIXME: This will make us think that in-bounds typed array accesses are actually // out-of-bounds. // https://bugs.webkit.org/show_bug.cgi?id=149886 if (arrayProfile) arrayProfile->setOutOfBounds(); } } RELEASE_AND_RETURN(scope, baseValue.get(globalObject, i, slot)); } else if (subscript.isNumber() && baseValue.isCell() && arrayProfile) arrayProfile->setOutOfBounds(); baseValue.requireObjectCoercible(globalObject); RETURN_IF_EXCEPTION(scope, { }); auto property = subscript.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, { }); RELEASE_AND_RETURN(scope, baseValue.get(globalObject, property, slot)); } JSC_DEFINE_JIT_OPERATION(operationGetByValWithThisGeneric, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, ArrayProfile* profile, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, EncodedJSValue encodedThis)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue baseValue = JSValue::decode(encodedBase); JSValue subscript = JSValue::decode(encodedSubscript); JSValue thisValue = JSValue::decode(encodedThis); stubInfo->tookSlowPath = true; return JSValue::encode(getByValWithThis(globalObject, callFrame, profile, baseValue, subscript, thisValue)); } JSC_DEFINE_JIT_OPERATION(operationGetByValWithThisOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, ArrayProfile* profile, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, EncodedJSValue encodedThis)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); JSValue baseValue = JSValue::decode(encodedBase); JSValue subscript = JSValue::decode(encodedSubscript); JSValue thisValue = JSValue::decode(encodedThis); CodeBlock* codeBlock = callFrame->codeBlock(); if (baseValue.isCell() && subscript.isInt32()) { Structure* structure = baseValue.asCell()->structure(); if (stubInfo->considerCachingGeneric(vm, codeBlock, structure)) { if (profile) { ConcurrentJSLocker locker(codeBlock->m_lock); profile->computeUpdatedPrediction(locker, codeBlock, structure); } repatchArrayGetByVal(globalObject, codeBlock, baseValue, subscript, *stubInfo, GetByKind::ByValWithThis); } } PropertySlot slot(thisValue, PropertySlot::PropertySlot::InternalMethodType::Get); if (baseValue.isCell() && CacheableIdentifier::isCacheableIdentifierCell(subscript)) { const Identifier propertyName = subscript.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, encodedJSValue()); if (subscript.isSymbol() || !parseIndex(propertyName)) { scope.release(); return JSValue::encode(baseValue.getPropertySlot(globalObject, propertyName, slot, [&] (bool found, PropertySlot& slot) -> JSValue { LOG_IC((ICEvent::OperationGetByValWithThisOptimize, baseValue.classInfoOrNull(), propertyName, baseValue == slot.slotBase())); CacheableIdentifier identifier = CacheableIdentifier::createFromCell(subscript.asCell()); if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(), identifier)) repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::ByValWithThis); return found ? slot.getValue(globalObject, propertyName) : jsUndefined(); })); } } RELEASE_AND_RETURN(scope, JSValue::encode(getByValWithThis(globalObject, callFrame, profile, baseValue, subscript, thisValue))); } JSC_DEFINE_JIT_OPERATION(operationGetByValWithThis, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedBase, EncodedJSValue encodedProperty, EncodedJSValue encodedThis)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); JSValue baseValue = JSValue::decode(encodedBase); JSValue property = JSValue::decode(encodedProperty); JSValue thisValue = JSValue::decode(encodedThis); if (LIKELY(baseValue.isCell())) { JSCell* base = baseValue.asCell(); if (std::optional index = property.tryGetAsUint32Index()) RELEASE_AND_RETURN(scope, getByValWithIndexAndThis(globalObject, base, *index, thisValue)); if (property.isString()) { Structure& structure = *base->structure(); if (JSCell::canUseFastGetOwnProperty(structure)) { auto existingAtomString = asString(property)->toExistingAtomString(globalObject); RETURN_IF_EXCEPTION(scope, { }); if (!existingAtomString.isNull()) { if (JSValue result = base->fastGetOwnProperty(vm, structure, existingAtomString.impl())) return JSValue::encode(result); } } } } baseValue.requireObjectCoercible(globalObject); RETURN_IF_EXCEPTION(scope, { }); auto propertyName = property.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, { }); PropertySlot slot(thisValue, PropertySlot::PropertySlot::InternalMethodType::Get); RELEASE_AND_RETURN(scope, JSValue::encode(baseValue.get(globalObject, propertyName, slot))); } ALWAYS_INLINE static JSValue getPrivateName(JSGlobalObject* globalObject, CallFrame* callFrame, JSValue baseValue, JSValue fieldNameValue) { UNUSED_PARAM(callFrame); VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); JSObject* base = baseValue.toObject(globalObject); RETURN_IF_EXCEPTION(scope, JSValue()); auto fieldName = fieldNameValue.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, JSValue()); PropertySlot slot(base, PropertySlot::InternalMethodType::GetOwnProperty); base->getPrivateField(globalObject, fieldName, slot); RETURN_IF_EXCEPTION(scope, JSValue()); return slot.getValue(globalObject, fieldName); } ALWAYS_INLINE static JSValue getPrivateName(JSGlobalObject* globalObject, CallFrame* callFrame, JSValue baseValue, Identifier fieldName) { ASSERT(fieldName.isPrivateName()); UNUSED_PARAM(callFrame); VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); baseValue.requireObjectCoercible(globalObject); RETURN_IF_EXCEPTION(scope, JSValue()); JSObject* base = baseValue.toObject(globalObject); RETURN_IF_EXCEPTION(scope, JSValue()); PropertySlot slot(base, PropertySlot::InternalMethodType::GetOwnProperty); base->getPrivateField(globalObject, fieldName, slot); RETURN_IF_EXCEPTION(scope, JSValue()); return slot.getValue(globalObject, fieldName); } JSC_DEFINE_JIT_OPERATION(operationGetPrivateNameOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBase, EncodedJSValue encodedFieldName)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); JSValue baseValue = JSValue::decode(encodedBase); JSValue fieldNameValue = JSValue::decode(encodedFieldName); ASSERT(CacheableIdentifier::isCacheableIdentifierCell(fieldNameValue)); CodeBlock* codeBlock = callFrame->codeBlock(); if (baseValue.isObject()) { const Identifier fieldName = fieldNameValue.toPropertyKey(globalObject); EXCEPTION_ASSERT(!scope.exception() || vm.hasPendingTerminationException()); RETURN_IF_EXCEPTION(scope, encodedJSValue()); ASSERT(fieldName.isSymbol()); JSObject* base = jsCast(baseValue.asCell()); PropertySlot slot(base, PropertySlot::InternalMethodType::GetOwnProperty); base->getPrivateField(globalObject, fieldName, slot); RETURN_IF_EXCEPTION(scope, encodedJSValue()); LOG_IC((ICEvent::OperationGetPrivateNameOptimize, baseValue.classInfoOrNull(), fieldName, true)); CacheableIdentifier identifier = CacheableIdentifier::createFromCell(fieldNameValue.asCell()); if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(), identifier)) repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::PrivateName); return JSValue::encode(slot.getValue(globalObject, fieldName)); } RELEASE_AND_RETURN(scope, JSValue::encode(getPrivateName(globalObject, callFrame, baseValue, fieldNameValue))); } JSC_DEFINE_JIT_OPERATION(operationGetPrivateName, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBase, EncodedJSValue encodedFieldName)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue baseValue = JSValue::decode(encodedBase); JSValue fieldNameValue = JSValue::decode(encodedFieldName); if (stubInfo) stubInfo->tookSlowPath = true; return JSValue::encode(getPrivateName(globalObject, callFrame, baseValue, fieldNameValue)); } JSC_DEFINE_JIT_OPERATION(operationGetPrivateNameById, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); stubInfo->tookSlowPath = true; JSValue baseValue = JSValue::decode(base); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier fieldName = Identifier::fromUid(vm, identifier.uid()); JSValue result = getPrivateName(globalObject, callFrame, baseValue, fieldName); LOG_IC((ICEvent::OperationGetPrivateNameById, baseValue.classInfoOrNull(), fieldName, true)); return JSValue::encode(result); } JSC_DEFINE_JIT_OPERATION(operationGetPrivateNameByIdOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); auto scope = DECLARE_THROW_SCOPE(vm); JSValue baseValue = JSValue::decode(base); auto fieldName = Identifier::fromUid(vm, identifier.uid()); if (baseValue.isObject()) { JSObject* base = jsCast(baseValue.asCell()); PropertySlot slot(base, PropertySlot::InternalMethodType::GetOwnProperty); base->getPrivateField(globalObject, fieldName, slot); RETURN_IF_EXCEPTION(scope, encodedJSValue()); LOG_IC((ICEvent::OperationGetPrivateNameByIdOptimize, baseValue.classInfoOrNull(), fieldName, true)); CodeBlock* codeBlock = callFrame->codeBlock(); if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(), identifier)) repatchGetBy(globalObject, codeBlock, baseValue, identifier, slot, *stubInfo, GetByKind::PrivateNameById); return JSValue::encode(slot.getValue(globalObject, fieldName)); } RELEASE_AND_RETURN(scope, JSValue::encode(getPrivateName(globalObject, callFrame, baseValue, fieldName))); } JSC_DEFINE_JIT_OPERATION(operationGetPrivateNameByIdGeneric, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue base, uintptr_t rawCacheableIdentifier)) { SuperSamplerScope superSamplerScope(false); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue baseValue = JSValue::decode(base); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier fieldName = Identifier::fromUid(vm, identifier.uid()); JSValue result = getPrivateName(globalObject, callFrame, baseValue, fieldName); LOG_IC((ICEvent::OperationGetPrivateNameByIdGeneric, baseValue.classInfoOrNull(), fieldName, true)); return JSValue::encode(result); } static bool deleteById(JSGlobalObject* globalObject, VM& vm, DeletePropertySlot& slot, JSValue base, const Identifier& ident, ECMAMode ecmaMode) { auto scope = DECLARE_THROW_SCOPE(vm); JSObject* baseObj = base.toObject(globalObject); RETURN_IF_EXCEPTION(scope, false); if (!baseObj) return false; bool couldDelete = baseObj->methodTable()->deleteProperty(baseObj, globalObject, ident, slot); RETURN_IF_EXCEPTION(scope, false); if (!couldDelete && ecmaMode.isStrict()) throwTypeError(globalObject, scope, UnableToDeletePropertyError); return couldDelete; } JSC_DEFINE_JIT_OPERATION(operationDeleteByIdOptimize, size_t, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier, ECMAMode ecmaMode)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); JSValue baseValue = JSValue::decode(encodedBase); DeletePropertySlot slot; Structure* oldStructure = baseValue.structureOrNull(); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); bool result = deleteById(globalObject, vm, slot, baseValue, ident, ecmaMode); RETURN_IF_EXCEPTION(scope, encodedJSValue()); if (baseValue.isObject()) { if (!parseIndex(ident)) { CodeBlock* codeBlock = callFrame->codeBlock(); if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(), identifier)) repatchDeleteBy(globalObject, codeBlock, slot, baseValue, oldStructure, identifier, *stubInfo, DelByKind::ById, ecmaMode); } } return result; } JSC_DEFINE_JIT_OPERATION(operationDeleteByIdGeneric, size_t, (JSGlobalObject* globalObject, StructureStubInfo*, EncodedJSValue encodedBase, uintptr_t rawCacheableIdentifier, ECMAMode ecmaMode)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); CacheableIdentifier identifier = CacheableIdentifier::createFromRawBits(rawCacheableIdentifier); Identifier ident = Identifier::fromUid(vm, identifier.uid()); DeletePropertySlot slot; return deleteById(globalObject, vm, slot, JSValue::decode(encodedBase), ident, ecmaMode); } static bool deleteByVal(JSGlobalObject* globalObject, VM& vm, DeletePropertySlot& slot, JSValue base, JSValue key, ECMAMode ecmaMode) { auto scope = DECLARE_THROW_SCOPE(vm); JSObject* baseObj = base.toObject(globalObject); RETURN_IF_EXCEPTION(scope, false); if (!baseObj) return false; bool couldDelete; uint32_t index; if (key.getUInt32(index)) couldDelete = baseObj->methodTable()->deletePropertyByIndex(baseObj, globalObject, index); else { Identifier property = key.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, false); couldDelete = baseObj->methodTable()->deleteProperty(baseObj, globalObject, property, slot); } RETURN_IF_EXCEPTION(scope, false); if (!couldDelete && ecmaMode.isStrict()) throwTypeError(globalObject, scope, UnableToDeletePropertyError); return couldDelete; } JSC_DEFINE_JIT_OPERATION(operationDeleteByValOptimize, size_t, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ECMAMode ecmaMode)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); JSValue baseValue = JSValue::decode(encodedBase); JSValue subscript = JSValue::decode(encodedSubscript); DeletePropertySlot slot; Structure* oldStructure = baseValue.structureOrNull(); bool result = deleteByVal(globalObject, vm, slot, baseValue, subscript, ecmaMode); RETURN_IF_EXCEPTION(scope, encodedJSValue()); if (baseValue.isObject() && CacheableIdentifier::isCacheableIdentifierCell(subscript)) { const Identifier propertyName = subscript.toPropertyKey(globalObject); RETURN_IF_EXCEPTION(scope, encodedJSValue()); if (subscript.isSymbol() || !parseIndex(propertyName)) { CodeBlock* codeBlock = callFrame->codeBlock(); CacheableIdentifier identifier = CacheableIdentifier::createFromCell(subscript.asCell()); if (stubInfo->considerCachingBy(vm, codeBlock, baseValue.structureOrNull(), identifier)) repatchDeleteBy(globalObject, codeBlock, slot, baseValue, oldStructure, identifier, *stubInfo, DelByKind::ByVal, ecmaMode); } } return result; } JSC_DEFINE_JIT_OPERATION(operationDeleteByValGeneric, size_t, (JSGlobalObject* globalObject, StructureStubInfo*, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, ECMAMode ecmaMode)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); DeletePropertySlot slot; return deleteByVal(globalObject, vm, slot, JSValue::decode(encodedBase), JSValue::decode(encodedSubscript), ecmaMode); } JSC_DEFINE_JIT_OPERATION(operationPushWithScope, JSCell*, (JSGlobalObject* globalObject, JSCell* currentScopeCell, EncodedJSValue objectValue)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); JSObject* object = JSValue::decode(objectValue).toObject(globalObject); RETURN_IF_EXCEPTION(scope, nullptr); JSScope* currentScope = jsCast(currentScopeCell); return JSWithScope::create(vm, globalObject, currentScope, object); } JSC_DEFINE_JIT_OPERATION(operationPushWithScopeObject, JSCell*, (JSGlobalObject* globalObject, JSCell* currentScopeCell, JSObject* object)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSScope* currentScope = jsCast(currentScopeCell); return JSWithScope::create(vm, globalObject, currentScope, object); } JSC_DEFINE_JIT_OPERATION(operationInstanceOfGeneric, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedProto)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue value = JSValue::decode(encodedValue); JSValue proto = JSValue::decode(encodedProto); stubInfo->tookSlowPath = true; bool result = JSObject::defaultHasInstance(globalObject, value, proto); return JSValue::encode(jsBoolean(result)); } JSC_DEFINE_JIT_OPERATION(operationInstanceOfOptimize, EncodedJSValue, (JSGlobalObject* globalObject, StructureStubInfo* stubInfo, EncodedJSValue encodedValue, EncodedJSValue encodedProto)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); JSValue value = JSValue::decode(encodedValue); JSValue proto = JSValue::decode(encodedProto); bool result = JSObject::defaultHasInstance(globalObject, value, proto); RETURN_IF_EXCEPTION(scope, JSValue::encode(jsUndefined())); CodeBlock* codeBlock = callFrame->codeBlock(); if (stubInfo->considerCachingGeneric(vm, codeBlock, value.structureOrNull())) repatchInstanceOf(globalObject, codeBlock, value, proto, *stubInfo, result); return JSValue::encode(jsBoolean(result)); } JSC_DEFINE_JIT_OPERATION(operationSizeFrameForForwardArguments, int32_t, (JSGlobalObject* globalObject, EncodedJSValue, int32_t numUsedStackSlots, int32_t)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return sizeFrameForForwardArguments(globalObject, callFrame, vm, numUsedStackSlots); } JSC_DEFINE_JIT_OPERATION(operationSizeFrameForVarargs, int32_t, (JSGlobalObject* globalObject, EncodedJSValue encodedArguments, int32_t numUsedStackSlots, int32_t firstVarArgOffset)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue arguments = JSValue::decode(encodedArguments); return sizeFrameForVarargs(globalObject, callFrame, vm, arguments, numUsedStackSlots, firstVarArgOffset); } JSC_DEFINE_JIT_OPERATION(operationSetupForwardArgumentsFrame, CallFrame*, (JSGlobalObject* globalObject, CallFrame* newCallFrame, EncodedJSValue, int32_t, int32_t length)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); setupForwardArgumentsFrame(globalObject, callFrame, newCallFrame, length); return newCallFrame; } JSC_DEFINE_JIT_OPERATION(operationSetupVarargsFrame, CallFrame*, (JSGlobalObject* globalObject, CallFrame* newCallFrame, EncodedJSValue encodedArguments, int32_t firstVarArgOffset, int32_t length)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue arguments = JSValue::decode(encodedArguments); setupVarargsFrame(globalObject, callFrame, newCallFrame, arguments, firstVarArgOffset, length); return newCallFrame; } JSC_DEFINE_JIT_OPERATION(operationSwitchCharWithUnknownKeyType, char*, (JSGlobalObject* globalObject, EncodedJSValue encodedKey, size_t tableIndex, int32_t min)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto throwScope = DECLARE_THROW_SCOPE(vm); JSValue key = JSValue::decode(encodedKey); CodeBlock* codeBlock = callFrame->codeBlock(); const SimpleJumpTable& linkedTable = codeBlock->baselineSwitchJumpTable(tableIndex); ASSERT(codeBlock->unlinkedSwitchJumpTable(tableIndex).m_min == min); void* result = linkedTable.m_ctiDefault.taggedPtr(); if (key.isString()) { JSString* string = asString(key); if (string->length() == 1) { String value = string->value(globalObject); RETURN_IF_EXCEPTION(throwScope, nullptr); result = linkedTable.ctiForValue(min, value[0]).taggedPtr(); } } assertIsTaggedWith(result); return reinterpret_cast(result); } JSC_DEFINE_JIT_OPERATION(operationSwitchImmWithUnknownKeyType, char*, (VM* vmPointer, EncodedJSValue encodedKey, size_t tableIndex, int32_t min)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue key = JSValue::decode(encodedKey); CodeBlock* codeBlock = callFrame->codeBlock(); const SimpleJumpTable& linkedTable = codeBlock->baselineSwitchJumpTable(tableIndex); ASSERT(codeBlock->unlinkedSwitchJumpTable(tableIndex).m_min == min); void* result; if (key.isInt32()) result = linkedTable.ctiForValue(min, key.asInt32()).taggedPtr(); else if (key.isDouble() && key.asDouble() == static_cast(key.asDouble())) result = linkedTable.ctiForValue(min, static_cast(key.asDouble())).taggedPtr(); else result = linkedTable.m_ctiDefault.taggedPtr(); assertIsTaggedWith(result); return reinterpret_cast(result); } JSC_DEFINE_JIT_OPERATION(operationSwitchStringWithUnknownKeyType, char*, (JSGlobalObject* globalObject, EncodedJSValue encodedKey, size_t tableIndex)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue key = JSValue::decode(encodedKey); CodeBlock* codeBlock = callFrame->codeBlock(); auto throwScope = DECLARE_THROW_SCOPE(vm); void* result; const StringJumpTable& linkedTable = codeBlock->baselineStringSwitchJumpTable(tableIndex); if (key.isString()) { StringImpl* value = asString(key)->value(globalObject).impl(); RETURN_IF_EXCEPTION(throwScope, nullptr); const UnlinkedStringJumpTable& unlinkedTable = codeBlock->unlinkedStringSwitchJumpTable(tableIndex); result = linkedTable.ctiForValue(unlinkedTable, value).taggedPtr(); } else result = linkedTable.ctiDefault().taggedPtr(); assertIsTaggedWith(result); return reinterpret_cast(result); } JSC_DEFINE_JIT_OPERATION(operationResolveScopeForBaseline, EncodedJSValue, (JSGlobalObject* globalObject, const JSInstruction* pc)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto throwScope = DECLARE_THROW_SCOPE(vm); CodeBlock* codeBlock = callFrame->codeBlock(); auto bytecode = pc->as(); const Identifier& ident = codeBlock->identifier(bytecode.m_var); JSScope* scope = callFrame->uncheckedR(bytecode.m_scope).Register::scope(); JSObject* resolvedScope = JSScope::resolve(globalObject, scope, ident); // Proxy can throw an error here, e.g. Proxy in with statement's @unscopables. RETURN_IF_EXCEPTION(throwScope, { }); auto& metadata = bytecode.metadata(codeBlock); ResolveType resolveType = metadata.m_resolveType; // ModuleVar does not keep the scope register value alive in DFG. ASSERT(resolveType != ModuleVar); switch (resolveType) { case GlobalProperty: case GlobalPropertyWithVarInjectionChecks: case UnresolvedProperty: case UnresolvedPropertyWithVarInjectionChecks: { if (resolvedScope->isGlobalObject()) { JSGlobalObject* globalObject = jsCast(resolvedScope); bool hasProperty = globalObject->hasProperty(globalObject, ident); RETURN_IF_EXCEPTION(throwScope, { }); if (hasProperty) { ConcurrentJSLocker locker(codeBlock->m_lock); metadata.m_resolveType = needsVarInjectionChecks(resolveType) ? GlobalPropertyWithVarInjectionChecks : GlobalProperty; metadata.m_globalObject.set(vm, codeBlock, globalObject); metadata.m_globalLexicalBindingEpoch = globalObject->globalLexicalBindingEpoch(); } } else if (resolvedScope->isGlobalLexicalEnvironment()) { JSGlobalLexicalEnvironment* globalLexicalEnvironment = jsCast(resolvedScope); ConcurrentJSLocker locker(codeBlock->m_lock); metadata.m_resolveType = needsVarInjectionChecks(resolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar; metadata.m_globalLexicalEnvironment.set(vm, codeBlock, globalLexicalEnvironment); } break; } default: break; } return JSValue::encode(resolvedScope); } JSC_DEFINE_JIT_OPERATION(operationGetFromScope, EncodedJSValue, (JSGlobalObject* globalObject, const JSInstruction* pc)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto throwScope = DECLARE_THROW_SCOPE(vm); CodeBlock* codeBlock = callFrame->codeBlock(); auto bytecode = pc->as(); const Identifier& ident = codeBlock->identifier(bytecode.m_var); JSObject* scope = jsCast(callFrame->uncheckedR(bytecode.m_scope).jsValue()); GetPutInfo& getPutInfo = bytecode.metadata(codeBlock).m_getPutInfo; // ModuleVar is always converted to ClosureVar for get_from_scope. ASSERT(getPutInfo.resolveType() != ModuleVar); RELEASE_AND_RETURN(throwScope, JSValue::encode(scope->getPropertySlot(globalObject, ident, [&] (bool found, PropertySlot& slot) -> JSValue { if (!found) { if (getPutInfo.resolveMode() == ThrowIfNotFound) throwException(globalObject, throwScope, createUndefinedVariableError(globalObject, ident)); return jsUndefined(); } JSValue result = JSValue(); if (scope->isGlobalLexicalEnvironment()) { // When we can't statically prove we need a TDZ check, we must perform the check on the slow path. result = slot.getValue(globalObject, ident); if (result == jsTDZValue()) { throwException(globalObject, throwScope, createTDZError(globalObject)); return jsUndefined(); } } CommonSlowPaths::tryCacheGetFromScopeGlobal(globalObject, codeBlock, vm, bytecode, scope, slot, ident); if (!result) return slot.getValue(globalObject, ident); return result; }))); } JSC_DEFINE_JIT_OPERATION(operationPutToScope, void, (JSGlobalObject* globalObject, const JSInstruction* pc)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto throwScope = DECLARE_THROW_SCOPE(vm); CodeBlock* codeBlock = callFrame->codeBlock(); auto bytecode = pc->as(); auto& metadata = bytecode.metadata(codeBlock); const Identifier& ident = codeBlock->identifier(bytecode.m_var); JSObject* scope = jsCast(callFrame->uncheckedR(bytecode.m_scope).jsValue()); JSValue value = callFrame->r(bytecode.m_value).jsValue(); GetPutInfo& getPutInfo = metadata.m_getPutInfo; // ModuleVar does not keep the scope register value alive in DFG. ASSERT(getPutInfo.resolveType() != ModuleVar); if (getPutInfo.resolveType() == ResolvedClosureVar) { JSLexicalEnvironment* environment = jsCast(scope); environment->variableAt(ScopeOffset(metadata.m_operand)).set(vm, environment, value); if (WatchpointSet* set = metadata.m_watchpointSet) set->touch(vm, "Executed op_put_scope"); return; } bool hasProperty = scope->hasProperty(globalObject, ident); RETURN_IF_EXCEPTION(throwScope, void()); if (hasProperty && scope->isGlobalLexicalEnvironment() && !isInitialization(getPutInfo.initializationMode())) { // When we can't statically prove we need a TDZ check, we must perform the check on the slow path. PropertySlot slot(scope, PropertySlot::InternalMethodType::Get); JSGlobalLexicalEnvironment::getOwnPropertySlot(scope, globalObject, ident, slot); if (slot.getValue(globalObject, ident) == jsTDZValue()) { throwException(globalObject, throwScope, createTDZError(globalObject)); return; } } if (getPutInfo.resolveMode() == ThrowIfNotFound && !hasProperty) { throwException(globalObject, throwScope, createUndefinedVariableError(globalObject, ident)); return; } PutPropertySlot slot(scope, getPutInfo.ecmaMode().isStrict(), PutPropertySlot::UnknownContext, isInitialization(getPutInfo.initializationMode())); scope->methodTable()->put(scope, globalObject, ident, value, slot); RETURN_IF_EXCEPTION(throwScope, void()); CommonSlowPaths::tryCachePutToScopeGlobal(globalObject, codeBlock, bytecode, scope, slot, ident); } JSC_DEFINE_JIT_OPERATION(operationThrow, void, (JSGlobalObject* globalObject, EncodedJSValue encodedExceptionValue)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); JSValue exceptionValue = JSValue::decode(encodedExceptionValue); throwException(globalObject, scope, exceptionValue); // Results stored out-of-band in vm.targetMachinePCForThrow & vm.callFrameForCatch genericUnwind(vm, callFrame); } JSC_DEFINE_JIT_OPERATION(operationReallocateButterflyToHavePropertyStorageWithInitialCapacity, char*, (VM* vmPointer, JSObject* object)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); ASSERT(!object->structure()->outOfLineCapacity()); Butterfly* result = object->allocateMoreOutOfLineStorage(vm, 0, initialOutOfLineCapacity); object->nukeStructureAndSetButterfly(vm, object->structureID(), result); return reinterpret_cast(result); } JSC_DEFINE_JIT_OPERATION(operationReallocateButterflyToGrowPropertyStorage, char*, (VM* vmPointer, JSObject* object, size_t newSize)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); Butterfly* result = object->allocateMoreOutOfLineStorage(vm, object->structure()->outOfLineCapacity(), newSize); object->nukeStructureAndSetButterfly(vm, object->structureID(), result); return reinterpret_cast(result); } JSC_DEFINE_JIT_OPERATION(operationOSRWriteBarrier, void, (VM* vmPointer, JSCell* cell)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); vm.writeBarrier(cell); } JSC_DEFINE_JIT_OPERATION(operationWriteBarrierSlowPath, void, (VM* vmPointer, JSCell* cell)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); vm.writeBarrierSlowPath(cell); } JSC_DEFINE_JIT_OPERATION(operationLookupExceptionHandler, void, (VM* vmPointer)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); genericUnwind(vm, callFrame); ASSERT(vm.targetMachinePCForThrow); } JSC_DEFINE_JIT_OPERATION(operationLookupExceptionHandlerFromCallerFrame, void, (VM* vmPointer)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); ASSERT(callFrame->isStackOverflowFrame()); ASSERT(jsCast(vm.exceptionForInspection()->value().asCell())->isStackOverflowError()); genericUnwind(vm, callFrame); ASSERT(vm.targetMachinePCForThrow); } JSC_DEFINE_JIT_OPERATION(operationVMHandleException, void, (VM* vmPointer)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); genericUnwind(vm, callFrame); } // This function "should" just take the JSGlobalObject*, but doing so would make it more difficult // to call from exception check sites. So, unlike all of our other functions, we allow // ourselves to play some gnarly ABI tricks just to simplify the calling convention. This is // particularly safe here since this is never called on the critical path - it's only for // testing. JSC_DEFINE_JIT_OPERATION(operationExceptionFuzz, void, (JSGlobalObject* globalObject)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); UNUSED_PARAM(scope); #if COMPILER(GCC_COMPATIBLE) void* returnPC = __builtin_return_address(0); // FIXME (see rdar://72897291): Work around a Clang bug where __builtin_return_address() // sometimes gives us a signed pointer, and sometimes does not. returnPC = removeCodePtrTag(returnPC); doExceptionFuzzing(globalObject, scope, "JITOperations", returnPC); #endif // COMPILER(GCC_COMPATIBLE) } JSC_DEFINE_JIT_OPERATION(operationExceptionFuzzWithCallFrame, void, (VM* vmPointer)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); UNUSED_PARAM(scope); #if COMPILER(GCC_COMPATIBLE) void* returnPC = __builtin_return_address(0); // FIXME (see rdar://72897291): Work around a Clang bug where __builtin_return_address() // sometimes gives us a signed pointer, and sometimes does not. returnPC = removeCodePtrTag(returnPC); doExceptionFuzzing(callFrame->lexicalGlobalObject(vm), scope, "JITOperations", returnPC); #endif // COMPILER(GCC_COMPATIBLE) } JSC_DEFINE_JIT_OPERATION(operationValueAdd, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return JSValue::encode(jsAdd(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2))); } JSC_DEFINE_JIT_OPERATION(operationValueAddProfiled, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, BinaryArithProfile* arithProfile)) { ASSERT(arithProfile); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return JSValue::encode(profiledAdd(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2), *arithProfile)); } JSC_DEFINE_JIT_OPERATION(operationValueAddProfiledOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITAddIC* addIC)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue op1 = JSValue::decode(encodedOp1); JSValue op2 = JSValue::decode(encodedOp2); BinaryArithProfile* arithProfile = addIC->arithProfile(); ASSERT(arithProfile); arithProfile->observeLHSAndRHS(op1, op2); auto nonOptimizeVariant = operationValueAddProfiledNoOptimize; addIC->generateOutOfLine(callFrame->codeBlock(), nonOptimizeVariant); #if ENABLE(MATH_IC_STATS) callFrame->codeBlock()->dumpMathICStats(); #endif JSValue result = jsAdd(globalObject, op1, op2); arithProfile->observeResult(result); return JSValue::encode(result); } JSC_DEFINE_JIT_OPERATION(operationValueAddProfiledNoOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITAddIC* addIC)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); BinaryArithProfile* arithProfile = addIC->arithProfile(); ASSERT(arithProfile); return JSValue::encode(profiledAdd(globalObject, JSValue::decode(encodedOp1), JSValue::decode(encodedOp2), *arithProfile)); } JSC_DEFINE_JIT_OPERATION(operationValueAddOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITAddIC* addIC)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue op1 = JSValue::decode(encodedOp1); JSValue op2 = JSValue::decode(encodedOp2); auto nonOptimizeVariant = operationValueAddNoOptimize; if (BinaryArithProfile* arithProfile = addIC->arithProfile()) arithProfile->observeLHSAndRHS(op1, op2); addIC->generateOutOfLine(callFrame->codeBlock(), nonOptimizeVariant); #if ENABLE(MATH_IC_STATS) callFrame->codeBlock()->dumpMathICStats(); #endif return JSValue::encode(jsAdd(globalObject, op1, op2)); } JSC_DEFINE_JIT_OPERATION(operationValueAddNoOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITAddIC*)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue op1 = JSValue::decode(encodedOp1); JSValue op2 = JSValue::decode(encodedOp2); JSValue result = jsAdd(globalObject, op1, op2); return JSValue::encode(result); } ALWAYS_INLINE static EncodedJSValue unprofiledMul(JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) { JSValue op1 = JSValue::decode(encodedOp1); JSValue op2 = JSValue::decode(encodedOp2); return JSValue::encode(jsMul(globalObject, op1, op2)); } ALWAYS_INLINE static EncodedJSValue profiledMul(JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, BinaryArithProfile& arithProfile, bool shouldObserveLHSAndRHSTypes = true) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); JSValue op1 = JSValue::decode(encodedOp1); JSValue op2 = JSValue::decode(encodedOp2); if (shouldObserveLHSAndRHSTypes) arithProfile.observeLHSAndRHS(op1, op2); JSValue result = jsMul(globalObject, op1, op2); RETURN_IF_EXCEPTION(scope, encodedJSValue()); arithProfile.observeResult(result); return JSValue::encode(result); } JSC_DEFINE_JIT_OPERATION(operationValueMul, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return unprofiledMul(globalObject, encodedOp1, encodedOp2); } JSC_DEFINE_JIT_OPERATION(operationValueMulNoOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC*)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return unprofiledMul(globalObject, encodedOp1, encodedOp2); } JSC_DEFINE_JIT_OPERATION(operationValueMulOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto nonOptimizeVariant = operationValueMulNoOptimize; if (BinaryArithProfile* arithProfile = mulIC->arithProfile()) arithProfile->observeLHSAndRHS(JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)); mulIC->generateOutOfLine(callFrame->codeBlock(), nonOptimizeVariant); #if ENABLE(MATH_IC_STATS) callFrame->codeBlock()->dumpMathICStats(); #endif return unprofiledMul(globalObject, encodedOp1, encodedOp2); } JSC_DEFINE_JIT_OPERATION(operationValueMulProfiled, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, BinaryArithProfile* arithProfile)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); ASSERT(arithProfile); return profiledMul(globalObject, encodedOp1, encodedOp2, *arithProfile); } JSC_DEFINE_JIT_OPERATION(operationValueMulProfiledOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); BinaryArithProfile* arithProfile = mulIC->arithProfile(); ASSERT(arithProfile); arithProfile->observeLHSAndRHS(JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)); auto nonOptimizeVariant = operationValueMulProfiledNoOptimize; mulIC->generateOutOfLine(callFrame->codeBlock(), nonOptimizeVariant); #if ENABLE(MATH_IC_STATS) callFrame->codeBlock()->dumpMathICStats(); #endif return profiledMul(globalObject, encodedOp1, encodedOp2, *arithProfile, false); } JSC_DEFINE_JIT_OPERATION(operationValueMulProfiledNoOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITMulIC* mulIC)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); BinaryArithProfile* arithProfile = mulIC->arithProfile(); ASSERT(arithProfile); return profiledMul(globalObject, encodedOp1, encodedOp2, *arithProfile); } // FIXME: it would be better to call those operationValueNegate, since the operand can be a BigInt JSC_DEFINE_JIT_OPERATION(operationArithNegate, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOperand)) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue operand = JSValue::decode(encodedOperand); JSValue primValue = operand.toPrimitive(globalObject, PreferNumber); RETURN_IF_EXCEPTION(scope, encodedJSValue()); #if USE(BIGINT32) if (primValue.isBigInt32()) RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::unaryMinus(globalObject, primValue.bigInt32AsInt32()))); #endif if (primValue.isHeapBigInt()) RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::unaryMinus(globalObject, primValue.asHeapBigInt()))); double number = primValue.toNumber(globalObject); RETURN_IF_EXCEPTION(scope, encodedJSValue()); return JSValue::encode(jsNumber(-number)); } // FIXME: it would be better to call those operationValueNegate, since the operand can be a BigInt JSC_DEFINE_JIT_OPERATION(operationArithNegateProfiled, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOperand, UnaryArithProfile* arithProfile)) { ASSERT(arithProfile); VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue operand = JSValue::decode(encodedOperand); arithProfile->observeArg(operand); JSValue primValue = operand.toPrimitive(globalObject, PreferNumber); RETURN_IF_EXCEPTION(scope, encodedJSValue()); #if USE(BIGINT32) if (primValue.isBigInt32()) { JSValue result = JSBigInt::unaryMinus(globalObject, primValue.bigInt32AsInt32()); RETURN_IF_EXCEPTION(scope, { }); arithProfile->observeResult(result); return JSValue::encode(result); } #endif if (primValue.isHeapBigInt()) { JSValue result = JSBigInt::unaryMinus(globalObject, primValue.asHeapBigInt()); RETURN_IF_EXCEPTION(scope, { }); arithProfile->observeResult(result); return JSValue::encode(result); } double number = primValue.toNumber(globalObject); RETURN_IF_EXCEPTION(scope, encodedJSValue()); JSValue result = jsNumber(-number); arithProfile->observeResult(result); return JSValue::encode(result); } // FIXME: it would be better to call those operationValueNegate, since the operand can be a BigInt JSC_DEFINE_JIT_OPERATION(operationArithNegateProfiledOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOperand, JITNegIC* negIC)) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue operand = JSValue::decode(encodedOperand); UnaryArithProfile* arithProfile = negIC->arithProfile(); ASSERT(arithProfile); arithProfile->observeArg(operand); negIC->generateOutOfLine(callFrame->codeBlock(), operationArithNegateProfiled); #if ENABLE(MATH_IC_STATS) callFrame->codeBlock()->dumpMathICStats(); #endif JSValue primValue = operand.toPrimitive(globalObject, PreferNumber); RETURN_IF_EXCEPTION(scope, encodedJSValue()); #if USE(BIGINT32) if (primValue.isBigInt32()) { JSValue result = JSBigInt::unaryMinus(globalObject, primValue.bigInt32AsInt32()); RETURN_IF_EXCEPTION(scope, { }); arithProfile->observeResult(result); return JSValue::encode(result); } #endif if (primValue.isHeapBigInt()) { JSValue result = JSBigInt::unaryMinus(globalObject, primValue.asHeapBigInt()); RETURN_IF_EXCEPTION(scope, { }); arithProfile->observeResult(result); return JSValue::encode(result); } double number = primValue.toNumber(globalObject); RETURN_IF_EXCEPTION(scope, encodedJSValue()); JSValue result = jsNumber(-number); arithProfile->observeResult(result); return JSValue::encode(result); } // FIXME: it would be better to call those operationValueNegate, since the operand can be a BigInt JSC_DEFINE_JIT_OPERATION(operationArithNegateOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOperand, JITNegIC* negIC)) { VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); JSValue operand = JSValue::decode(encodedOperand); if (UnaryArithProfile* arithProfile = negIC->arithProfile()) arithProfile->observeArg(operand); negIC->generateOutOfLine(callFrame->codeBlock(), operationArithNegate); #if ENABLE(MATH_IC_STATS) callFrame->codeBlock()->dumpMathICStats(); #endif JSValue primValue = operand.toPrimitive(globalObject, PreferNumber); RETURN_IF_EXCEPTION(scope, encodedJSValue()); #if USE(BIGINT32) // FIXME: why does this function profile the argument but not the result? if (primValue.isBigInt32()) RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::unaryMinus(globalObject, primValue.bigInt32AsInt32()))); #endif if (primValue.isHeapBigInt()) RELEASE_AND_RETURN(scope, JSValue::encode(JSBigInt::unaryMinus(globalObject, primValue.asHeapBigInt()))); double number = primValue.toNumber(globalObject); RETURN_IF_EXCEPTION(scope, encodedJSValue()); return JSValue::encode(jsNumber(-number)); } ALWAYS_INLINE static EncodedJSValue unprofiledSub(JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2) { JSValue op1 = JSValue::decode(encodedOp1); JSValue op2 = JSValue::decode(encodedOp2); return JSValue::encode(jsSub(globalObject, op1, op2)); } ALWAYS_INLINE static EncodedJSValue profiledSub(VM& vm, JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, BinaryArithProfile& arithProfile, bool shouldObserveLHSAndRHSTypes = true) { auto scope = DECLARE_THROW_SCOPE(vm); JSValue op1 = JSValue::decode(encodedOp1); JSValue op2 = JSValue::decode(encodedOp2); if (shouldObserveLHSAndRHSTypes) arithProfile.observeLHSAndRHS(op1, op2); JSValue result = jsSub(globalObject, op1, op2); RETURN_IF_EXCEPTION(scope, encodedJSValue()); arithProfile.observeResult(result); return JSValue::encode(result); } JSC_DEFINE_JIT_OPERATION(operationValueSub, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return unprofiledSub(globalObject, encodedOp1, encodedOp2); } JSC_DEFINE_JIT_OPERATION(operationValueSubProfiled, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, BinaryArithProfile* arithProfile)) { ASSERT(arithProfile); VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return profiledSub(vm, globalObject, encodedOp1, encodedOp2, *arithProfile); } JSC_DEFINE_JIT_OPERATION(operationValueSubOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC* subIC)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto nonOptimizeVariant = operationValueSubNoOptimize; if (BinaryArithProfile* arithProfile = subIC->arithProfile()) arithProfile->observeLHSAndRHS(JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)); subIC->generateOutOfLine(callFrame->codeBlock(), nonOptimizeVariant); #if ENABLE(MATH_IC_STATS) callFrame->codeBlock()->dumpMathICStats(); #endif return unprofiledSub(globalObject, encodedOp1, encodedOp2); } JSC_DEFINE_JIT_OPERATION(operationValueSubNoOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC*)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); return unprofiledSub(globalObject, encodedOp1, encodedOp2); } JSC_DEFINE_JIT_OPERATION(operationValueSubProfiledOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC* subIC)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); BinaryArithProfile* arithProfile = subIC->arithProfile(); ASSERT(arithProfile); arithProfile->observeLHSAndRHS(JSValue::decode(encodedOp1), JSValue::decode(encodedOp2)); auto nonOptimizeVariant = operationValueSubProfiledNoOptimize; subIC->generateOutOfLine(callFrame->codeBlock(), nonOptimizeVariant); #if ENABLE(MATH_IC_STATS) callFrame->codeBlock()->dumpMathICStats(); #endif return profiledSub(vm, globalObject, encodedOp1, encodedOp2, *arithProfile, false); } JSC_DEFINE_JIT_OPERATION(operationValueSubProfiledNoOptimize, EncodedJSValue, (JSGlobalObject* globalObject, EncodedJSValue encodedOp1, EncodedJSValue encodedOp2, JITSubIC* subIC)) { VM& vm = globalObject->vm(); CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); BinaryArithProfile* arithProfile = subIC->arithProfile(); ASSERT(arithProfile); return profiledSub(vm, globalObject, encodedOp1, encodedOp2, *arithProfile); } JSC_DEFINE_JIT_OPERATION(operationDebuggerWillCallNativeExecutable, void, (CallFrame* callFrame)) { ASSERT(!callFrame->isWasmFrame()); auto* globalObject = callFrame->jsCallee()->globalObject(); if (!globalObject) return; auto* debugger = globalObject->debugger(); if (!debugger) return; debugger->willCallNativeExecutable(callFrame); } JSC_DEFINE_JIT_OPERATION(operationProcessTypeProfilerLog, void, (VM* vmPointer)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); vm.typeProfilerLog()->processLogEntries(vm, "Log Full, called from inside baseline JIT"_s); } JSC_DEFINE_JIT_OPERATION(operationProcessShadowChickenLog, void, (VM* vmPointer)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); RELEASE_ASSERT(vm.shadowChicken()); vm.shadowChicken()->update(vm, callFrame); } JSC_DEFINE_JIT_OPERATION(operationRetrieveAndClearExceptionIfCatchable, JSCell*, (VM* vmPointer)) { VM& vm = *vmPointer; CallFrame* callFrame = DECLARE_CALL_FRAME(vm); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); RELEASE_ASSERT(!!scope.exception()); Exception* exception = scope.exception(); if (UNLIKELY(vm.isTerminationException(exception))) { genericUnwind(vm, callFrame); return nullptr; } // We want to clear the exception here rather than in the catch prologue // JIT code because clearing it also entails clearing a bit in an Atomic // bit field in VMTraps. scope.clearException(); return exception; } } // namespace JSC IGNORE_WARNINGS_END #endif // ENABLE(JIT)