blob: 80f1f06dfc231e0d1472cecd65bb6b2bbba4428e [file] [log] [blame]
/*
* 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) && USE(JSVALUE64)
#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 "JSInternalPromise.h"
#include "JSLexicalEnvironment.h"
#include "JSRemoteFunction.h"
#include "JSWithScope.h"
#include "LLIntEntrypoint.h"
#include "MegamorphicCache.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"
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<OpResolveScope>();
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 = jsCast<JSGlobalObject*>(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 = jsCast<JSGlobalLexicalEnvironment*>(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<OpGetFromScope>();
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<OpPutToScope>();
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 = jsCast<JSLexicalEnvironment*>(jsScope);
environment->variableAt(ScopeOffset(metadata.m_operand)).set(vm, environment, value);
if (RefPtr set = metadata.m_watchpointSet)
set->touch(vm, "Executed op_put_scope<ResolvedClosureVar>");
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) && USE(JSVALUE64)