Skip to content

Commit 77705a5

Browse files
authored
Merge pull request #1211 from NativeScript/bektchiev/dont-lose-exception
fix(runtime): Do not lose exception in TS extends
2 parents 775598a + fabe90e commit 77705a5

File tree

7 files changed

+220
-34
lines changed

7 files changed

+220
-34
lines changed

src/NativeScript/ObjC/Constructor/ObjCConstructorBase.mm

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ static JSValue getInitializerForSwiftStyleConstruction(ExecState* execState, Obj
280280
}
281281

282282
if (initializer && initializer.isCell()) {
283+
auto scope = DECLARE_CATCH_SCOPE(vm);
283284
CallData callData;
284285
CallType callType = JSC::getCallData(execState->vm(), initializer, callData);
285286
ASSERT(callType != CallType::None);
@@ -294,8 +295,13 @@ static JSValue getInitializerForSwiftStyleConstruction(ExecState* execState, Obj
294295

295296
ObjCConstructorBase* newTarget = jsCast<ObjCConstructorBase*>(execState->newTarget());
296297
id instance = [newTarget->klasses().known alloc];
297-
JSValue thisValue;
298+
if (scope.exception()) {
299+
// When allocating a JS Derived native instance, it is possible to throw a JS exception.
300+
// Discard the native instance and do not call an initializer for it.
301+
return JSValue::encode(jsUndefined());
302+
}
298303

304+
JSValue thisValue;
299305
Strong<AllocatedPlaceholder> allocatedPlaceHolder;
300306
if (ObjCConstructorNative* nativeConstructor = jsDynamicCast<ObjCConstructorNative*>(vm, constructor)) {
301307
allocatedPlaceHolder = AllocatedPlaceholder::create(vm, jsCast<GlobalObject*>(execState->lexicalGlobalObject()), nativeConstructor->allocatedPlaceholderStructure(), instance, constructor->instancesStructure());

tests/TestRunner/app/Inheritance/TypeScriptTests.js

Lines changed: 128 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,30 @@
1-
var TSObject = (function (_super) {
1+
// TODO: Use TypeScript definitions when they get ready
2+
var __extends = (this && this.__extends) || (function () {
3+
var extendStatics = function (d, b) {
4+
extendStatics = Object.setPrototypeOf ||
5+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
7+
return extendStatics(d, b);
8+
};
9+
return function (d, b) {
10+
extendStatics(d, b);
11+
function __() { this.constructor = d; }
12+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
13+
};
14+
})();
15+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
16+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
17+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
18+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
19+
return c > 3 && r && Object.defineProperty(target, key, r), r;
20+
};
21+
var __metadata = (this && this.__metadata) || function (k, v) {
22+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
23+
};
24+
var __param = (this && this.__param) || function (paramIndex, decorator) {
25+
return function (target, key) { decorator(target, key, paramIndex); }
26+
};
27+
var TSObject = /** @class */ (function (_super) {
228
__extends(TSObject, _super);
329
function TSObject() {
430
return _super !== null && _super.apply(this, arguments) || this;
@@ -58,14 +84,14 @@ var TSObject = (function (_super) {
5884
TSObject.returnsConstructorMethod = function () {
5985
return TSObject;
6086
};
87+
TSObject.property = 1;
88+
TSObject.ObjCExposedMethods = {
89+
'voidSelector': { returns: interop.types.void },
90+
'variadicSelector:x:': { returns: NSObject, params: [NSString, interop.types.int32] }
91+
};
6192
return TSObject;
6293
}(TNSDerivedInterface));
63-
TSObject.property = 1;
64-
TSObject.ObjCExposedMethods = {
65-
'voidSelector': { returns: interop.types.void },
66-
'variadicSelector:x:': { returns: NSObject, params: [NSString, interop.types.int32] }
67-
};
68-
var TSObject1 = (function (_super) {
94+
var TSObject1 = /** @class */ (function (_super) {
6995
__extends(TSObject1, _super);
7096
function TSObject1() {
7197
return _super !== null && _super.apply(this, arguments) || this;
@@ -98,10 +124,10 @@ var TSObject1 = (function (_super) {
98124
enumerable: true,
99125
configurable: true
100126
});
127+
TSObject1.ObjCProtocols = [TNSBaseProtocol2];
101128
return TSObject1;
102129
}(NSObject));
103-
TSObject1.ObjCProtocols = [TNSBaseProtocol2];
104-
var TSDecoratedObject = (function (_super) {
130+
var TSDecoratedObject = /** @class */ (function (_super) {
105131
__extends(TSDecoratedObject, _super);
106132
function TSDecoratedObject() {
107133
return _super !== null && _super.apply(this, arguments) || this;
@@ -116,16 +142,22 @@ var TSDecoratedObject = (function (_super) {
116142
TSDecoratedObject.staticFunc = function (x) {
117143
TNSLog('staticFunc:' + x + ' called');
118144
};
145+
__decorate([
146+
ObjCMethod(),
147+
__metadata("design:type", Function),
148+
__metadata("design:paramtypes", []),
149+
__metadata("design:returntype", void 0)
150+
], TSDecoratedObject.prototype, "voidSelector", null);
151+
__decorate([
152+
ObjCMethod('variadicSelector:x:', NSObject),
153+
__param(0, ObjCParam(NSString)), __param(1, ObjCParam(interop.types.int32)),
154+
__metadata("design:type", Function),
155+
__metadata("design:paramtypes", [Object, Object]),
156+
__metadata("design:returntype", void 0)
157+
], TSDecoratedObject.prototype, "variadicSelectorX", null);
119158
return TSDecoratedObject;
120159
}(TNSDerivedInterface));
121-
__decorate([
122-
ObjCMethod()
123-
], TSDecoratedObject.prototype, "voidSelector", null);
124-
__decorate([
125-
ObjCMethod('variadicSelector:x:', NSObject),
126-
__param(0, ObjCParam(NSString)), __param(1, ObjCParam(interop.types.int32))
127-
], TSDecoratedObject.prototype, "variadicSelectorX", null);
128-
var TSDecoratedObject1 = (function (_super) {
160+
var TSDecoratedObject1 = /** @class */ (function (_super) {
129161
__extends(TSDecoratedObject1, _super);
130162
function TSDecoratedObject1() {
131163
return _super !== null && _super.apply(this, arguments) || this;
@@ -158,12 +190,12 @@ var TSDecoratedObject1 = (function (_super) {
158190
enumerable: true,
159191
configurable: true
160192
});
193+
TSDecoratedObject1 = __decorate([
194+
ObjCClass(TNSBaseProtocol2)
195+
], TSDecoratedObject1);
161196
return TSDecoratedObject1;
162197
}(NSObject));
163-
TSDecoratedObject1 = __decorate([
164-
ObjCClass(TNSBaseProtocol2)
165-
], TSDecoratedObject1);
166-
var UnusedConstructor = (function (_super) {
198+
var UnusedConstructor = /** @class */ (function (_super) {
167199
__extends(UnusedConstructor, _super);
168200
function UnusedConstructor() {
169201
var _this = _super !== null && _super.apply(this, arguments) || this;
@@ -238,6 +270,78 @@ describe(module.id, function () {
238270
expect(TNSGetOutput()).toBe('voidSelector called' +
239271
'variadicSelector:native x:9 called');
240272
});
273+
it("MethodOverrides: errors", function () {
274+
expect(function () {
275+
var TSObjectErr1 = /** @class */ (function (_super) {
276+
__extends(TSObjectErr1, _super);
277+
function TSObjectErr1() {
278+
return _super !== null && _super.apply(this, arguments) || this;
279+
}
280+
Object.defineProperty(TSObjectErr1.prototype, "isEqual", {
281+
get: function () { return false; },
282+
enumerable: true,
283+
configurable: true
284+
});
285+
return TSObjectErr1;
286+
}(NSObject));
287+
return TSObjectErr1.alloc();
288+
}).toThrowError(/Cannot override native method "isEqual" with a property, define it as a JS function instead./);
289+
expect(function () {
290+
var TSObjectErr2 = /** @class */ (function (_super) {
291+
__extends(TSObjectErr2, _super);
292+
function TSObjectErr2() {
293+
return _super !== null && _super.apply(this, arguments) || this;
294+
}
295+
return TSObjectErr2;
296+
}(TNSDerivedInterface));
297+
TSObjectErr2.prototype.isEqual = true;
298+
return TSObjectErr2.alloc();
299+
}).toThrowError(/true cannot override native method "isEqual"./);
300+
});
301+
it('ExposeWithWrongParams', function () {
302+
expect(function () {
303+
var ExposeWithWrongParams = /** @class */ (function (_super) {
304+
__extends(ExposeWithWrongParams, _super);
305+
function ExposeWithWrongParams() {
306+
return _super !== null && _super.apply(this, arguments) || this;
307+
}
308+
ExposeWithWrongParams.prototype.wrongRet = function () { };
309+
ExposeWithWrongParams.ObjCExposedMethods = {
310+
'wrongRet': { returns: "a string", params: [interop.types.selector] }
311+
};
312+
return ExposeWithWrongParams;
313+
}(NSObject));
314+
return ExposeWithWrongParams.alloc();
315+
}).toThrowError("\"a string\" Method wrongRet has an invalid return type encoding");
316+
expect(function () {
317+
var ExposeWithWrongParams2 = /** @class */ (function (_super) {
318+
__extends(ExposeWithWrongParams2, _super);
319+
function ExposeWithWrongParams2() {
320+
return _super !== null && _super.apply(this, arguments) || this;
321+
}
322+
ExposeWithWrongParams2.prototype.wrongArg = function () { };
323+
ExposeWithWrongParams2.ObjCExposedMethods = {
324+
'wrongArg': { returns: interop.types.selector, params: [3] }
325+
};
326+
return ExposeWithWrongParams2;
327+
}(NSObject));
328+
return ExposeWithWrongParams2.alloc();
329+
}).toThrowError("3 Method wrongArg has an invalid type encoding for argument 1");
330+
expect(function () {
331+
var ExposeWithWrongParams3 = /** @class */ (function (_super) {
332+
__extends(ExposeWithWrongParams3, _super);
333+
function ExposeWithWrongParams3() {
334+
return _super !== null && _super.apply(this, arguments) || this;
335+
}
336+
ExposeWithWrongParams3.prototype.wrongArg = function () { };
337+
ExposeWithWrongParams3.ObjCExposedMethods = {
338+
'wrongArg': { returns: interop.types.void, params: { notArray: true } }
339+
};
340+
return ExposeWithWrongParams3;
341+
}(NSObject));
342+
return ExposeWithWrongParams3.alloc();
343+
}).toThrowError("Object The 'params' property of method wrongArg is not an array");
344+
});
241345
it('AddedNewProperty', function () {
242346
var object = TSObject.alloc().init();
243347
expect(object.method()).toBe(1);
@@ -257,12 +361,12 @@ describe(module.id, function () {
257361
'baseProtocolProperty1Optional called');
258362
});
259363
it('PlainExtends', function () {
260-
var A = (function () {
364+
var A = /** @class */ (function () {
261365
function A() {
262366
}
263367
return A;
264368
}());
265-
var B = (function (_super) {
369+
var B = /** @class */ (function (_super) {
266370
__extends(B, _super);
267371
function B() {
268372
return _super !== null && _super.apply(this, arguments) || this;
@@ -273,7 +377,7 @@ describe(module.id, function () {
273377
});
274378
it('Scope', function () {
275379
global["Derived"] = 3;
276-
var Derived = (function (_super) {
380+
var Derived = /** @class */ (function (_super) {
277381
__extends(Derived, _super);
278382
function Derived() {
279383
return _super !== null && _super.apply(this, arguments) || this;

tests/TestRunner/app/Inheritance/TypeScriptTests.ts

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
// TODO: Use TypeScript definitions when they get ready
22

3+
declare function afterEach(param);
4+
declare function describe(name, func);
5+
declare function expect(param);
6+
declare function it(name, func);
37
declare function TNSClearOutput();
48
declare function TNSLog(message);
59
declare function TNSGetOutput();
@@ -10,8 +14,16 @@ declare var module;
1014
declare function NSStringFromClass(klass);
1115
declare function NSClassFromString(klassName);
1216

17+
declare function ObjCClass(param);
18+
declare function ObjCMethod(name?, param?);
19+
declare function ObjCParam(param);
20+
1321
declare var interop;
14-
declare var jasmine;
22+
23+
declare var global;
24+
25+
declare var NSString: any;
26+
1527

1628
declare class NSObject {
1729
public static alloc();
@@ -297,6 +309,52 @@ describe(module.id, function () {
297309
);
298310
});
299311

312+
it("MethodOverrides: errors", function () {
313+
expect(() => {
314+
class TSObjectErr1 extends NSObject {
315+
get isEqual() { return false; }
316+
}
317+
return TSObjectErr1.alloc();
318+
}).toThrowError(/Cannot override native method "isEqual" with a property, define it as a JS function instead./);
319+
320+
expect(() => {
321+
class TSObjectErr2 extends TNSDerivedInterface {
322+
}
323+
(TSObjectErr2.prototype as any).isEqual = true;
324+
return TSObjectErr2.alloc();
325+
}).toThrowError(/true cannot override native method "isEqual"./);
326+
});
327+
328+
it('ExposeWithWrongParams', function () {
329+
expect(() => {
330+
class ExposeWithWrongParams extends NSObject {
331+
wrongRet() {}
332+
public static ObjCExposedMethods = {
333+
'wrongRet': { returns: "a string", params: [interop.types.selector] }
334+
};
335+
}
336+
return ExposeWithWrongParams.alloc();
337+
}).toThrowError("\"a string\" Method wrongRet has an invalid return type encoding");
338+
expect(() => {
339+
class ExposeWithWrongParams2 extends NSObject {
340+
wrongArg() {}
341+
public static ObjCExposedMethods = {
342+
'wrongArg': { returns: interop.types.selector, params: [3] }
343+
};
344+
}
345+
return ExposeWithWrongParams2.alloc();
346+
}).toThrowError("3 Method wrongArg has an invalid type encoding for argument 1");
347+
expect(() => {
348+
class ExposeWithWrongParams3 extends NSObject {
349+
wrongArg() {}
350+
public static ObjCExposedMethods = {
351+
'wrongArg': { returns: interop.types.void, params: { notArray: true } }
352+
};
353+
}
354+
return ExposeWithWrongParams3.alloc();
355+
}).toThrowError("Object The 'params' property of method wrongArg is not an array");
356+
});
357+
300358
it('AddedNewProperty', function () {
301359
var object = TSObject.alloc().init();
302360

@@ -345,7 +403,7 @@ describe(module.id, function () {
345403
it('TypeScriptDecoratedShim', function () {
346404
expect(global.__decorate).toBeDefined();
347405
expect(global.__param).toBeDefined();
348-
});
406+
});
349407

350408
it('TypeScriptDecoratedProtocolImplementation', function () {
351409
var object = TSDecoratedObject1.alloc().init();
@@ -368,7 +426,7 @@ describe(module.id, function () {
368426
'variadicSelector:native x:9 called'
369427
);
370428
});
371-
429+
372430
it('TypeScriptDecoratedExposedMethodsCalledFromJs', function () {
373431
var object = TSDecoratedObject.alloc().init();
374432

@@ -382,5 +440,4 @@ describe(module.id, function () {
382440
'staticFunc:9 called'
383441
);
384442
});
385-
386443
});

tests/TestRunner/app/Marshalling/ReferenceTests.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,14 +153,17 @@ describe(module.id, function () {
153153

154154
it("CString should be passed as its UTF8 encoding and returned as a reference to unsigned characters", function () {
155155
const str = "test АБВГ";
156-
const result = functionWithUCharPtr(str);
156+
const ptr = interop.alloc(str.length*2 + 1); // alloc 2 bytes per character (although some of them are 1-byte chars)
157+
strcpy(ptr, str);
158+
159+
const result = functionWithUCharPtr(ptr);
157160

158161
expect(TNSGetOutput()).toBe(str);
159162

160163
const strUtf8 = utf8.encode(str);
161164
for (i in strUtf8) {
162-
const actual = strUtf8.charCodeAt(i);
163-
const expected = result[i];
165+
const actual = result[i];
166+
const expected = strUtf8.charCodeAt(i);
164167
expect(actual).toBe(expected, `Char code difference at index ${i} ("${actual}" vs "${expected}")`);
165168
}
166169
});

tests/TestRunner/app/shared

Submodule shared updated 1 file

tests/TestRunner/app/tsconfig.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"compilerOptions": {
3+
"module": "commonjs",
4+
"target": "es5",
5+
"experimentalDecorators": true,
6+
"emitDecoratorMetadata": true,
7+
"noEmitHelpers": false,
8+
"noEmitOnError": true,
9+
"lib": [
10+
"es6",
11+
"dom"
12+
]
13+
},
14+
"exclude": [
15+
]
16+
}

0 commit comments

Comments
 (0)