lib/VM/JSCallSite.cpp (308 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #include "hermes/VM/JSCallSite.h" #include "hermes/BCGen/HBC/DebugInfo.h" #include "hermes/Support/OptValue.h" #include "hermes/VM/Callable.h" #include "hermes/VM/RuntimeModule-inline.h" namespace hermes { namespace vm { namespace { /// A helper class with all the fields stored in a CallSite. It is populated by /// callSiteFromSelfHandle below. struct JSCallSiteInfo { Handle<JSError> error; size_t stackFrameIndex; }; /// Raises a TypeError when an incompatible receiver is used for any of the /// CallSite methods below. static ExecutionStatus raiseIncompatibleReceiverError(Runtime &runtime) { return runtime.raiseTypeError( "CallSite method called on an incompatible receiver"); } /// Looks up property \p iProp in \p selfHandle raising a type error if one /// isn't found. /// \return A pseudo-handle to \p iProp. static CallResult<PseudoHandle<>> getCallSiteProp( Runtime &runtime, Handle<JSObject> selfHandle, Predefined::IProp iProp) { auto pof = PropOpFlags().plusMustExist().plusThrowOnError(); CallResult<PseudoHandle<>> res = JSObject::getNamed_RJS( selfHandle, runtime, Predefined::getSymbolID(iProp), pof); if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { runtime.clearThrownValue(); return raiseIncompatibleReceiverError(runtime); } return res; } /// Extracts the CallSite information from \p selfHandle. Raises TypeError if /// any CallSite property isn't found. static CallResult<JSCallSiteInfo> callSiteFromSelfHandle( Runtime &runtime, Handle<> selfHandle) { auto selfObjHandle = Handle<JSObject>::dyn_vmcast(selfHandle); if (LLVM_UNLIKELY(!selfObjHandle)) { return raiseIncompatibleReceiverError(runtime); } auto errorRes = getCallSiteProp( runtime, selfObjHandle, Predefined::InternalPropertyCallSiteError); if (LLVM_UNLIKELY(errorRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } auto error = runtime.makeHandle<JSError>(std::move(*errorRes)); auto frameIndexRes = getCallSiteProp( runtime, selfObjHandle, Predefined::InternalPropertyCallSiteStackFrameIndex); if (LLVM_UNLIKELY(frameIndexRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } const size_t frameIndex = frameIndexRes->getHermesValue().getNumber(); return JSCallSiteInfo{std::move(error), frameIndex}; } /// \return a reference to the stack frame into which this CallSite object is /// a view. CallResult<const StackTraceInfo *> getStackTraceInfo( Runtime &runtime, Handle<> selfHandle) { auto callSiteRes = callSiteFromSelfHandle(runtime, selfHandle); if (LLVM_UNLIKELY(callSiteRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } const StackTrace *stacktrace = callSiteRes->error->getStackTrace(); assert( stacktrace && "The Error associated with this CallSite has already released its " "stack trace vector"); // Returning a pointer to stacktrace elements is safe because: // 1. stacktrace's ownership is managed (indirectly) by selfHandle (i.e., // the CallSite object); and // 2. stacktrace is not modified after it is created. return &stacktrace->at(callSiteRes->stackFrameIndex); } Handle<JSObject> createJSCallSite(Runtime &runtime) { auto parentHandle = Handle<JSObject>::vmcast(&runtime.callSitePrototype); auto *cell = runtime.makeAFixed<JSObject>( runtime, parentHandle, runtime.getHiddenClassForPrototype( *parentHandle, JSObject::numOverlapSlots<JSObject>()), GCPointerBase::NoBarriers()); return JSObjectInit::initToHandle(runtime, cell); } } // namespace CallResult<HermesValue> JSCallSite::create( Runtime &runtime, Handle<JSError> errorHandle, uint32_t stackFrameIndex) { Handle<JSObject> selfHandle = createJSCallSite(runtime); assert( errorHandle->getStackTrace() && "Error passed to CallSite must have a stack trace"); assert( stackFrameIndex < errorHandle->getStackTrace()->size() && "Stack frame index out of bounds"); auto dpf = DefinePropertyFlags::getDefaultNewPropertyFlags(); auto addCallSiteProp = [&](Predefined::IProp iProp, Handle<> value) { auto res = JSObject::defineOwnProperty( selfHandle, runtime, Predefined::getSymbolID(iProp), dpf, std::move(value)); assert( res != ExecutionStatus::EXCEPTION && *res && "defineOwnProperty() failed"); return res; }; auto res = addCallSiteProp( Predefined::InternalPropertyCallSiteError, std::move(errorHandle)); if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } auto frameIndexHV = HermesValue::encodeNumberValue(stackFrameIndex); res = addCallSiteProp( Predefined::InternalPropertyCallSiteStackFrameIndex, runtime.makeHandle(std::move(frameIndexHV))); if (LLVM_UNLIKELY(res == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } assert( callSiteFromSelfHandle(runtime, selfHandle) != ExecutionStatus::EXCEPTION && "JSCallSite should **be** a CallSite by now"); return selfHandle.getHermesValue(); } CallResult<HermesValue> JSCallSite::getFunctionName( Runtime &runtime, Handle<> selfHandle) { auto callSiteRes = callSiteFromSelfHandle(runtime, selfHandle); if (LLVM_UNLIKELY(callSiteRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } auto functionName = JSError::getFunctionNameAtIndex( runtime, callSiteRes->error, callSiteRes->stackFrameIndex); return functionName ? functionName.getHermesValue() : HermesValue::encodeNullValue(); } CallResult<HermesValue> JSCallSite::getFileName( Runtime &runtime, Handle<> selfHandle) { auto stiRes = getStackTraceInfo(runtime, selfHandle); if (LLVM_UNLIKELY(stiRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } const StackTraceInfo *sti = *stiRes; if (sti->codeBlock) { OptValue<hbc::DebugSourceLocation> location = JSError::getDebugInfo(sti->codeBlock, sti->bytecodeOffset); RuntimeModule *runtimeModule = sti->codeBlock->getRuntimeModule(); auto makeUTF8Ref = [](llvh::StringRef ref) { const uint8_t *utf8 = reinterpret_cast<const uint8_t *>(ref.data()); return llvh::makeArrayRef(utf8, ref.size()); }; if (location) { auto debugInfo = runtimeModule->getBytecode()->getDebugInfo(); std::string utf8Storage; llvh::StringRef fileName = hbc::getStringFromEntry( debugInfo->getFilenameTable()[location->filenameId], debugInfo->getFilenameStorage(), utf8Storage); return StringPrimitive::createEfficient(runtime, makeUTF8Ref(fileName)); } else { llvh::StringRef sourceURL = runtimeModule->getSourceURL(); if (!sourceURL.empty()) { return StringPrimitive::createEfficient( runtime, makeUTF8Ref(sourceURL)); } } } return HermesValue::encodeNullValue(); } CallResult<HermesValue> JSCallSite::getLineNumber( Runtime &runtime, Handle<> selfHandle) { auto stiRes = getStackTraceInfo(runtime, selfHandle); if (LLVM_UNLIKELY(stiRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } const StackTraceInfo *sti = *stiRes; if (sti->codeBlock) { OptValue<hbc::DebugSourceLocation> location = JSError::getDebugInfo(sti->codeBlock, sti->bytecodeOffset); if (location) { return HermesValue::encodeNumberValue(location->line); } else { // Add 1 to the CJSModuleOffset to account for 1-based indexing of // symbolication tools. return HermesValue::encodeNumberValue( sti->codeBlock->getRuntimeModule()->getBytecode()->getSegmentID() + 1); } } return HermesValue::encodeNullValue(); } CallResult<HermesValue> JSCallSite::getColumnNumber( Runtime &runtime, Handle<> selfHandle) { auto stiRes = getStackTraceInfo(runtime, selfHandle); if (LLVM_UNLIKELY(stiRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } const StackTraceInfo *sti = *stiRes; if (sti->codeBlock) { OptValue<hbc::DebugSourceLocation> location = JSError::getDebugInfo(sti->codeBlock, sti->bytecodeOffset); if (location) { return HermesValue::encodeNumberValue(location->column); } } return HermesValue::encodeNullValue(); } CallResult<HermesValue> JSCallSite::getBytecodeAddress( Runtime &runtime, Handle<> selfHandle) { auto stiRes = getStackTraceInfo(runtime, selfHandle); if (LLVM_UNLIKELY(stiRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } const StackTraceInfo *sti = *stiRes; if (sti->codeBlock) { return HermesValue::encodeNumberValue( sti->bytecodeOffset + sti->codeBlock->getVirtualOffset()); } return HermesValue::encodeNullValue(); } CallResult<HermesValue> JSCallSite::isNative( Runtime &runtime, Handle<> selfHandle) { auto stiRes = getStackTraceInfo(runtime, selfHandle); if (LLVM_UNLIKELY(stiRes == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } const StackTraceInfo *sti = *stiRes; return HermesValue::encodeBoolValue(!sti->codeBlock); } namespace { /// Ensures that \p selfHandle is a CallSite (raising a TypeError if not) before /// returning the default value. CallResult<HermesValue> HandleUnimplemented( Runtime &runtime, Handle<> selfHandle, HermesValue (*createReturnHV)()) { if (LLVM_UNLIKELY( callSiteFromSelfHandle(runtime, selfHandle) == ExecutionStatus::EXCEPTION)) { return ExecutionStatus::EXCEPTION; } return (*createReturnHV)(); } } // namespace CallResult<HermesValue> JSCallSite::getThis( Runtime &runtime, Handle<> selfHandle) { return HandleUnimplemented( runtime, selfHandle, &HermesValue::encodeUndefinedValue); } CallResult<HermesValue> JSCallSite::getTypeName( Runtime &runtime, Handle<> selfHandle) { return HandleUnimplemented( runtime, selfHandle, &HermesValue::encodeNullValue); } CallResult<HermesValue> JSCallSite::getFunction( Runtime &runtime, Handle<> selfHandle) { return HandleUnimplemented( runtime, selfHandle, &HermesValue::encodeUndefinedValue); } CallResult<HermesValue> JSCallSite::getMethodName( Runtime &runtime, Handle<> selfHandle) { return HandleUnimplemented( runtime, selfHandle, &HermesValue::encodeNullValue); } CallResult<HermesValue> JSCallSite::getEvalOrigin( Runtime &runtime, Handle<> selfHandle) { return HandleUnimplemented( runtime, selfHandle, &HermesValue::encodeNullValue); } CallResult<HermesValue> JSCallSite::isToplevel( Runtime &runtime, Handle<> selfHandle) { return HandleUnimplemented( runtime, selfHandle, &HermesValue::encodeNullValue); } CallResult<HermesValue> JSCallSite::isEval( Runtime &runtime, Handle<> selfHandle) { return HandleUnimplemented( runtime, selfHandle, &HermesValue::encodeNullValue); } CallResult<HermesValue> JSCallSite::isConstructor( Runtime &runtime, Handle<> selfHandle) { return HandleUnimplemented( runtime, selfHandle, &HermesValue::encodeNullValue); } CallResult<HermesValue> JSCallSite::isAsync( Runtime &runtime, Handle<> selfHandle) { return HandleUnimplemented( runtime, selfHandle, [] { return HermesValue::encodeBoolValue(false); }); } CallResult<HermesValue> JSCallSite::isPromiseAll( Runtime &runtime, Handle<> selfHandle) { return HandleUnimplemented( runtime, selfHandle, [] { return HermesValue::encodeBoolValue(false); }); } CallResult<HermesValue> JSCallSite::getPromiseIndex( Runtime &runtime, Handle<> selfHandle) { return HandleUnimplemented( runtime, selfHandle, &HermesValue::encodeNullValue); } } // namespace vm } // namespace hermes