/* * Copyright (C) 2026 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 "LOLJITOperations.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 "EnsureStillAliveHere.h" #include "ExceptionFuzz.h" #include "FrameTracers.h" #include "GetterSetter.h" #include "ICStats.h" #include "InlineCacheCompiler.h" #include "Interpreter.h" #include "JIT.h" #include "JITExceptions.h" #include "JITThunks.h" #include "JITToDFGDeferredCompilationCallback.h" #include "JITWorklist.h" #include "JSArrayIterator.h" #include "JSAsyncFunction.h" #include "JSAsyncGenerator.h" #include "JSAsyncGeneratorFunction.h" #include "JSBoundFunction.h" #include "JSCInlines.h" #include "JSCPtrTag.h" #include "JSGeneratorFunction.h" #include "JSGlobalObjectFunctions.h" #include "JSLexicalEnvironment.h" #include "JSRemoteFunction.h" #include "JSWithScope.h" #include "LLIntEntrypoint.h" #include "MegamorphicCache.h" #include "ObjectConstructor.h" #include "PropertyInlineCache.h" #include "PropertyName.h" #include "RegExpObject.h" #include "RepatchInlines.h" #include "ShadowChicken.h" #include "SuperSampler.h" #include "ThunkGenerators.h" #include "TypeProfilerLog.h" namespace JSC::LOL { JSC_DEFINE_JIT_OPERATION(operationResolveScopeForLOL, EncodedJSValue, (CallFrame* callFrame, unsigned bytecodeOffset, JSScope* environment)) { CodeBlock* codeBlock = callFrame->codeBlock(); JSGlobalObject* globalObject = codeBlock->globalObject(); VM& vm = globalObject->vm(); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); const JSInstruction* pc = codeBlock->instructionAt(BytecodeIndex(bytecodeOffset)); auto bytecode = pc->as(); const Identifier& ident = codeBlock->identifier(bytecode.m_var); JSObject* resolvedScope = JSScope::resolve(globalObject, environment, ident); // Proxy can throw an error here, e.g. Proxy in with statement's @unscopables. OPERATION_RETURN_IF_EXCEPTION(scope, encodedJSValue()); 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 = uncheckedDowncast(resolvedScope); bool hasProperty = globalObject->hasProperty(globalObject, ident); OPERATION_RETURN_IF_EXCEPTION(scope, encodedJSValue()); 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 = uncheckedDowncast(resolvedScope); ConcurrentJSLocker locker(codeBlock->m_lock); metadata.m_resolveType = needsVarInjectionChecks(resolveType) ? GlobalLexicalVarWithVarInjectionChecks : GlobalLexicalVar; metadata.m_globalLexicalEnvironment.set(vm, codeBlock, globalLexicalEnvironment); } break; } default: break; } OPERATION_RETURN(scope, JSValue::encode(resolvedScope)); } JSC_DEFINE_JIT_OPERATION(operationGetFromScopeForLOL, EncodedJSValue, (CallFrame* callFrame, unsigned bytecodeOffset, JSObject* environment)) { CodeBlock* codeBlock = callFrame->codeBlock(); JSGlobalObject* globalObject = codeBlock->globalObject(); VM& vm = globalObject->vm(); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); const JSInstruction* pc = codeBlock->instructionAt(BytecodeIndex(bytecodeOffset)); auto bytecode = pc->as(); const Identifier& ident = codeBlock->identifier(bytecode.m_var); GetPutInfo& getPutInfo = bytecode.metadata(codeBlock).m_getPutInfo; // ModuleVar is always converted to ClosureVar for get_from_scope. ASSERT(getPutInfo.resolveType() != ModuleVar); OPERATION_RETURN(scope, JSValue::encode(environment->getPropertySlot(globalObject, ident, [&] (bool found, PropertySlot& slot) -> JSValue { if (!found) { if (getPutInfo.resolveMode() == ThrowIfNotFound) throwException(globalObject, scope, createUndefinedVariableError(globalObject, ident)); return jsUndefined(); } JSValue result = JSValue(); if (environment->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, scope, createTDZError(globalObject, ident.string())); return jsUndefined(); } } CommonSlowPaths::tryCacheGetFromScopeGlobal(globalObject, codeBlock, vm, bytecode, environment, slot, ident); if (!result) return slot.getValue(globalObject, ident); return result; }))); } JSC_DEFINE_JIT_OPERATION(operationPutToScopeForLOL, void, (CallFrame* callFrame, unsigned bytecodeOffset, JSObject* jsScope, JSValue value)) { CodeBlock* codeBlock = callFrame->codeBlock(); JSGlobalObject* globalObject = codeBlock->globalObject(); VM& vm = globalObject->vm(); JITOperationPrologueCallFrameTracer tracer(vm, callFrame); auto scope = DECLARE_THROW_SCOPE(vm); const JSInstruction* pc = codeBlock->instructionAt(BytecodeIndex(bytecodeOffset)); auto bytecode = pc->as(); auto& metadata = bytecode.metadata(codeBlock); const Identifier& ident = codeBlock->identifier(bytecode.m_var); 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 = uncheckedDowncast(jsScope); environment->variableAt(ScopeOffset(metadata.m_operand)).set(vm, environment, value); if (InlineWatchpointSet* set = metadata.m_watchpointSet) set->touch(vm, "Executed op_put_scope"); OPERATION_RETURN(scope); } bool hasProperty = jsScope->hasProperty(globalObject, ident); OPERATION_RETURN_IF_EXCEPTION(scope); if (hasProperty && jsScope->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(jsScope, PropertySlot::InternalMethodType::Get); JSGlobalLexicalEnvironment::getOwnPropertySlot(jsScope, globalObject, ident, slot); if (slot.getValue(globalObject, ident) == jsTDZValue()) { throwException(globalObject, scope, createTDZError(globalObject, ident.string())); OPERATION_RETURN(scope); } } if (getPutInfo.resolveMode() == ThrowIfNotFound && !hasProperty) { throwException(globalObject, scope, createUndefinedVariableError(globalObject, ident)); OPERATION_RETURN(scope); } PutPropertySlot slot(jsScope, getPutInfo.ecmaMode().isStrict(), PutPropertySlot::UnknownContext, isInitialization(getPutInfo.initializationMode())); jsScope->methodTable()->put(jsScope, globalObject, ident, value, slot); OPERATION_RETURN_IF_EXCEPTION(scope); CommonSlowPaths::tryCachePutToScopeGlobal(globalObject, codeBlock, bytecode, jsScope, slot, ident); OPERATION_RETURN(scope); } } // namespace JSC::LOL #endif // ENABLE(JIT)