Ignore:
Timestamp:
Apr 13, 2014, 11:01:54 AM (11 years ago)
Author:
[email protected]
Message:

Rewrite Function.bind as a builtin
https://bugs.webkit.org/show_bug.cgi?id=131083

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

This change removes the existing function.bind implementation
entirely so JSBoundFunction is no more.

Instead we just return a regular JS closure with a few
private properties hanging off it that allow us to perform
the necessary bound function fakery. While most of this is
simple, a couple of key changes:

  • The parser and lexer now directly track whether they're parsing code for call or construct and convert the private name @IsConstructor into TRUETOK or FALSETOK as appropriate. This automatically gives us the ability to vary behaviour from within the builtin. It also leaves a lot of headroom for trivial future improvements.
  • The instanceof operator now uses the prototypeForHasInstance private name, and we have a helper function to ensure that all objects that need to can update their magical 'prototype' property pair correctly.
  • API/JSScriptRef.cpp:

(parseScript):

  • JavaScriptCore.xcodeproj/project.pbxproj:
  • builtins/BuiltinExecutables.cpp:

(JSC::BuiltinExecutables::createBuiltinExecutable):

  • builtins/Function.prototype.js:

(bind.bindingFunction):
(bind.else.bindingFunction):
(bind):

  • bytecode/UnlinkedCodeBlock.cpp:

(JSC::generateFunctionCodeBlock):

  • bytecompiler/NodesCodegen.cpp:

(JSC::InstanceOfNode::emitBytecode):

  • interpreter/Interpreter.cpp:
  • parser/Lexer.cpp:

(JSC::Lexer<T>::Lexer):
(JSC::Lexer<LChar>::parseIdentifier):
(JSC::Lexer<UChar>::parseIdentifier):

  • parser/Lexer.h:
  • parser/Parser.cpp:

(JSC::Parser<LexerType>::Parser):
(JSC::Parser<LexerType>::parseInner):

  • parser/Parser.h:

(JSC::parse):

  • parser/ParserModes.h:
  • runtime/CodeCache.cpp:

(JSC::CodeCache::getGlobalCodeBlock):
(JSC::CodeCache::getFunctionExecutableFromGlobalCode):

  • runtime/CommonIdentifiers.h:
  • runtime/Completion.cpp:

(JSC::checkSyntax):

  • runtime/Executable.cpp:

(JSC::ProgramExecutable::checkSyntax):

  • runtime/FunctionPrototype.cpp:

(JSC::FunctionPrototype::addFunctionProperties):
(JSC::functionProtoFuncBind): Deleted.

  • runtime/JSBoundFunction.cpp: Removed.
  • runtime/JSBoundFunction.h: Removed.
  • runtime/JSFunction.cpp:

(JSC::RetrieveCallerFunctionFunctor::RetrieveCallerFunctionFunctor):
(JSC::RetrieveCallerFunctionFunctor::operator()):
(JSC::retrieveCallerFunction):
(JSC::JSFunction::getOwnPropertySlot):
(JSC::JSFunction::defineOwnProperty):

  • runtime/JSGlobalObject.cpp:

(JSC::JSGlobalObject::reset):

  • runtime/JSGlobalObjectFunctions.cpp:

(JSC::globalFuncSetTypeErrorAccessor):

  • runtime/JSGlobalObjectFunctions.h:
  • runtime/JSObject.h:

(JSC::JSObject::inlineGetOwnPropertySlot):

Source/WebCore:

Switch WebCore to use the helper functions when defining the
prototype properties on DOM constructors, and update bindings
tests accordingly.

  • bindings/js/JSImageConstructor.cpp:

(WebCore::JSImageConstructor::finishCreation):

  • bindings/scripts/CodeGeneratorJS.pm:

(GenerateConstructorHelperMethods):

  • bindings/scripts/test/JS/JSTestActiveDOMObject.cpp:

(WebCore::JSTestActiveDOMObjectConstructor::finishCreation):

  • bindings/scripts/test/JS/JSTestCustomNamedGetter.cpp:

(WebCore::JSTestCustomNamedGetterConstructor::finishCreation):

  • bindings/scripts/test/JS/JSTestEventConstructor.cpp:

(WebCore::JSTestEventConstructorConstructor::finishCreation):

  • bindings/scripts/test/JS/JSTestEventTarget.cpp:

(WebCore::JSTestEventTargetConstructor::finishCreation):

  • bindings/scripts/test/JS/JSTestException.cpp:

(WebCore::JSTestExceptionConstructor::finishCreation):

  • bindings/scripts/test/JS/JSTestGenerateIsReachable.cpp:

(WebCore::JSTestGenerateIsReachableConstructor::finishCreation):

  • bindings/scripts/test/JS/JSTestInterface.cpp:

(WebCore::JSTestInterfaceConstructor::finishCreation):

  • bindings/scripts/test/JS/JSTestMediaQueryListListener.cpp:

(WebCore::JSTestMediaQueryListListenerConstructor::finishCreation):

  • bindings/scripts/test/JS/JSTestNamedConstructor.cpp:

(WebCore::JSTestNamedConstructorConstructor::finishCreation):
(WebCore::JSTestNamedConstructorNamedConstructor::finishCreation):

  • bindings/scripts/test/JS/JSTestNode.cpp:

(WebCore::JSTestNodeConstructor::finishCreation):

  • bindings/scripts/test/JS/JSTestObj.cpp:

(WebCore::JSTestObjConstructor::finishCreation):

  • bindings/scripts/test/JS/JSTestOverloadedConstructors.cpp:

(WebCore::JSTestOverloadedConstructorsConstructor::finishCreation):

  • bindings/scripts/test/JS/JSTestSerializedScriptValueInterface.cpp:

(WebCore::JSTestSerializedScriptValueInterfaceConstructor::finishCreation):

  • bindings/scripts/test/JS/JSTestTypedefs.cpp:

(WebCore::JSTestTypedefsConstructor::finishCreation):

  • bindings/scripts/test/JS/JSattribute.cpp:

(WebCore::JSattributeConstructor::finishCreation):

  • bindings/scripts/test/JS/JSreadonly.cpp:

(WebCore::JSreadonlyConstructor::finishCreation):

LayoutTests:

Testing.

  • js/dom/function-bind-expected.txt:
  • js/regress/function-bind-expected.txt: Added.
  • js/regress/function-bind.html: Added.
  • js/regress/script-tests/function-bind.js: Added.

(foo):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/runtime/JSFunction.cpp

    r167165 r167199  
    3434#include "GetterSetter.h"
    3535#include "JSArray.h"
    36 #include "JSBoundFunction.h"
    3736#include "JSFunctionInlines.h"
    3837#include "JSGlobalObject.h"
     
    252251class RetrieveCallerFunctionFunctor {
    253252public:
    254     RetrieveCallerFunctionFunctor(JSFunction* functionObj)
    255         : m_targetCallee(jsDynamicCast<JSObject*>(functionObj))
     253    RetrieveCallerFunctionFunctor(ExecState* exec, JSFunction* functionObj)
     254        : m_exec(exec)
     255        , m_targetCallee(jsDynamicCast<JSObject*>(functionObj))
    256256        , m_hasFoundFrame(false)
    257257        , m_hasSkippedToCallerFrame(false)
     
    266266        JSObject* callee = visitor->callee();
    267267
    268         if (callee && callee->inherits(JSBoundFunction::info()))
     268        if (callee && callee->hasOwnProperty(m_exec, m_exec->propertyNames().boundFunctionNamePrivateName))
    269269            return StackVisitor::Continue;
    270270
     
    284284
    285285private:
     286    ExecState* m_exec;
    286287    JSObject* m_targetCallee;
    287288    bool m_hasFoundFrame;
     
    292293static JSValue retrieveCallerFunction(ExecState* exec, JSFunction* functionObj)
    293294{
    294     RetrieveCallerFunctionFunctor functor(functionObj);
     295    RetrieveCallerFunctionFunctor functor(exec, functionObj);
    295296    exec->iterate(functor);
    296297    return functor.result();
     
    329330{
    330331    JSFunction* thisObject = jsCast<JSFunction*>(object);
    331     if (thisObject->isHostOrBuiltinFunction())
     332    if (thisObject->isHostFunction())
    332333        return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
    333 
    334     if (propertyName == exec->propertyNames().prototype) {
     334    if (thisObject->isBuiltinFunction()) {
     335        if (propertyName == exec->propertyNames().caller) {
     336            if (thisObject->jsExecutable()->isStrictMode()) {
     337                bool result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
     338                if (!result) {
     339                    thisObject->putDirectAccessor(exec, propertyName, thisObject->globalObject()->throwTypeErrorGetterSetter(exec->vm()), DontDelete | DontEnum | Accessor);
     340                    result = Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
     341                    ASSERT(result);
     342                }
     343                return result;
     344            }
     345            slot.setCacheableCustom(thisObject, ReadOnly | DontEnum | DontDelete, callerGetter);
     346            return true;
     347        }
     348        if (propertyName == exec->propertyNames().prototypeForHasInstancePrivateName) {
     349            PropertySlot boundFunctionSlot(thisObject);
     350            if (Base::getOwnPropertySlot(thisObject, exec, exec->propertyNames().boundFunctionPrivateName, boundFunctionSlot)) {
     351                JSValue boundFunction = boundFunctionSlot.getValue(exec, exec->propertyNames().boundFunctionPrivateName);
     352                PropertySlot boundPrototypeSlot(asObject(boundFunction));
     353                if (asObject(boundFunction)->getPropertySlot(exec, propertyName, boundPrototypeSlot)) {
     354                    slot.setValue(boundPrototypeSlot.slotBase(), boundPrototypeSlot.attributes(), boundPrototypeSlot.getValue(exec, propertyName));
     355                    return true;
     356                }
     357            }
     358        }
     359        if (propertyName == exec->propertyNames().name) {
     360            PropertySlot nameSlot(thisObject);
     361            if (Base::getOwnPropertySlot(thisObject, exec, exec->vm().propertyNames->boundFunctionNamePrivateName, nameSlot)) {
     362                slot.setValue(thisObject, DontEnum | DontDelete | ReadOnly, nameSlot.getValue(exec, exec->vm().propertyNames->boundFunctionNamePrivateName));
     363                return true;
     364            }
     365        }
     366        if (propertyName == exec->propertyNames().length) {
     367            PropertySlot lengthSlot(thisObject);
     368            if (Base::getOwnPropertySlot(thisObject, exec, exec->vm().propertyNames->boundFunctionLengthPrivateName, lengthSlot)) {
     369                slot.setValue(thisObject, DontEnum | DontDelete | ReadOnly, lengthSlot.getValue(exec, exec->vm().propertyNames->boundFunctionLengthPrivateName));
     370                return true;
     371            }
     372        }
     373           
     374        return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
     375    }
     376   
     377    if (propertyName == exec->propertyNames().prototype || propertyName == exec->propertyNames().prototypeForHasInstancePrivateName) {
    335378        VM& vm = exec->vm();
    336379        unsigned attributes;
     
    339382            JSObject* prototype = constructEmptyObject(exec);
    340383            prototype->putDirect(vm, exec->propertyNames().constructor, thisObject, DontEnum);
    341             thisObject->putDirect(vm, exec->propertyNames().prototype, prototype, DontDelete | DontEnum);
     384            thisObject->putDirectPrototypeProperty(vm, prototype, DontDelete | DontEnum);
    342385            offset = thisObject->getDirectOffset(vm, exec->propertyNames().prototype, attributes);
    343386            ASSERT(isValidOffset(offset));
     
    391434{
    392435    JSFunction* thisObject = jsCast<JSFunction*>(object);
    393     if (!thisObject->isHostOrBuiltinFunction() && (mode == IncludeDontEnumProperties)) {
    394         VM& vm = exec->vm();
    395         // Make sure prototype has been reified.
    396         PropertySlot slot(thisObject);
    397         thisObject->methodTable(vm)->getOwnPropertySlot(thisObject, exec, vm.propertyNames->prototype, slot);
    398 
    399         propertyNames.add(vm.propertyNames->arguments);
    400         propertyNames.add(vm.propertyNames->caller);
    401         propertyNames.add(vm.propertyNames->length);
    402         propertyNames.add(vm.propertyNames->name);
     436    if (mode == IncludeDontEnumProperties) {
     437        bool shouldIncludeJSFunctionProperties = !thisObject->isHostOrBuiltinFunction();
     438        if (!shouldIncludeJSFunctionProperties && thisObject->isBuiltinFunction()) {
     439            PropertySlot boundFunctionSlot(thisObject);
     440            shouldIncludeJSFunctionProperties = Base::getOwnPropertySlot(thisObject, exec, exec->propertyNames().boundFunctionPrivateName, boundFunctionSlot);
     441        }
     442        if (shouldIncludeJSFunctionProperties) {
     443            VM& vm = exec->vm();
     444            // Make sure prototype has been reified.
     445            PropertySlot slot(thisObject);
     446            thisObject->methodTable(vm)->getOwnPropertySlot(thisObject, exec, vm.propertyNames->prototype, slot);
     447
     448            propertyNames.add(vm.propertyNames->arguments);
     449            propertyNames.add(vm.propertyNames->caller);
     450            propertyNames.add(vm.propertyNames->length);
     451            propertyNames.add(vm.propertyNames->name);
     452        }
    403453    }
    404454    Base::getOwnNonIndexPropertyNames(thisObject, exec, propertyNames, mode);
     
    420470        thisObject->m_allocationProfileWatchpoint.fireAll();
    421471        // Don't allow this to be cached, since a [[Put]] must clear m_allocationProfile.
    422         PutPropertySlot dontCache(thisObject);
    423         Base::put(thisObject, exec, propertyName, value, dontCache);
     472        PutPropertySlot dontCachePrototype(thisObject);
     473        Base::put(thisObject, exec, propertyName, value, dontCachePrototype);
     474        PutPropertySlot dontCachePrototypeForHasInstance(thisObject);
     475        Base::put(thisObject, exec, exec->propertyNames().prototypeForHasInstancePrivateName, value, dontCachePrototypeForHasInstance);
    424476        return;
    425477    }
     
    466518        thisObject->m_allocationProfile.clear();
    467519        thisObject->m_allocationProfileWatchpoint.fireAll();
    468         return Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException);
     520        if (!Base::defineOwnProperty(object, exec, propertyName, descriptor, throwException))
     521            return false;
     522        Base::defineOwnProperty(object, exec, exec->propertyNames().prototypeForHasInstancePrivateName, descriptor, throwException);
     523        return true;
    469524    }
    470525
Note: See TracChangeset for help on using the changeset viewer.