diff options
Diffstat (limited to 'Source/JavaScriptCore/runtime/JSBoundFunction.cpp')
-rw-r--r-- | Source/JavaScriptCore/runtime/JSBoundFunction.cpp | 158 |
1 files changed, 122 insertions, 36 deletions
diff --git a/Source/JavaScriptCore/runtime/JSBoundFunction.cpp b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp index f22827a37..cc2ec6305 100644 --- a/Source/JavaScriptCore/runtime/JSBoundFunction.cpp +++ b/Source/JavaScriptCore/runtime/JSBoundFunction.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011, 2016-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,67 +28,154 @@ #include "GetterSetter.h" #include "JSGlobalObject.h" -#include "Operations.h" +#include "JSCInlines.h" namespace JSC { -const ClassInfo JSBoundFunction::s_info = { "Function", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSBoundFunction) }; +const ClassInfo JSBoundFunction::s_info = { "Function", &Base::s_info, 0, CREATE_METHOD_TABLE(JSBoundFunction) }; + +EncodedJSValue JSC_HOST_CALL boundThisNoArgsFunctionCall(ExecState* exec) +{ + JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(exec->jsCallee()); + + MarkedArgumentBuffer args; + for (unsigned i = 0; i < exec->argumentCount(); ++i) + args.append(exec->uncheckedArgument(i)); + + JSFunction* targetFunction = jsCast<JSFunction*>(boundFunction->targetFunction()); + ExecutableBase* executable = targetFunction->executable(); + if (executable->hasJITCodeForCall()) { + // Force the executable to cache its arity entrypoint. + executable->entrypointFor(CodeForCall, MustCheckArity); + } + CallData callData; + CallType callType = getCallData(targetFunction, callData); + ASSERT(callType != CallType::None); + return JSValue::encode(call(exec, targetFunction, callType, callData, boundFunction->boundThis(), args)); +} EncodedJSValue JSC_HOST_CALL boundFunctionCall(ExecState* exec) { - JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(exec->callee()); + JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(exec->jsCallee()); - ASSERT(isJSArray(boundFunction->boundArgs())); // Currently this is true! - JSArray* boundArgs = asArray(boundFunction->boundArgs()); + JSArray* boundArgs = boundFunction->boundArgs(); MarkedArgumentBuffer args; - for (unsigned i = 0; i < boundArgs->length(); ++i) - args.append(boundArgs->getIndexQuickly(i)); + if (boundArgs) { + for (unsigned i = 0; i < boundArgs->length(); ++i) + args.append(boundArgs->getIndexQuickly(i)); + } for (unsigned i = 0; i < exec->argumentCount(); ++i) args.append(exec->uncheckedArgument(i)); JSObject* targetFunction = boundFunction->targetFunction(); CallData callData; CallType callType = getCallData(targetFunction, callData); - ASSERT(callType != CallTypeNone); + ASSERT(callType != CallType::None); return JSValue::encode(call(exec, targetFunction, callType, callData, boundFunction->boundThis(), args)); } +EncodedJSValue JSC_HOST_CALL boundThisNoArgsFunctionConstruct(ExecState* exec) +{ + JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(exec->jsCallee()); + + MarkedArgumentBuffer args; + for (unsigned i = 0; i < exec->argumentCount(); ++i) + args.append(exec->uncheckedArgument(i)); + + JSFunction* targetFunction = jsCast<JSFunction*>(boundFunction->targetFunction()); + ConstructData constructData; + ConstructType constructType = getConstructData(targetFunction, constructData); + ASSERT(constructType != ConstructType::None); + return JSValue::encode(construct(exec, targetFunction, constructType, constructData, args)); +} + EncodedJSValue JSC_HOST_CALL boundFunctionConstruct(ExecState* exec) { - JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(exec->callee()); + JSBoundFunction* boundFunction = jsCast<JSBoundFunction*>(exec->jsCallee()); - ASSERT(isJSArray(boundFunction->boundArgs())); // Currently this is true! - JSArray* boundArgs = asArray(boundFunction->boundArgs()); + JSArray* boundArgs = boundFunction->boundArgs(); MarkedArgumentBuffer args; - for (unsigned i = 0; i < boundArgs->length(); ++i) - args.append(boundArgs->getIndexQuickly(i)); + if (boundArgs) { + for (unsigned i = 0; i < boundArgs->length(); ++i) + args.append(boundArgs->getIndexQuickly(i)); + } for (unsigned i = 0; i < exec->argumentCount(); ++i) args.append(exec->uncheckedArgument(i)); JSObject* targetFunction = boundFunction->targetFunction(); ConstructData constructData; ConstructType constructType = getConstructData(targetFunction, constructData); - ASSERT(constructType != ConstructTypeNone); + ASSERT(constructType != ConstructType::None); return JSValue::encode(construct(exec, targetFunction, constructType, constructData, args)); } -JSBoundFunction* JSBoundFunction::create(VM& vm, JSGlobalObject* globalObject, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs, int length, const String& name) +EncodedJSValue JSC_HOST_CALL isBoundFunction(ExecState* exec) { - ConstructData constructData; - ConstructType constructType = JSC::getConstructData(targetFunction, constructData); - bool canConstruct = constructType != ConstructTypeNone; - NativeExecutable* executable = vm.getHostFunction(boundFunctionCall, canConstruct ? boundFunctionConstruct : callHostFunctionAsConstructor); - JSBoundFunction* function = new (NotNull, allocateCell<JSBoundFunction>(vm.heap)) JSBoundFunction(vm, globalObject, globalObject->boundFunctionStructure(), targetFunction, boundThis, boundArgs); + return JSValue::encode(JSValue(static_cast<bool>(jsDynamicCast<JSBoundFunction*>(exec->vm(), exec->uncheckedArgument(0))))); +} - function->finishCreation(vm, executable, length, name); - return function; +EncodedJSValue JSC_HOST_CALL hasInstanceBoundFunction(ExecState* exec) +{ + JSBoundFunction* boundObject = jsCast<JSBoundFunction*>(exec->uncheckedArgument(0)); + JSValue value = exec->uncheckedArgument(1); + + return JSValue::encode(jsBoolean(boundObject->targetFunction()->hasInstance(exec, value))); +} + +inline Structure* getBoundFunctionStructure(VM& vm, ExecState* exec, JSGlobalObject* globalObject, JSObject* targetFunction) +{ + auto scope = DECLARE_THROW_SCOPE(vm); + JSValue prototype = targetFunction->getPrototype(vm, exec); + RETURN_IF_EXCEPTION(scope, nullptr); + JSFunction* targetJSFunction = jsDynamicCast<JSFunction*>(vm, targetFunction); + + // We only cache the structure of the bound function if the bindee is a JSFunction since there + // isn't any good place to put the structure on Internal Functions. + if (targetJSFunction) { + Structure* structure = targetJSFunction->rareData(vm)->getBoundFunctionStructure(); + if (structure && structure->storedPrototype() == prototype && structure->globalObject() == globalObject) + return structure; + } + + Structure* result = globalObject->boundFunctionStructure(); + + // It would be nice if the structure map was keyed global objects in addition to the other things. Unfortunately, it is not + // currently. Whoever works on caching structure changes for prototype transistions should consider this problem as well. + // See: https://bugs.webkit.org/show_bug.cgi?id=152738 + if (prototype.isObject() && prototype.getObject()->globalObject() == globalObject) { + result = vm.prototypeMap.emptyStructureForPrototypeFromBaseStructure(globalObject, prototype.getObject(), result); + ASSERT_WITH_SECURITY_IMPLICATION(result->globalObject() == globalObject); + } else + result = Structure::create(vm, globalObject, prototype, result->typeInfo(), result->classInfo()); + + if (targetJSFunction) + targetJSFunction->rareData(vm)->setBoundFunctionStructure(vm, result); + + return result; } -void JSBoundFunction::destroy(JSCell* cell) +JSBoundFunction* JSBoundFunction::create(VM& vm, ExecState* exec, JSGlobalObject* globalObject, JSObject* targetFunction, JSValue boundThis, JSArray* boundArgs, int length, const String& name) { - static_cast<JSBoundFunction*>(cell)->JSBoundFunction::~JSBoundFunction(); + auto scope = DECLARE_THROW_SCOPE(vm); + ConstructData constructData; + ConstructType constructType = JSC::getConstructData(targetFunction, constructData); + bool canConstruct = constructType != ConstructType::None; + + bool slowCase = boundArgs || !getJSFunction(targetFunction); + + NativeExecutable* executable = vm.getHostFunction( + slowCase ? boundFunctionCall : boundThisNoArgsFunctionCall, + slowCase ? NoIntrinsic : BoundThisNoArgsFunctionCallIntrinsic, + canConstruct ? (slowCase ? boundFunctionConstruct : boundThisNoArgsFunctionConstruct) : callHostFunctionAsConstructor, nullptr, + name); + Structure* structure = getBoundFunctionStructure(vm, exec, globalObject, targetFunction); + RETURN_IF_EXCEPTION(scope, nullptr); + JSBoundFunction* function = new (NotNull, allocateCell<JSBoundFunction>(vm.heap)) JSBoundFunction(vm, globalObject, structure, targetFunction, boundThis, boundArgs); + + function->finishCreation(vm, executable, length); + return function; } bool JSBoundFunction::customHasInstance(JSObject* object, ExecState* exec, JSValue value) @@ -96,34 +183,33 @@ bool JSBoundFunction::customHasInstance(JSObject* object, ExecState* exec, JSVal return jsCast<JSBoundFunction*>(object)->m_targetFunction->hasInstance(exec, value); } -JSBoundFunction::JSBoundFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure, JSObject* targetFunction, JSValue boundThis, JSValue boundArgs) +JSBoundFunction::JSBoundFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure, JSObject* targetFunction, JSValue boundThis, JSArray* boundArgs) : Base(vm, globalObject, structure) , m_targetFunction(vm, this, targetFunction) , m_boundThis(vm, this, boundThis) - , m_boundArgs(vm, this, boundArgs) + , m_boundArgs(vm, this, boundArgs, WriteBarrier<JSArray>::MayBeNull) { } -void JSBoundFunction::finishCreation(VM& vm, NativeExecutable* executable, int length, const String& name) +void JSBoundFunction::finishCreation(VM& vm, NativeExecutable* executable, int length) { + String name; // We lazily create our 'name' string property. Base::finishCreation(vm, executable, length, name); - ASSERT(inherits(info())); + ASSERT(inherits(vm, info())); - putDirectNonIndexAccessor(vm, vm.propertyNames->arguments, globalObject()->throwTypeErrorGetterSetter(vm), DontDelete | DontEnum | Accessor); - putDirectNonIndexAccessor(vm, vm.propertyNames->caller, globalObject()->throwTypeErrorGetterSetter(vm), DontDelete | DontEnum | Accessor); + putDirectNonIndexAccessor(vm, vm.propertyNames->arguments, globalObject()->throwTypeErrorArgumentsCalleeAndCallerGetterSetter(), DontDelete | DontEnum | Accessor); + putDirectNonIndexAccessor(vm, vm.propertyNames->caller, globalObject()->throwTypeErrorArgumentsCalleeAndCallerGetterSetter(), DontDelete | DontEnum | Accessor); } void JSBoundFunction::visitChildren(JSCell* cell, SlotVisitor& visitor) { JSBoundFunction* thisObject = jsCast<JSBoundFunction*>(cell); ASSERT_GC_OBJECT_INHERITS(thisObject, info()); - COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); - ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); Base::visitChildren(thisObject, visitor); - visitor.append(&thisObject->m_targetFunction); - visitor.append(&thisObject->m_boundThis); - visitor.append(&thisObject->m_boundArgs); + visitor.append(thisObject->m_targetFunction); + visitor.append(thisObject->m_boundThis); + visitor.append(thisObject->m_boundArgs); } } // namespace JSC |