lib/VM/Callable.cpp (1,133 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/Callable.h"
#include "hermes/VM/ArrayLike.h"
#include "hermes/VM/BuildMetadata.h"
#include "hermes/VM/JSDataView.h"
#include "hermes/VM/JSDate.h"
#include "hermes/VM/JSError.h"
#include "hermes/VM/JSMapImpl.h"
#include "hermes/VM/JSNativeFunctions.h"
#include "hermes/VM/JSProxy.h"
#include "hermes/VM/JSRegExp.h"
#include "hermes/VM/JSTypedArray.h"
#include "hermes/VM/JSWeakMapImpl.h"
#include "hermes/VM/PrimitiveBox.h"
#include "hermes/VM/PropertyAccessor.h"
#include "hermes/VM/SmallXString.h"
#include "hermes/VM/StackFrame-inline.h"
#include "hermes/VM/StringPrimitive.h"
#include "llvh/ADT/ArrayRef.h"
namespace hermes {
namespace vm {
//===----------------------------------------------------------------------===//
// class Environment
const VTable Environment::vt{CellKind::EnvironmentKind, 0};
void EnvironmentBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
const auto *self = static_cast<const Environment *>(cell);
mb.setVTable(&Environment::vt);
mb.addField("parentEnvironment", &self->parentEnvironment_);
mb.addArray(self->getSlots(), &self->size_, sizeof(GCHermesValue));
}
//===----------------------------------------------------------------------===//
// class Callable
void CallableBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<Callable>());
JSObjectBuildMeta(cell, mb);
const auto *self = static_cast<const Callable *>(cell);
mb.setVTable(&Callable::vt);
mb.addField("environment", &self->environment_);
}
std::string Callable::_snapshotNameImpl(GCCell *cell, GC *gc) {
auto *const self = reinterpret_cast<Callable *>(cell);
return self->getNameIfExists(gc->getPointerBase());
}
CallResult<PseudoHandle<JSObject>> Callable::_newObjectImpl(
Handle<Callable> /*selfHandle*/,
Runtime &runtime,
Handle<JSObject> parentHandle) {
return JSObject::create(runtime, parentHandle);
}
void Callable::defineLazyProperties(Handle<Callable> fn, Runtime &runtime) {
// lazy functions can be Bound or JS Functions.
if (auto jsFun = Handle<JSFunction>::dyn_vmcast(fn)) {
const CodeBlock *codeBlock = jsFun->getCodeBlock();
// Create empty object for prototype.
auto prototypeParent = vmisa<JSGeneratorFunction>(*jsFun)
? Handle<JSObject>::vmcast(&runtime.generatorPrototype)
: Handle<JSObject>::vmcast(&runtime.objectPrototype);
// According to ES12 26.7.4, AsyncFunction instances do not have a
// 'prototype' property, hence we need to set an null handle here.
auto prototypeObjectHandle = vmisa<JSAsyncFunction>(*jsFun)
? Runtime::makeNullHandle<JSObject>()
: runtime.makeHandle(JSObject::create(runtime, prototypeParent));
auto cr = Callable::defineNameLengthAndPrototype(
fn,
runtime,
codeBlock->getNameMayAllocate(),
codeBlock->getParamCount() - 1,
prototypeObjectHandle,
Callable::WritablePrototype::Yes,
codeBlock->isStrictMode());
assert(
cr != ExecutionStatus::EXCEPTION && "failed to define length and name");
(void)cr;
} else if (vmisa<BoundFunction>(fn.get())) {
Handle<BoundFunction> boundfn = Handle<BoundFunction>::vmcast(fn);
Handle<Callable> target = runtime.makeHandle(boundfn->getTarget(runtime));
unsigned int argsWithThis = boundfn->getArgCountWithThis(runtime);
auto res = BoundFunction::initializeLengthAndName(
boundfn, runtime, target, argsWithThis == 0 ? 0 : argsWithThis - 1);
assert(
res != ExecutionStatus::EXCEPTION &&
"failed to define length and name of bound function");
(void)res;
} else {
// no other kind of function can be lazy currently
assert(false && "invalid lazy function");
}
}
ExecutionStatus Callable::defineNameLengthAndPrototype(
Handle<Callable> selfHandle,
Runtime &runtime,
SymbolID name,
unsigned paramCount,
Handle<JSObject> prototypeObjectHandle,
WritablePrototype writablePrototype,
bool strictMode) {
PropertyFlags pf;
pf.clear();
pf.enumerable = 0;
pf.writable = 0;
pf.configurable = 1;
GCScope scope{runtime, "defineNameLengthAndPrototype"};
namespace P = Predefined;
/// Adds a property to the object in \p OBJ_HANDLE. \p SYMBOL provides its name
/// as a \c Predefined enum value, and its value is rooted in \p HANDLE. If
/// property definition fails, the exceptional execution status will be
/// propagated to the outer function.
#define DEFINE_PROP(OBJ_HANDLE, SYMBOL, HANDLE) \
do { \
auto status = JSObject::defineNewOwnProperty( \
OBJ_HANDLE, runtime, Predefined::getSymbolID(SYMBOL), pf, HANDLE); \
if (LLVM_UNLIKELY(status == ExecutionStatus::EXCEPTION)) { \
return ExecutionStatus::EXCEPTION; \
} \
} while (false)
assert(name.isValid() && "A name must always be provided");
// Length is the number of formal arguments.
// 10.2.9 SetFunctionLength is performed during 10.2.3 OrdinaryFunctionCreate.
auto lengthHandle =
runtime.makeHandle(HermesValue::encodeDoubleValue(paramCount));
DEFINE_PROP(selfHandle, P::length, lengthHandle);
// Define the name.
// 10.2.8 SetFunctionName is performed after 10.2.3 OrdinaryFunctionCreate.
auto nameHandle = runtime.makeHandle(runtime.getStringPrimFromSymbolID(name));
DEFINE_PROP(selfHandle, P::name, nameHandle);
if (strictMode) {
// Define .callee and .arguments properties: throw always in strict mode.
auto accessor =
Handle<PropertyAccessor>::vmcast(&runtime.throwTypeErrorAccessor);
pf.clear();
pf.enumerable = 0;
pf.configurable = 0;
pf.accessor = 1;
DEFINE_PROP(selfHandle, P::caller, accessor);
DEFINE_PROP(selfHandle, P::arguments, accessor);
}
if (prototypeObjectHandle) {
// Set its 'prototype' property.
pf.clear();
pf.enumerable = 0;
/// System constructors have read-only prototypes.
pf.writable = (uint8_t)writablePrototype;
pf.configurable = 0;
DEFINE_PROP(selfHandle, P::prototype, prototypeObjectHandle);
if (LLVM_LIKELY(!vmisa<JSGeneratorFunction>(*selfHandle))) {
// Set the 'constructor' property in the prototype object.
// This must not be set for GeneratorFunctions, because
// prototypes must not point back to their constructors.
// See the diagram: ES9.0 25.2 (GeneratorFunction objects).
pf.clear();
pf.enumerable = 0;
pf.writable = 1;
pf.configurable = 1;
DEFINE_PROP(prototypeObjectHandle, P::constructor, selfHandle);
}
}
return ExecutionStatus::RETURNED;
#undef DEFINE_PROP
}
/// Execute this function with no arguments. This is just a convenience
/// helper method; it actually invokes the interpreter recursively.
CallResult<PseudoHandle<>> Callable::executeCall0(
Handle<Callable> selfHandle,
Runtime &runtime,
Handle<> thisArgHandle,
bool construct) {
ScopedNativeCallFrame newFrame{
runtime,
0,
selfHandle.getHermesValue(),
construct ? selfHandle.getHermesValue()
: HermesValue::encodeUndefinedValue(),
*thisArgHandle};
if (LLVM_UNLIKELY(newFrame.overflowed()))
return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
return call(selfHandle, runtime);
}
/// Execute this function with one argument. This is just a convenience
/// helper method; it actually invokes the interpreter recursively.
CallResult<PseudoHandle<>> Callable::executeCall1(
Handle<Callable> selfHandle,
Runtime &runtime,
Handle<> thisArgHandle,
HermesValue param1,
bool construct) {
ScopedNativeCallFrame newFrame{
runtime,
1,
selfHandle.getHermesValue(),
construct ? selfHandle.getHermesValue()
: HermesValue::encodeUndefinedValue(),
*thisArgHandle};
if (LLVM_UNLIKELY(newFrame.overflowed()))
return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
newFrame->getArgRef(0) = param1;
return call(selfHandle, runtime);
}
/// Execute this function with two arguments. This is just a convenience
/// helper method; it actually invokes the interpreter recursively.
CallResult<PseudoHandle<>> Callable::executeCall2(
Handle<Callable> selfHandle,
Runtime &runtime,
Handle<> thisArgHandle,
HermesValue param1,
HermesValue param2,
bool construct) {
ScopedNativeCallFrame newFrame{
runtime,
2,
selfHandle.getHermesValue(),
construct ? selfHandle.getHermesValue()
: HermesValue::encodeUndefinedValue(),
*thisArgHandle};
if (LLVM_UNLIKELY(newFrame.overflowed()))
return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
newFrame->getArgRef(0) = param1;
newFrame->getArgRef(1) = param2;
return call(selfHandle, runtime);
}
/// Execute this function with three arguments. This is just a convenience
/// helper method; it actually invokes the interpreter recursively.
CallResult<PseudoHandle<>> Callable::executeCall3(
Handle<Callable> selfHandle,
Runtime &runtime,
Handle<> thisArgHandle,
HermesValue param1,
HermesValue param2,
HermesValue param3,
bool construct) {
ScopedNativeCallFrame newFrame{
runtime,
3,
selfHandle.getHermesValue(),
construct ? selfHandle.getHermesValue()
: HermesValue::encodeUndefinedValue(),
*thisArgHandle};
if (LLVM_UNLIKELY(newFrame.overflowed()))
return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
newFrame->getArgRef(0) = param1;
newFrame->getArgRef(1) = param2;
newFrame->getArgRef(2) = param3;
return call(selfHandle, runtime);
}
/// Execute this function with four arguments. This is just a convenience
/// helper method; it actually invokes the interpreter recursively.
CallResult<PseudoHandle<>> Callable::executeCall4(
Handle<Callable> selfHandle,
Runtime &runtime,
Handle<> thisArgHandle,
HermesValue param1,
HermesValue param2,
HermesValue param3,
HermesValue param4,
bool construct) {
ScopedNativeCallFrame newFrame{
runtime,
4,
selfHandle.getHermesValue(),
construct ? selfHandle.getHermesValue()
: HermesValue::encodeUndefinedValue(),
*thisArgHandle};
if (LLVM_UNLIKELY(newFrame.overflowed()))
return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
newFrame->getArgRef(0) = param1;
newFrame->getArgRef(1) = param2;
newFrame->getArgRef(2) = param3;
newFrame->getArgRef(3) = param4;
return call(selfHandle, runtime);
}
CallResult<PseudoHandle<>> Callable::executeCall(
Handle<Callable> selfHandle,
Runtime &runtime,
Handle<> newTarget,
Handle<> thisArgument,
Handle<JSObject> arrayLike) {
CallResult<uint64_t> nRes = getArrayLikeLength(arrayLike, runtime);
if (LLVM_UNLIKELY(nRes == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
if (*nRes > UINT32_MAX) {
return runtime.raiseRangeError("Too many arguments for apply");
}
uint32_t n = static_cast<uint32_t>(*nRes);
ScopedNativeCallFrame newFrame{
runtime, n, selfHandle.getHermesValue(), *newTarget, *thisArgument};
if (LLVM_UNLIKELY(newFrame.overflowed()))
return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
// Initialize the arguments to undefined because we might allocate and cause
// a gc while populating them.
// TODO: look into doing this lazily.
newFrame.fillArguments(n, HermesValue::encodeUndefinedValue());
if (LLVM_UNLIKELY(
createListFromArrayLike(
arrayLike,
runtime,
n,
[&newFrame](Runtime &, uint64_t index, PseudoHandle<> value) {
newFrame->getArgRef(index) = value.getHermesValue();
return ExecutionStatus::RETURNED;
}) == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
return Callable::call(selfHandle, runtime);
}
CallResult<PseudoHandle<>> Callable::executeConstruct0(
Handle<Callable> selfHandle,
Runtime &runtime) {
auto thisVal = Callable::createThisForConstruct(selfHandle, runtime);
if (LLVM_UNLIKELY(thisVal == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
auto thisValHandle = runtime.makeHandle<JSObject>(std::move(thisVal->get()));
auto result = executeCall0(selfHandle, runtime, thisValHandle, true);
if (LLVM_UNLIKELY(result == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
return (*result)->isObject() ? std::move(result)
: CallResult<PseudoHandle<>>(createPseudoHandle(
thisValHandle.getHermesValue()));
}
CallResult<PseudoHandle<>> Callable::executeConstruct1(
Handle<Callable> selfHandle,
Runtime &runtime,
Handle<> param1) {
auto thisVal = Callable::createThisForConstruct(selfHandle, runtime);
if (LLVM_UNLIKELY(thisVal == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
auto thisValHandle = runtime.makeHandle<JSObject>(std::move(thisVal->get()));
CallResult<PseudoHandle<>> result =
executeCall1(selfHandle, runtime, thisValHandle, *param1, true);
if (LLVM_UNLIKELY(result == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
return (*result)->isObject() ? std::move(result)
: CallResult<PseudoHandle<>>(createPseudoHandle(
thisValHandle.getHermesValue()));
}
CallResult<PseudoHandle<JSObject>> Callable::createThisForConstruct(
Handle<Callable> selfHandle,
Runtime &runtime) {
CallResult<PseudoHandle<>> prototypeProp = JSObject::getNamed_RJS(
selfHandle, runtime, Predefined::getSymbolID(Predefined::prototype));
if (LLVM_UNLIKELY(prototypeProp == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
Handle<JSObject> prototype = vmisa<JSObject>(prototypeProp->get())
? runtime.makeHandle<JSObject>(prototypeProp->get())
: Handle<JSObject>::vmcast(&runtime.objectPrototype);
return Callable::newObject(selfHandle, runtime, prototype);
}
CallResult<double> Callable::extractOwnLengthProperty_RJS(
Handle<Callable> selfHandle,
Runtime &runtime) {
CallResult<PseudoHandle<>> propRes{
createPseudoHandle(HermesValue::encodeUndefinedValue())};
NamedPropertyDescriptor desc;
if (JSObject::getOwnNamedDescriptor(
selfHandle,
runtime,
Predefined::getSymbolID(Predefined::length),
desc)) {
propRes = JSObject::getNamedPropertyValue_RJS(
selfHandle, runtime, selfHandle, desc);
} else if (selfHandle->isProxyObject()) {
ComputedPropertyDescriptor desc;
CallResult<bool> hasLength = JSProxy::getOwnProperty(
selfHandle,
runtime,
runtime.getPredefinedStringHandle(Predefined::length),
desc,
nullptr);
if (LLVM_UNLIKELY(hasLength == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
if (*hasLength) {
propRes = JSProxy::getNamed(
selfHandle,
runtime,
Predefined::getSymbolID(Predefined::length),
selfHandle);
}
}
{
NoAllocScope naScope{runtime};
if (propRes == ExecutionStatus::EXCEPTION) {
return ExecutionStatus::EXCEPTION;
}
if (!(*propRes)->isNumber()) {
return 0.0;
}
}
auto intRes =
toIntegerOrInfinity(runtime, runtime.makeHandle(std::move(*propRes)));
if (intRes == ExecutionStatus::EXCEPTION) {
return ExecutionStatus::EXCEPTION;
}
return intRes->getNumber();
}
//===----------------------------------------------------------------------===//
// class BoundFunction
const CallableVTable BoundFunction::vt{
{
VTable(
CellKind::BoundFunctionKind,
cellSize<BoundFunction>(),
nullptr,
nullptr,
nullptr,
nullptr,
VTable::HeapSnapshotMetadata{
HeapSnapshot::NodeType::Closure,
BoundFunction::_snapshotNameImpl,
BoundFunction::_snapshotAddEdgesImpl,
nullptr,
nullptr}),
BoundFunction::_getOwnIndexedRangeImpl,
BoundFunction::_haveOwnIndexedImpl,
BoundFunction::_getOwnIndexedPropertyFlagsImpl,
BoundFunction::_getOwnIndexedImpl,
BoundFunction::_setOwnIndexedImpl,
BoundFunction::_deleteOwnIndexedImpl,
BoundFunction::_checkAllOwnIndexedImpl,
},
BoundFunction::_newObjectImpl,
BoundFunction::_callImpl};
void BoundFunctionBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<BoundFunction>());
CallableBuildMeta(cell, mb);
const auto *self = static_cast<const BoundFunction *>(cell);
mb.setVTable(&BoundFunction::vt);
mb.addField("target", &self->target_);
mb.addField("argStorage", &self->argStorage_);
}
CallResult<HermesValue> BoundFunction::create(
Runtime &runtime,
Handle<Callable> target,
unsigned argCountWithThis,
ConstArgIterator argsWithThis) {
unsigned argCount = argCountWithThis > 0 ? argCountWithThis - 1 : 0;
auto arrRes = ArrayStorage::create(runtime, argCount + 1);
if (LLVM_UNLIKELY(arrRes == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
auto argStorageHandle = runtime.makeHandle<ArrayStorage>(*arrRes);
auto *cell = runtime.makeAFixed<BoundFunction>(
runtime,
Handle<JSObject>::vmcast(&runtime.functionPrototype),
runtime.getHiddenClassForPrototype(
runtime.functionPrototypeRawPtr, numOverlapSlots<BoundFunction>()),
target,
argStorageHandle);
auto selfHandle = JSObjectInit::initToHandle(runtime, cell);
// Copy the arguments. If we don't have any, we must at least initialize
// 'this' to 'undefined'.
MutableHandle<ArrayStorage> handle(
runtime, selfHandle->argStorage_.get(runtime));
// In case the storage was trimmed, make sure it has enough capacity.
ArrayStorage::ensureCapacity(handle, runtime, argCount + 1);
if (argCountWithThis) {
for (unsigned i = 0; i != argCountWithThis; ++i) {
ArrayStorage::push_back(handle, runtime, Handle<>(&argsWithThis[i]));
}
} else {
// Don't need to worry about resizing since it was created with a capacity
// of at least 1.
ArrayStorage::push_back(handle, runtime, Runtime::getUndefinedValue());
}
// Update the storage pointer in case push_back() needed to reallocate.
selfHandle->argStorage_.set(runtime, *handle, &runtime.getHeap());
if (target->isLazy()) {
// If the target is lazy we can make the bound function lazy.
// If the target is NOT lazy, it might have getter/setters on length that
// throws and we also need to throw.
selfHandle->flags_.lazyObject = 1;
} else {
if (initializeLengthAndName(selfHandle, runtime, target, argCount) ==
ExecutionStatus::EXCEPTION) {
return ExecutionStatus::EXCEPTION;
}
}
return selfHandle.getHermesValue();
}
ExecutionStatus BoundFunction::initializeLengthAndName(
Handle<Callable> selfHandle,
Runtime &runtime,
Handle<Callable> target,
unsigned argCount) {
if (LLVM_UNLIKELY(target->isLazy())) {
Callable::initializeLazyObject(runtime, target);
}
// Extract target.length.
auto targetLength = Callable::extractOwnLengthProperty_RJS(target, runtime);
if (targetLength == ExecutionStatus::EXCEPTION)
return ExecutionStatus::EXCEPTION;
// Define .length
PropertyFlags pf{};
pf.enumerable = 0;
pf.writable = 0;
pf.configurable = 1;
// Length is the number of formal arguments.
auto length = runtime.makeHandle(HermesValue::encodeNumberValue(
argCount >= *targetLength ? 0.0 : *targetLength - argCount));
if (LLVM_UNLIKELY(
JSObject::defineNewOwnProperty(
selfHandle,
runtime,
Predefined::getSymbolID(Predefined::length),
pf,
length) == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
// Set the name by prepending "bound ".
auto propRes = JSObject::getNamed_RJS(
target, runtime, Predefined::getSymbolID(Predefined::name));
if (LLVM_UNLIKELY(propRes == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
auto nameHandle = (*propRes)->isString()
? runtime.makeHandle<StringPrimitive>(propRes->getHermesValue())
: runtime.getPredefinedStringHandle(Predefined::emptyString);
auto nameView = StringPrimitive::createStringView(runtime, nameHandle);
llvh::SmallU16String<32> boundName{"bound "};
boundName.append(nameView.begin(), nameView.end());
// Share name strings for repeatedly bound functions by using the
// identifier table. If a new symbol is created, it will disappear
// after the name string dies, since nothing else refers to it.
auto &identifierTable = runtime.getIdentifierTable();
auto boundNameSym = identifierTable.getSymbolHandle(runtime, boundName);
if (LLVM_UNLIKELY(boundNameSym == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
Handle<StringPrimitive> boundNameHandle(
runtime, identifierTable.getStringPrim(runtime, **boundNameSym));
DefinePropertyFlags dpf = DefinePropertyFlags::getDefaultNewPropertyFlags();
dpf.writable = 0;
dpf.enumerable = 0;
if (LLVM_UNLIKELY(
JSObject::defineOwnProperty(
selfHandle,
runtime,
Predefined::getSymbolID(Predefined::name),
dpf,
boundNameHandle) == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
// Define .callee and .arguments properties: throw always in bound functions.
auto accessor =
Handle<PropertyAccessor>::vmcast(&runtime.throwTypeErrorAccessor);
pf.clear();
pf.enumerable = 0;
pf.configurable = 0;
pf.accessor = 1;
if (LLVM_UNLIKELY(
JSObject::defineNewOwnProperty(
selfHandle,
runtime,
Predefined::getSymbolID(Predefined::caller),
pf,
accessor) == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
if (LLVM_UNLIKELY(
JSObject::defineNewOwnProperty(
selfHandle,
runtime,
Predefined::getSymbolID(Predefined::arguments),
pf,
accessor) == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
return ExecutionStatus::RETURNED;
}
CallResult<PseudoHandle<JSObject>> BoundFunction::_newObjectImpl(
Handle<Callable> selfHandle,
Runtime &runtime,
Handle<JSObject>) {
auto *self = vmcast<BoundFunction>(*selfHandle);
// If it is a chain of bound functions, skip directly to the end.
while (auto *targetAsBound =
dyn_vmcast<BoundFunction>(self->getTarget(runtime)))
self = targetAsBound;
auto targetHandle = runtime.makeHandle(self->getTarget(runtime));
// We must duplicate the [[Construct]] functionality here.
// Obtain "target.prototype".
auto propRes = JSObject::getNamed_RJS(
targetHandle, runtime, Predefined::getSymbolID(Predefined::prototype));
if (propRes == ExecutionStatus::EXCEPTION)
return ExecutionStatus::EXCEPTION;
auto prototype = runtime.makeHandle(std::move(*propRes));
// If target.prototype is an object, use it, otherwise use the standard
// object prototype.
return targetHandle->getVT()->newObject(
targetHandle,
runtime,
prototype->isObject()
? Handle<JSObject>::vmcast(prototype)
: Handle<JSObject>::vmcast(&runtime.objectPrototype));
}
CallResult<PseudoHandle<>> BoundFunction::_boundCall(
BoundFunction *self,
const Inst *ip,
Runtime &runtime) {
ScopedNativeDepthTracker depthTracker{runtime};
if (LLVM_UNLIKELY(depthTracker.overflowed())) {
return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
}
CallResult<PseudoHandle<>> res{ExecutionStatus::EXCEPTION};
StackFramePtr originalCalleeFrame = StackFramePtr(runtime.getStackPointer());
// Save the original newTarget since we will overwrite it.
HermesValue originalNewTarget = originalCalleeFrame.getNewTargetRef();
// Save the original arg count since we will lose it.
auto originalArgCount = originalCalleeFrame.getArgCount();
// Keep track of the total arg count.
auto totalArgCount = originalArgCount;
auto callerFrame = runtime.getCurrentFrame();
// We must preserve the "thisArg" passed to us by the caller because it is in
// a register that is not supposed to be modified by a call. Copy it to the
// scratch register in the caller's frame.
// Note that since there is only one scratch reg, we must process all chained
// bound calls in one go (which is more efficient anyway).
callerFrame.getScratchRef() = originalCalleeFrame.getThisArgRef();
// Pop the stack down to the first argument, erasing the call frame - we don't
// need the call frame since we will build a new one.
runtime.popToSavedStackPointer(&originalCalleeFrame.getThisArgRef());
// Loop, copying the bound arguments of all chained bound functions.
for (;;) {
auto boundArgCount = self->getArgCountWithThis(runtime) - 1;
totalArgCount += boundArgCount;
// Check if we have enough stack for the arguments and the frame metadata.
if (LLVM_UNLIKELY(!runtime.checkAvailableStack(
StackFrameLayout::callerOutgoingRegisters(boundArgCount)))) {
// Oops, we ran out of stack in the middle of calling a bound function.
// Restore everything and bail.
// We can't "pop" the stack pointer to an arbitrary value, which may be
// higher than the current pointer. So, first we pop everything that we
// may have pushed, then allocate the correct amount to get back to the
// initial state.
runtime.popToSavedStackPointer(&originalCalleeFrame.getThisArgRef());
runtime.allocUninitializedStack(
StackFrameLayout::CallerExtraRegistersAtEnd + 1);
assert(
runtime.getStackPointer() == originalCalleeFrame.ptr() &&
"Stack wasn't restored properly");
res = runtime.raiseStackOverflow(
Runtime::StackOverflowKind::JSRegisterStack);
goto bail;
}
// Allocate space only for the arguments for now.
auto *stack = runtime.allocUninitializedStack(boundArgCount);
// Copy the bound arguments (but not the bound "this").
std::uninitialized_copy_n(
self->getArgsWithThis(runtime) + 1, boundArgCount, ArgIterator(stack));
// Loop while the target is another bound function.
auto *targetAsBound = dyn_vmcast<BoundFunction>(self->getTarget(runtime));
if (!targetAsBound)
break;
self = targetAsBound;
}
// Block scope for non-trivial variables to avoid complaints from "goto".
{
// Allocate space for "thisArg" and the frame metdata following the outgoing
// registers. Note that we already checked earlier that we have enough
// stack.
auto *stack = runtime.allocUninitializedStack(
StackFrameLayout::CallerExtraRegistersAtEnd + 1);
// Initialize the new frame metadata.
auto newCalleeFrame = StackFramePtr::initFrame(
stack,
runtime.getCurrentFrame(),
ip,
nullptr,
totalArgCount,
HermesValue::encodeObjectValue(self->getTarget(runtime)),
originalNewTarget);
// Initialize "thisArg". When constructing we must use the original 'this',
// not the bound one.
newCalleeFrame.getThisArgRef() = !originalNewTarget.isUndefined()
? static_cast<HermesValue>(callerFrame.getScratchRef())
: self->getArgsWithThis(runtime)[0];
res =
Callable::call(newCalleeFrame.getCalleeClosureHandleUnsafe(), runtime);
assert(
runtime.getCurrentFrame() == callerFrame &&
"caller frame not restored");
// Restore the original stack level.
runtime.popToSavedStackPointer(originalCalleeFrame.ptr());
}
bail:
// We must restore the original call frame. There is no need to restore
// all the fields to their previous values, just the registers which are not
// supposed to be modified by a call.
StackFramePtr::initFrame(
originalCalleeFrame.ptr(),
StackFramePtr{},
ip,
nullptr,
0,
HermesValue::encodeEmptyValue(),
HermesValue::encodeEmptyValue());
// Restore "thisArg" and clear the scratch register to avoid a leak.
originalCalleeFrame.getThisArgRef() = callerFrame.getScratchRef();
callerFrame.getScratchRef() = HermesValue::encodeUndefinedValue();
return res;
}
CallResult<PseudoHandle<>> BoundFunction::_callImpl(
Handle<Callable> selfHandle,
Runtime &runtime) {
// Pass `nullptr` as the IP because this function is never called
// from the interpreter, which should use `_boundCall` directly.
return _boundCall(vmcast<BoundFunction>(selfHandle.get()), nullptr, runtime);
}
//===----------------------------------------------------------------------===//
// class NativeFunction
const CallableVTable NativeFunction::vt{
{
VTable(
CellKind::NativeFunctionKind,
cellSize<NativeFunction>(),
nullptr,
nullptr,
nullptr,
nullptr,
VTable::HeapSnapshotMetadata{
HeapSnapshot::NodeType::Closure,
NativeFunction::_snapshotNameImpl,
NativeFunction::_snapshotAddEdgesImpl,
nullptr,
nullptr}),
NativeFunction::_getOwnIndexedRangeImpl,
NativeFunction::_haveOwnIndexedImpl,
NativeFunction::_getOwnIndexedPropertyFlagsImpl,
NativeFunction::_getOwnIndexedImpl,
NativeFunction::_setOwnIndexedImpl,
NativeFunction::_deleteOwnIndexedImpl,
NativeFunction::_checkAllOwnIndexedImpl,
},
NativeFunction::_newObjectImpl,
NativeFunction::_callImpl};
void NativeFunctionBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<NativeFunction>());
CallableBuildMeta(cell, mb);
mb.setVTable(&NativeFunction::vt);
}
std::string NativeFunction::_snapshotNameImpl(GCCell *cell, GC *gc) {
auto *const self = reinterpret_cast<NativeFunction *>(cell);
return getFunctionName(self->functionPtr_);
}
Handle<NativeFunction> NativeFunction::create(
Runtime &runtime,
Handle<JSObject> parentHandle,
void *context,
NativeFunctionPtr functionPtr,
SymbolID name,
unsigned paramCount,
Handle<JSObject> prototypeObjectHandle,
unsigned additionalSlotCount) {
size_t reservedSlots =
numOverlapSlots<NativeFunction>() + additionalSlotCount;
auto *cell = runtime.makeAFixed<NativeFunction>(
runtime,
parentHandle,
runtime.getHiddenClassForPrototype(*parentHandle, reservedSlots),
context,
functionPtr);
auto selfHandle = JSObjectInit::initToHandle(runtime, cell);
// Allocate a propStorage if the number of additional slots requires it.
runtime.ignoreAllocationFailure(
JSObject::allocatePropStorage(selfHandle, runtime, reservedSlots));
auto st = defineNameLengthAndPrototype(
selfHandle,
runtime,
name,
paramCount,
prototypeObjectHandle,
Callable::WritablePrototype::Yes,
false);
(void)st;
assert(
st != ExecutionStatus::EXCEPTION && "defineLengthAndPrototype() failed");
return selfHandle;
}
Handle<NativeFunction> NativeFunction::create(
Runtime &runtime,
Handle<JSObject> parentHandle,
Handle<Environment> parentEnvHandle,
void *context,
NativeFunctionPtr functionPtr,
SymbolID name,
unsigned paramCount,
Handle<JSObject> prototypeObjectHandle,
unsigned additionalSlotCount) {
auto *cell = runtime.makeAFixed<NativeFunction>(
runtime,
parentHandle,
runtime.getHiddenClassForPrototype(
*parentHandle,
numOverlapSlots<NativeFunction>() + additionalSlotCount),
parentEnvHandle,
context,
functionPtr);
auto selfHandle = JSObjectInit::initToHandle(runtime, cell);
auto st = defineNameLengthAndPrototype(
selfHandle,
runtime,
name,
paramCount,
prototypeObjectHandle,
Callable::WritablePrototype::Yes,
false);
(void)st;
assert(
st != ExecutionStatus::EXCEPTION && "defineLengthAndPrototype() failed");
return selfHandle;
}
CallResult<PseudoHandle<>> NativeFunction::_callImpl(
Handle<Callable> selfHandle,
Runtime &runtime) {
return _nativeCall(vmcast<NativeFunction>(selfHandle.get()), runtime);
}
CallResult<PseudoHandle<JSObject>> NativeFunction::_newObjectImpl(
Handle<Callable>,
Runtime &runtime,
Handle<JSObject>) {
return runtime.raiseTypeError(
"This function cannot be used as a constructor.");
}
//===----------------------------------------------------------------------===//
// class NativeConstructor
template <class From>
static CallResult<PseudoHandle<JSObject>> toCallResultPseudoHandleJSObject(
PseudoHandle<From> &&other) {
return PseudoHandle<JSObject>{std::move(other)};
}
template <class From>
static CallResult<PseudoHandle<JSObject>> toCallResultPseudoHandleJSObject(
CallResult<PseudoHandle<From>> &&other) {
return std::move(other);
}
template <class From>
static CallResult<PseudoHandle<JSObject>> toCallResultPseudoHandleJSObject(
CallResult<Handle<From>> other) {
if (LLVM_UNLIKELY(other == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
return PseudoHandle<JSObject>{*other};
}
template <class From>
static CallResult<PseudoHandle<JSObject>> toCallResultPseudoHandleJSObject(
Handle<From> other) {
return PseudoHandle<JSObject>{other};
}
template <class NativeClass>
CallResult<PseudoHandle<JSObject>> NativeConstructor::creatorFunction(
Runtime &runtime,
Handle<JSObject> prototype,
void *) {
return toCallResultPseudoHandleJSObject(
NativeClass::create(runtime, prototype));
}
#define NATIVE_CONSTRUCTOR(fun) \
template CallResult<PseudoHandle<JSObject>> fun( \
Runtime &, Handle<JSObject>, void *);
#include "hermes/VM/NativeFunctions.def"
#undef NATIVE_CONSTRUCTOR
const CallableVTable NativeConstructor::vt{
{
VTable(
CellKind::NativeConstructorKind,
cellSize<NativeConstructor>(),
nullptr,
nullptr,
nullptr,
nullptr,
VTable::HeapSnapshotMetadata{
HeapSnapshot::NodeType::Closure,
NativeConstructor::_snapshotNameImpl,
NativeConstructor::_snapshotAddEdgesImpl,
nullptr,
nullptr}),
NativeConstructor::_getOwnIndexedRangeImpl,
NativeConstructor::_haveOwnIndexedImpl,
NativeConstructor::_getOwnIndexedPropertyFlagsImpl,
NativeConstructor::_getOwnIndexedImpl,
NativeConstructor::_setOwnIndexedImpl,
NativeConstructor::_deleteOwnIndexedImpl,
NativeConstructor::_checkAllOwnIndexedImpl,
},
NativeConstructor::_newObjectImpl,
NativeConstructor::_callImpl};
void NativeConstructorBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<NativeConstructor>());
NativeFunctionBuildMeta(cell, mb);
mb.setVTable(&NativeConstructor::vt);
}
#ifndef NDEBUG
CallResult<PseudoHandle<>> NativeConstructor::_callImpl(
Handle<Callable> selfHandle,
Runtime &runtime) {
StackFramePtr newFrame{runtime.getStackPointer()};
if (newFrame.isConstructorCall()) {
auto consHandle = Handle<NativeConstructor>::vmcast(selfHandle);
assert(
consHandle->targetKind_ ==
vmcast<JSObject>(newFrame.getThisArgRef())->getKind() &&
"call(construct=true) called without the correct 'this' value");
}
return NativeFunction::_callImpl(selfHandle, runtime);
}
#endif
//===----------------------------------------------------------------------===//
// class JSFunction
const CallableVTable JSFunction::vt{
{
VTable(
CellKind::JSFunctionKind,
cellSize<JSFunction>(),
nullptr,
nullptr,
nullptr,
nullptr,
VTable::HeapSnapshotMetadata{
HeapSnapshot::NodeType::Closure,
JSFunction::_snapshotNameImpl,
JSFunction::_snapshotAddEdgesImpl,
nullptr,
JSFunction::_snapshotAddLocationsImpl}),
JSFunction::_getOwnIndexedRangeImpl,
JSFunction::_haveOwnIndexedImpl,
JSFunction::_getOwnIndexedPropertyFlagsImpl,
JSFunction::_getOwnIndexedImpl,
JSFunction::_setOwnIndexedImpl,
JSFunction::_deleteOwnIndexedImpl,
JSFunction::_checkAllOwnIndexedImpl,
},
JSFunction::_newObjectImpl,
JSFunction::_callImpl};
void JSFunctionBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSFunction>());
CallableBuildMeta(cell, mb);
const auto *self = static_cast<const JSFunction *>(cell);
mb.addField("domain", &self->domain_);
mb.setVTable(&JSFunction::vt);
}
PseudoHandle<JSFunction> JSFunction::create(
Runtime &runtime,
Handle<Domain> domain,
Handle<JSObject> parentHandle,
Handle<Environment> envHandle,
CodeBlock *codeBlock) {
auto *cell = runtime.makeAFixed<JSFunction, kHasFinalizer>(
runtime,
domain,
parentHandle,
runtime.getHiddenClassForPrototype(
*parentHandle, numOverlapSlots<JSFunction>()),
envHandle,
codeBlock);
auto self = JSObjectInit::initToPseudoHandle(runtime, cell);
self->flags_.lazyObject = 1;
return self;
}
void JSFunction::addLocationToSnapshot(
HeapSnapshot &snap,
HeapSnapshot::NodeID id) const {
if (auto location = codeBlock_->getSourceLocation()) {
snap.addLocation(
id,
codeBlock_->getRuntimeModule()->getScriptID(),
location->line,
location->column);
}
}
CallResult<PseudoHandle<>> JSFunction::_callImpl(
Handle<Callable> selfHandle,
Runtime &runtime) {
auto *self = vmcast<JSFunction>(selfHandle.get());
CallResult<HermesValue> result{ExecutionStatus::EXCEPTION};
result = runtime.interpretFunction(self->getCodeBlock());
if (LLVM_UNLIKELY(result == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
return createPseudoHandle(*result);
}
std::string JSFunction::_snapshotNameImpl(GCCell *cell, GC *gc) {
auto *const self = vmcast<JSFunction>(cell);
std::string funcName = Callable::_snapshotNameImpl(self, gc);
if (!funcName.empty()) {
return funcName;
}
return self->codeBlock_->getNameString(gc->getCallbacks());
}
void JSFunction::_snapshotAddEdgesImpl(
GCCell *cell,
GC *gc,
HeapSnapshot &snap) {
auto *const self = vmcast<JSFunction>(cell);
// Add the super type's edges too.
Callable::_snapshotAddEdgesImpl(self, gc, snap);
// A node for the code block will be added as part of the RuntimeModule.
snap.addNamedEdge(
HeapSnapshot::EdgeType::Shortcut,
"codeBlock",
gc->getIDTracker().getNativeID(self->codeBlock_));
}
void JSFunction::_snapshotAddLocationsImpl(
GCCell *cell,
GC *gc,
HeapSnapshot &snap) {
auto *const self = vmcast<JSFunction>(cell);
self->addLocationToSnapshot(snap, gc->getObjectID(self));
}
//===----------------------------------------------------------------------===//
// class JSAsyncFunction
const CallableVTable JSAsyncFunction::vt{
{
VTable(
CellKind::JSAsyncFunctionKind,
cellSize<JSAsyncFunction>(),
nullptr,
nullptr,
nullptr,
nullptr,
VTable::HeapSnapshotMetadata{
HeapSnapshot::NodeType::Closure,
JSAsyncFunction::_snapshotNameImpl,
JSAsyncFunction::_snapshotAddEdgesImpl,
nullptr,
nullptr}),
JSAsyncFunction::_getOwnIndexedRangeImpl,
JSAsyncFunction::_haveOwnIndexedImpl,
JSAsyncFunction::_getOwnIndexedPropertyFlagsImpl,
JSAsyncFunction::_getOwnIndexedImpl,
JSAsyncFunction::_setOwnIndexedImpl,
JSAsyncFunction::_deleteOwnIndexedImpl,
JSAsyncFunction::_checkAllOwnIndexedImpl,
},
JSAsyncFunction::_newObjectImpl,
JSAsyncFunction::_callImpl};
void JSAsyncFunctionBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSAsyncFunction>());
JSFunctionBuildMeta(cell, mb);
mb.setVTable(&JSAsyncFunction::vt);
}
PseudoHandle<JSAsyncFunction> JSAsyncFunction::create(
Runtime &runtime,
Handle<Domain> domain,
Handle<JSObject> parentHandle,
Handle<Environment> envHandle,
CodeBlock *codeBlock) {
auto *cell = runtime.makeAFixed<JSAsyncFunction, kHasFinalizer>(
runtime,
domain,
parentHandle,
runtime.getHiddenClassForPrototype(
*parentHandle, numOverlapSlots<JSAsyncFunction>()),
envHandle,
codeBlock);
auto self = JSObjectInit::initToPseudoHandle(runtime, cell);
self->flags_.lazyObject = 1;
return self;
}
//===----------------------------------------------------------------------===//
// class JSGeneratorFunction
const CallableVTable JSGeneratorFunction::vt{
{
VTable(
CellKind::JSGeneratorFunctionKind,
cellSize<JSGeneratorFunction>(),
nullptr,
nullptr,
nullptr,
nullptr,
VTable::HeapSnapshotMetadata{
HeapSnapshot::NodeType::Closure,
JSGeneratorFunction::_snapshotNameImpl,
JSGeneratorFunction::_snapshotAddEdgesImpl,
nullptr,
nullptr}),
JSGeneratorFunction::_getOwnIndexedRangeImpl,
JSGeneratorFunction::_haveOwnIndexedImpl,
JSGeneratorFunction::_getOwnIndexedPropertyFlagsImpl,
JSGeneratorFunction::_getOwnIndexedImpl,
JSGeneratorFunction::_setOwnIndexedImpl,
JSGeneratorFunction::_deleteOwnIndexedImpl,
JSGeneratorFunction::_checkAllOwnIndexedImpl,
},
JSGeneratorFunction::_newObjectImpl,
JSGeneratorFunction::_callImpl};
void JSGeneratorFunctionBuildMeta(const GCCell *cell, Metadata::Builder &mb) {
mb.addJSObjectOverlapSlots(JSObject::numOverlapSlots<JSGeneratorFunction>());
JSFunctionBuildMeta(cell, mb);
mb.setVTable(&JSGeneratorFunction::vt);
}
PseudoHandle<JSGeneratorFunction> JSGeneratorFunction::create(
Runtime &runtime,
Handle<Domain> domain,
Handle<JSObject> parentHandle,
Handle<Environment> envHandle,
CodeBlock *codeBlock) {
auto *cell = runtime.makeAFixed<JSGeneratorFunction, kHasFinalizer>(
runtime,
domain,
parentHandle,
runtime.getHiddenClassForPrototype(
*parentHandle, numOverlapSlots<JSGeneratorFunction>()),
envHandle,
codeBlock);
auto self = JSObjectInit::initToPseudoHandle(runtime, cell);
self->flags_.lazyObject = 1;
return self;
}
//===----------------------------------------------------------------------===//
// class GeneratorInnerFunction
const CallableVTable GeneratorInnerFunction::vt{
{
VTable(
CellKind::GeneratorInnerFunctionKind,
cellSize<GeneratorInnerFunction>(),
nullptr,
nullptr,
nullptr,
nullptr,
VTable::HeapSnapshotMetadata{
HeapSnapshot::NodeType::Closure,
GeneratorInnerFunction::_snapshotNameImpl,
GeneratorInnerFunction::_snapshotAddEdgesImpl,
nullptr,
nullptr}),
GeneratorInnerFunction::_getOwnIndexedRangeImpl,
GeneratorInnerFunction::_haveOwnIndexedImpl,
GeneratorInnerFunction::_getOwnIndexedPropertyFlagsImpl,
GeneratorInnerFunction::_getOwnIndexedImpl,
GeneratorInnerFunction::_setOwnIndexedImpl,
GeneratorInnerFunction::_deleteOwnIndexedImpl,
GeneratorInnerFunction::_checkAllOwnIndexedImpl,
},
GeneratorInnerFunction::_newObjectImpl,
GeneratorInnerFunction::_callImpl};
void GeneratorInnerFunctionBuildMeta(
const GCCell *cell,
Metadata::Builder &mb) {
mb.addJSObjectOverlapSlots(
JSObject::numOverlapSlots<GeneratorInnerFunction>());
JSFunctionBuildMeta(cell, mb);
const auto *self = static_cast<const GeneratorInnerFunction *>(cell);
mb.setVTable(&GeneratorInnerFunction::vt);
mb.addField("savedContext", &self->savedContext_);
mb.addField("result", &self->result_);
}
CallResult<Handle<GeneratorInnerFunction>> GeneratorInnerFunction::create(
Runtime &runtime,
Handle<Domain> domain,
Handle<JSObject> parentHandle,
Handle<Environment> envHandle,
CodeBlock *codeBlock,
NativeArgs args) {
auto *cell = runtime.makeAFixed<GeneratorInnerFunction>(
runtime,
domain,
parentHandle,
runtime.getHiddenClassForPrototype(
*parentHandle, numOverlapSlots<GeneratorInnerFunction>()),
envHandle,
codeBlock,
args.getArgCount());
auto self = JSObjectInit::initToHandle(runtime, cell);
const uint32_t ctxSize = getContextSize(codeBlock, args.getArgCount());
auto ctxRes = ArrayStorage::create(runtime, ctxSize, ctxSize);
if (LLVM_UNLIKELY(ctxRes == ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
auto ctx = runtime.makeHandle<ArrayStorage>(*ctxRes);
// Set "this" as the first element.
ctx->set(0, args.getThisArg(), &runtime.getHeap());
// Set the rest of the arguments.
// Argument i goes in slot i+1 to account for the "this".
for (uint32_t i = 0, e = args.getArgCount(); i < e; ++i) {
ctx->set(i + 1, args.getArg(i), &runtime.getHeap());
}
self->savedContext_.set(runtime, ctx.get(), &runtime.getHeap());
return self;
}
/// Call the callable with arguments already on the stack.
CallResult<PseudoHandle<>> GeneratorInnerFunction::callInnerFunction(
Handle<GeneratorInnerFunction> selfHandle,
Runtime &runtime,
Handle<> arg,
Action action) {
auto self = Handle<GeneratorInnerFunction>::vmcast(selfHandle);
SmallHermesValue shv =
SmallHermesValue::encodeHermesValue(arg.getHermesValue(), runtime);
self->result_.set(shv, &runtime.getHeap());
self->action_ = action;
auto ctx = runtime.makeMutableHandle(selfHandle->savedContext_);
// Account for the `this` argument stored as the first element of ctx.
const uint32_t argCount = self->argCount_;
// Generators cannot be used as constructors, so newTarget is always
// undefined.
HermesValue newTarget = HermesValue::encodeUndefinedValue();
ScopedNativeCallFrame frame{
runtime,
argCount, // Account for `this`.
selfHandle.getHermesValue(),
newTarget,
ctx->at(0)};
if (LLVM_UNLIKELY(frame.overflowed()))
return runtime.raiseStackOverflow(Runtime::StackOverflowKind::NativeStack);
for (ArrayStorage::size_type i = 0, e = argCount; i < e; ++i) {
frame->getArgRef(i) = ctx->at(i + 1);
}
// Force lazy compilation immediately in order to size the context properly.
// We're about to call the function anyway, so this doesn't reduce laziness.
// Note that this will do nothing after the very first time a lazy function
// is called, so we only resize before we save any registers at all.
if (LLVM_UNLIKELY(selfHandle->getCodeBlock()->isLazy())) {
selfHandle->getCodeBlock()->lazyCompile(runtime);
if (LLVM_UNLIKELY(
ArrayStorage::resize(
ctx,
runtime,
getContextSize(
selfHandle->getCodeBlock(), selfHandle->argCount_)) ==
ExecutionStatus::EXCEPTION)) {
return ExecutionStatus::EXCEPTION;
}
selfHandle->savedContext_.set(runtime, ctx.get(), &runtime.getHeap());
}
return JSFunction::_callImpl(selfHandle, runtime);
}
void GeneratorInnerFunction::restoreStack(Runtime &runtime) {
const uint32_t frameOffset = getFrameOffsetInContext();
const uint32_t frameSize = getFrameSizeInContext(runtime);
// Start at the lower end of the range to be copied.
PinnedHermesValue *dst = runtime.getCurrentFrame().ptr();
assert(
dst + frameSize <= runtime.getStackPointer() &&
"writing off the end of the stack");
const GCHermesValue *src =
savedContext_.getNonNull(runtime)->data() + frameOffset;
GCHermesValueUtil::copyToPinned(src, src + frameSize, dst);
}
void GeneratorInnerFunction::saveStack(Runtime &runtime) {
const uint32_t frameOffset = getFrameOffsetInContext();
const uint32_t frameSize = getFrameSizeInContext(runtime);
// Start at the lower end of the range to be copied.
PinnedHermesValue *first = runtime.getCurrentFrame().ptr();
assert(
first + frameSize <= runtime.getStackPointer() &&
"reading off the end of the stack");
// Use GCHermesValue::copy to ensure write barriers are executed.
GCHermesValue::copy(
first,
first + frameSize,
savedContext_.getNonNull(runtime)->data() + frameOffset,
&runtime.getHeap());
}
} // namespace vm
} // namespace hermes