Changeset 241956 in webkit


Ignore:
Timestamp:
Feb 22, 2019 12:20:49 PM (5 years ago)
Author:
ysuzuki@apple.com
Message:

[JSC] putNonEnumerable in JSWrapperMap is too costly
https://bugs.webkit.org/show_bug.cgi?id=194935

Reviewed by Mark Lam.

When we convert Objective-C blocks to JS objects, we need to set up a corresponding function object correctly.
During this allocation, we call [JSValue defineProperty:descriptor] to connect a "prototype" object and "constructor" object.
The problem is that this API has a particularly costly implementation:

_context globalObject][@"Object"] invokeMethod:@"defineProperty" withArguments:@[ self, key, descriptor ?;

This wraps each JS objects appear in this code with Objective-C wrapper. And we convert a NSDictionary to JSObject, which
has "writable", "enumerable", "configurable", "value" fields, and call the "defineProperty" JS function through Objective-C wrapper.
This allocates many Objective-C wrappers and JS objects for descriptors. Since JSC has a direct C++ API "defineOwnProperty", we should
bypass these Objective-C APIs and call JSC's code directly.

This patch changes putNonEnumerable implementation, from calling [JSValue defineProperty:descriptor] to calling JSC C++ code directly.
We do not change [JSValue defineProperty:descriptor] implementation for now because of two reasons. (1) This is not used in our benchmarks
except for this (converting an Objective-C block to a JS object) one path. And (2) even if we were to re-write [JSValue defineProperty:descriptor]
to be more optimized, we would still want to call the JSC C++ version of defineProperty directly here to avoid NSDictionary allocation for a descriptor.

  • API/APIUtils.h:

(setException):

  • API/JSWrapperMap.mm:

(putNonEnumerable):
(copyMethodsToObject):
(-[JSObjCClassInfo allocateConstructorAndPrototypeInContext:]):
(-[JSObjCClassInfo wrapperForObject:inContext:]):

Location:
trunk/Source/JavaScriptCore
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/API/APIUtils.h

    r235254 r241956  
    5858        *returnedExceptionRef = toRef(exec, exception);
    5959#if ENABLE(REMOTE_INSPECTOR)
    60     VM& vm = exec->vm();
     60    JSC::VM& vm = exec->vm();
    6161    vm.vmEntryGlobalObject(exec)->inspectorController().reportAPIException(exec, JSC::Exception::create(vm, exception));
    6262#endif
  • trunk/Source/JavaScriptCore/API/JSWrapperMap.mm

    r237607 r241956  
    2929#if JSC_OBJC_API_ENABLED
    3030#import "APICast.h"
     31#import "APIUtils.h"
    3132#import "JSAPIWrapperObject.h"
    3233#import "JSCInlines.h"
     
    179180}
    180181
    181 inline void putNonEnumerable(JSValue *base, NSString *propertyName, JSValue *value)
    182 {
    183     [base defineProperty:propertyName descriptor:@{
    184         JSPropertyDescriptorValueKey: value,
    185         JSPropertyDescriptorWritableKey: @YES,
    186         JSPropertyDescriptorEnumerableKey: @NO,
    187         JSPropertyDescriptorConfigurableKey: @YES
    188     }];
     182inline void putNonEnumerable(JSContext *context, JSValue *base, NSString *propertyName, JSValue *value)
     183{
     184    if (![base isObject])
     185        return;
     186    JSC::ExecState* exec = toJS([context JSGlobalContextRef]);
     187    JSC::VM& vm = exec->vm();
     188    JSC::JSLockHolder locker(vm);
     189    auto scope = DECLARE_CATCH_SCOPE(vm);
     190
     191    JSC::JSObject* baseObject = JSC::asObject(toJS(exec, [base JSValueRef]));
     192    auto name = OpaqueJSString::tryCreate(propertyName);
     193    if (!name)
     194        return;
     195
     196    JSC::PropertyDescriptor descriptor;
     197    descriptor.setValue(toJS(exec, [value JSValueRef]));
     198    descriptor.setEnumerable(false);
     199    descriptor.setConfigurable(true);
     200    descriptor.setWritable(true);
     201    bool shouldThrow = false;
     202    baseObject->methodTable(vm)->defineOwnProperty(baseObject, exec, name->identifier(&vm), descriptor, shouldThrow);
     203
     204    JSValueRef exception = 0;
     205    if (handleExceptionIfNeeded(scope, exec, &exception) == ExceptionStatus::DidThrow)
     206        [context valueFromNotifyException:exception];
    189207}
    190208
     
    255273            JSObjectRef method = objCCallbackFunctionForMethod(context, objcClass, protocol, isInstanceMethod, sel, types);
    256274            if (method)
    257                 putNonEnumerable(object, name, [JSValue valueWithJSValueRef:method inContext:context]);
     275                putNonEnumerable(context, object, name, [JSValue valueWithJSValueRef:method inContext:context]);
    258276        }
    259277    });
     
    485503        JSValue* prototype = [JSValue valueWithJSValueRef:toRef(jsPrototype) inContext:context];
    486504        JSValue* constructor = [JSValue valueWithJSValueRef:toRef(jsConstructor) inContext:context];
    487         putNonEnumerable(prototype, @"constructor", constructor);
    488         putNonEnumerable(constructor, @"prototype", prototype);
     505
     506        putNonEnumerable(context, prototype, @"constructor", constructor);
     507        putNonEnumerable(context, constructor, @"prototype", prototype);
    489508
    490509        Protocol *exportProtocol = getJSExportProtocol();
     
    512531            JSValue *constructor = [JSValue valueWithJSValueRef:method inContext:context];
    513532            JSValue *prototype = [JSValue valueWithNewObjectInContext:context];
    514             putNonEnumerable(constructor, @"prototype", prototype);
    515             putNonEnumerable(prototype, @"constructor", constructor);
     533            putNonEnumerable(context, constructor, @"prototype", prototype);
     534            putNonEnumerable(context, prototype, @"constructor", constructor);
    516535            return toJS(method);
    517536        }
  • trunk/Source/JavaScriptCore/ChangeLog

    r241955 r241956  
     12019-02-22  Yusuke Suzuki  <ysuzuki@apple.com>
     2
     3        [JSC] putNonEnumerable in JSWrapperMap is too costly
     4        https://bugs.webkit.org/show_bug.cgi?id=194935
     5
     6        Reviewed by Mark Lam.
     7
     8        When we convert Objective-C blocks to JS objects, we need to set up a corresponding function object correctly.
     9        During this allocation, we call [JSValue defineProperty:descriptor] to connect a "prototype" object and "constructor" object.
     10        The problem is that this API has a particularly costly implementation:
     11
     12            [[_context globalObject][@"Object"] invokeMethod:@"defineProperty" withArguments:@[ self, key, descriptor ]];
     13
     14        This wraps each JS objects appear in this code with Objective-C wrapper. And we convert a NSDictionary to JSObject, which
     15        has "writable", "enumerable", "configurable", "value" fields, and call the "defineProperty" JS function through Objective-C wrapper.
     16        This allocates many Objective-C wrappers and JS objects for descriptors. Since JSC has a direct C++ API "defineOwnProperty", we should
     17        bypass these Objective-C APIs and call JSC's code directly.
     18
     19        This patch changes `putNonEnumerable` implementation, from calling [JSValue defineProperty:descriptor] to calling JSC C++ code directly.
     20        We do not change [JSValue defineProperty:descriptor] implementation for now because of two reasons. (1) This is not used in our benchmarks
     21        except for this (converting an Objective-C block to a JS object) one path. And (2) even if we were to re-write [JSValue defineProperty:descriptor]
     22        to be more optimized, we would still want to call the JSC C++ version of defineProperty directly here to avoid NSDictionary allocation for a descriptor.
     23
     24        * API/APIUtils.h:
     25        (setException):
     26        * API/JSWrapperMap.mm:
     27        (putNonEnumerable):
     28        (copyMethodsToObject):
     29        (-[JSObjCClassInfo allocateConstructorAndPrototypeInContext:]):
     30        (-[JSObjCClassInfo wrapperForObject:inContext:]):
     31
    1322019-02-22  Yusuke Suzuki  <ysuzuki@apple.com>
    233
Note: See TracChangeset for help on using the changeset viewer.