Bug 159446 - RELEASE_ASSERT(!thisObject) in ObjCCallbackFunctionImpl::call when calling JSExport ObjC Constructor without operator new
Summary: RELEASE_ASSERT(!thisObject) in ObjCCallbackFunctionImpl::call when calling JS...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: JavaScriptCore (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Joseph Pecoraro
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2016-07-05 17:27 PDT by Joseph Pecoraro
Modified: 2016-07-05 22:04 PDT (History)
7 users (show)

See Also:


Attachments
[PATCH] Proposed Fix (9.92 KB, patch)
2016-07-05 19:39 PDT, Joseph Pecoraro
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Joseph Pecoraro 2016-07-05 17:27:03 PDT
Summary:
RELEASE_ASSERT(!thisObject) in ObjCCallbackFunctionImpl::call when calling JSExport ObjC Constructor without operator new

Test:
/*
 * shell> xcrun clang -framework Foundation -framework JavaScriptCore construct.m
 */

#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>

@protocol JSFooExports <JSExport>
- (instancetype)initWithName:(NSString *)name;
@end

@interface JSFoo : NSObject <JSFooExports>
- (instancetype)initWithName:(NSString *)name;
@end

@implementation JSFoo
- (instancetype)initWithName:(NSString *)name {
    self = [super init];
    if (!self) return nil;
    NSLog(@">>> initWithName: %@", name);
    return self;
}
@end

int main()
{
    JSContext *ctx = [[JSContext alloc] init];
    ctx[@"JSFoo"] = [JSFoo class];
    [ctx evaluateScript:@"JSFoo()"];
    return 0;
}

Steps to Reproduce:
1. Compile + Run test
  => RELEASE_ASSERT crash immediately

Crash:
Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_BREAKPOINT (SIGTRAP)
Exception Codes:       0x0000000000000002, 0x0000000000000000

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   com.apple.JavaScriptCore      	0x000000010bdf0e6e JSC::ObjCCallbackFunctionImpl::call(JSContext*, OpaqueJSValue*, unsigned long, OpaqueJSValue const* const*, OpaqueJSValue const**) + 654 (ObjCCallbackFunction.mm:557)
1   com.apple.JavaScriptCore      	0x000000010bdf0926 JSC::objCCallbackFunctionCallAsFunction(OpaqueJSContext const*, OpaqueJSValue*, OpaqueJSValue*, unsigned long, OpaqueJSValue const* const*, OpaqueJSValue const**) + 262 (ObjCCallbackFunction.mm:462)
2   com.apple.JavaScriptCore      	0x000000010bdf1b7d long long JSC::APICallbackFunction::call<JSC::ObjCCallbackFunction>(JSC::ExecState*) + 573 (APICallbackFunction.h:61)
3   com.apple.JavaScriptCore      	0x000000010bdb15b3 JSC::LLInt::setUpCall(JSC::ExecState*, JSC::Instruction*, JSC::CodeSpecializationKind, JSC::JSValue, JSC::LLIntCallLinkInfo*) + 595 (LLIntSlowPaths.cpp:1203)
4   com.apple.JavaScriptCore      	0x000000010bdb87ce llint_entry + 24803
5   com.apple.JavaScriptCore      	0x000000010bdb2508 vmEntryToJavaScript + 299
6   com.apple.JavaScriptCore      	0x000000010bc0c2ee JSC::JITCode::execute(JSC::VM*, JSC::ProtoCallFrame*) + 158 (JITCode.cpp:81)
7   com.apple.JavaScriptCore      	0x000000010bb65cb5 JSC::Interpreter::execute(JSC::ProgramExecutable*, JSC::ExecState*, JSC::JSObject*) + 17125 (Interpreter.cpp:961)
8   com.apple.JavaScriptCore      	0x000000010b7d0ea5 JSC::evaluate(JSC::ExecState*, JSC::SourceCode const&, JSC::JSValue, WTF::NakedPtr<JSC::Exception>&) + 469 (Completion.cpp:107)
9   com.apple.JavaScriptCore      	0x000000010bc61d98 JSEvaluateScript + 456 (NakedPtr.h:54)
10  com.apple.JavaScriptCore      	0x000000010bc73639 -[JSContext evaluateScript:withSourceURL:] + 105 (JSContext.mm:102)
11  a.out                         	0x000000010b56ee9b main + 171
12  libdyld.dylib                 	0x00007fff918095ad start + 1
...
Comment 1 Joseph Pecoraro 2016-07-05 17:39:31 PDT
JavaScript Classes disallow calling the constructor without `new`.

> class Foo {}; Foo()
< Exception: TypeError: Cannot call a class constructor

Maybe the ObjC bridge should do the same.
Comment 2 Geoffrey Garen 2016-07-05 17:40:41 PDT
JS exception seems like much better behavior than RELEASE_ASSERT.
Comment 3 Joseph Pecoraro 2016-07-05 19:39:39 PDT
Created attachment 282842 [details]
[PATCH] Proposed Fix
Comment 4 WebKit Commit Bot 2016-07-05 19:40:25 PDT
Attachment 282842 [details] did not pass style-queue:


ERROR: Source/JavaScriptCore/API/tests/testapi.mm:1352:  Place brace on its own line for function definitions.  [whitespace/braces] [4]
Total errors found: 1 in 9 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 5 Joseph Pecoraro 2016-07-05 19:43:19 PDT
The new testapi output looks like:

> testapi[41761:43777520] TEST: "ObjC Constructor without 'new' pre": PASSED
> testapi[41761:43777520] TypeError: Cannot call a class constructor without |new|
> testapi[41761:43777520] TEST: "ObjC Constructor without 'new' post": PASSED

I went ahead and updated the JavaScript error message for ES6 classes. Other browsers hinted that this was missing 'new', and I think we should do the same:

  Safari Before:
  TypeError: Cannot call a class constructor

  Safari After:
  TypeError: Cannot call a class constructor without |new|


  Chrome:
  TypeError: Class constructor Foo cannot be invoked without 'new'(…)

  Firefox:
  TypeError: Cannot call a class constructor without |new|
Comment 6 Mark Lam 2016-07-05 19:46:04 PDT
Comment on attachment 282842 [details]
[PATCH] Proposed Fix

View in context: https://bugs.webkit.org/attachment.cgi?id=282842&action=review

r=me

> Source/JavaScriptCore/API/tests/testapi.mm:1337
> +

Please remove this empty line.
Comment 7 WebKit Commit Bot 2016-07-05 22:04:40 PDT
Comment on attachment 282842 [details]
[PATCH] Proposed Fix

Clearing flags on attachment: 282842

Committed r202846: <http://trac.webkit.org/changeset/202846>
Comment 8 WebKit Commit Bot 2016-07-05 22:04:44 PDT
All reviewed patches have been landed.  Closing bug.