Skip to content

Commit 1035757

Browse files
committed
fix(ios): make KrollPromise no-op under covers when KrollFinalizer is running to avoid crash
* when finalizing don't even try to generate error object from context
1 parent a59e7e3 commit 1035757

File tree

4 files changed

+70
-17
lines changed

4 files changed

+70
-17
lines changed

iphone/TitaniumKit/TitaniumKit/Sources/Kroll/KrollObject.h

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ bool KrollDeleteProperty(JSContextRef ctx, JSObjectRef object, JSStringRef prope
4848
+ (id)toID:(KrollContext *)context value:(JSValueRef)ref;
4949
+ (JSValueRef)toValue:(KrollContext *)context value:(id)obj;
5050
+ (id)nonNull:(id)value;
51+
+ (BOOL)isFinalizing;
5152

5253
/**
5354
Checks if a property with the given name exists on our target.

iphone/TitaniumKit/TitaniumKit/Sources/Kroll/KrollObject.m

+8-1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ JSValueRef ConvertIdTiValue(KrollContext *context, id obj)
8787
//
8888
// callback for handling finalization (in JS land)
8989
//
90+
static BOOL finalizing = NO;
9091
void KrollFinalizer(JSObjectRef ref)
9192
{
9293
id o = (id)JSObjectGetPrivate(ref);
@@ -112,9 +113,10 @@ void KrollFinalizer(JSObjectRef ref)
112113
}
113114
}
114115
}
115-
116+
finalizing = YES;
116117
[o release];
117118
o = nil;
119+
finalizing = NO;
118120
}
119121

120122
bool KrollDeleteProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception)
@@ -399,6 +401,11 @@ + (JSClassRef)jsClassRef
399401
return KrollObjectClassRef;
400402
}
401403

404+
+ (BOOL)isFinalizing
405+
{
406+
return finalizing;
407+
}
408+
402409
- (id)initWithTarget:(id)target_ context:(KrollContext *)context_
403410
{
404411
if (self = [self init]) {

iphone/TitaniumKit/TitaniumKit/Sources/Kroll/KrollPromise.h

+3
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@
1313
@private
1414
JSValue *resolveFunc;
1515
JSValue *rejectFunc;
16+
BOOL _fulfilled;
17+
BOOL _flushMe;
1618
}
1719

1820
@property (readonly, nonatomic) JSValue *JSValue;
1921

2022
- (void)resolve:(NSArray *)arguments;
2123
- (void)reject:(NSArray *)arguments;
2224
- (void)rejectWithErrorMessage:(NSString *)message;
25+
- (void)flush;
2326

2427
- (KrollPromise *)initInContext:(JSContext *)context;
2528

iphone/TitaniumKit/TitaniumKit/Sources/Kroll/KrollPromise.m

+58-16
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
#import "KrollPromise.h"
2+
#import "KrollObject.h"
23
#import "TiExceptionHandler.h"
34

45
@implementation KrollPromise
56

67
- (KrollPromise *)initInContext:(JSContext *)context
78
{
89
if (self = [super init]) {
10+
if ([KrollObject isFinalizing]) {
11+
// We cannot create a Promise in this context! If an object is being finalized the function
12+
// we call to generate a Promise will crash complaining about the Promise constructor not being an object!
13+
return self;
14+
}
915
if (@available(iOS 13, *)) {
1016
// Use iOS 13 APIs.
1117
JSObjectRef resolve;
1218
JSObjectRef reject;
1319
JSValueRef exception = NULL;
14-
1520
JSObjectRef promiseRef = JSObjectMakeDeferredPromise(context.JSGlobalContextRef, &resolve, &reject, &exception);
1621
if (exception) {
17-
// FIXME: Randomly getting "null is not an object" and I don't know what is null here. The context? The Promise prototype?
1822
// report exception
1923
JSValue *error = [JSValue valueWithJSValueRef:exception inContext:context];
20-
NSLog(@"%@", error[@"message"]);
2124
[context setException:error];
2225
_JSValue = [[JSValue valueWithUndefinedInContext:context] retain];
2326
resolveFunc = [[JSValue valueWithUndefinedInContext:context] retain];
@@ -41,7 +44,6 @@ - (KrollPromise *)initInContext:(JSContext *)context
4144
if (exception != nil) {
4245
[TiExceptionHandler.defaultExceptionHandler reportScriptError:exception inJSContext:context];
4346
}
44-
// FIXME: Do we need to use a ManagedJSValue here rather than retain it? We do expose it to the JS Engine!
4547
_JSValue = [[createPromise callWithArguments:@[ executor ]] retain];
4648
resolveFunc = [executor[@"resolve"] retain];
4749
rejectFunc = [executor[@"reject"] retain];
@@ -67,35 +69,75 @@ + (KrollPromise *)rejected:(NSArray *)arguments inContext:(JSContext *)context
6769
+ (KrollPromise *)rejectedWithErrorMessage:(NSString *)message inContext:(JSContext *)context
6870
{
6971
KrollPromise *promise = [[[KrollPromise alloc] initInContext:context] autorelease];
70-
JSValue *error = [JSValue valueWithNewErrorFromMessage:message inContext:context];
71-
[promise reject:@[ error ]];
72+
[promise rejectWithErrorMessage:message];
7273
return promise;
7374
}
7475

7576
- (void)resolve:(NSArray *)arguments
7677
{
77-
[resolveFunc callWithArguments:arguments];
78+
if (resolveFunc) {
79+
[resolveFunc callWithArguments:arguments];
80+
}
81+
_fulfilled = YES;
82+
if (_flushMe) {
83+
[self flush];
84+
_flushMe = NO;
85+
}
7886
}
7987

8088
- (void)reject:(NSArray *)arguments
8189
{
82-
[rejectFunc callWithArguments:arguments];
90+
if (rejectFunc) {
91+
[rejectFunc callWithArguments:arguments];
92+
}
93+
_fulfilled = YES;
94+
if (_flushMe) {
95+
[self flush];
96+
_flushMe = NO;
97+
}
98+
}
99+
100+
// We need to handle "settling" fulfillments/rejections so we don't leave unhandled rejections around
101+
- (void)flush
102+
{
103+
if (_JSValue == nil) {
104+
// assume no-op Promise generated during finalization
105+
return;
106+
}
107+
if (_fulfilled) {
108+
JSValue *noop = [JSValue
109+
valueWithObject:^() {
110+
}
111+
inContext:rejectFunc.context];
112+
[_JSValue invokeMethod:@"then" withArguments:@[ noop, noop ]];
113+
} else {
114+
// not yet fulfilled/rejected, so mark it to get flushed after it is
115+
_flushMe = YES;
116+
}
83117
}
84118

85119
- (void)rejectWithErrorMessage:(NSString *)message
86120
{
87-
JSValue *error = [JSValue valueWithNewErrorFromMessage:message inContext:rejectFunc.context];
88-
[self reject:@[ error ]];
121+
if (rejectFunc) {
122+
JSValue *error = [JSValue valueWithNewErrorFromMessage:message inContext:rejectFunc.context];
123+
[self reject:@[ error ]];
124+
}
89125
}
90126

91127
- (void)dealloc
92128
{
93-
[_JSValue release];
94-
_JSValue = nil;
95-
[resolveFunc release];
96-
resolveFunc = nil;
97-
[rejectFunc release];
98-
rejectFunc = nil;
129+
if (_JSValue) {
130+
[_JSValue release];
131+
_JSValue = nil;
132+
}
133+
if (resolveFunc) {
134+
[resolveFunc release];
135+
resolveFunc = nil;
136+
}
137+
if (rejectFunc) {
138+
[rejectFunc release];
139+
rejectFunc = nil;
140+
}
99141
[super dealloc];
100142
}
101143

0 commit comments

Comments
 (0)