Skip to content

fix(runtime): Do not lose exception in TS extends #1211

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/NativeScript/ObjC/Constructor/ObjCConstructorBase.mm
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ static JSValue getInitializerForSwiftStyleConstruction(ExecState* execState, Obj
}

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

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

JSValue thisValue;
Strong<AllocatedPlaceholder> allocatedPlaceHolder;
if (ObjCConstructorNative* nativeConstructor = jsDynamicCast<ObjCConstructorNative*>(vm, constructor)) {
allocatedPlaceHolder = AllocatedPlaceholder::create(vm, jsCast<GlobalObject*>(execState->lexicalGlobalObject()), nativeConstructor->allocatedPlaceholderStructure(), instance, constructor->instancesStructure());
Expand Down
2 changes: 1 addition & 1 deletion src/webkit
152 changes: 128 additions & 24 deletions tests/TestRunner/app/Inheritance/TypeScriptTests.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,30 @@
var TSObject = (function (_super) {
// TODO: Use TypeScript definitions when they get ready
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
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;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var TSObject = /** @class */ (function (_super) {
__extends(TSObject, _super);
function TSObject() {
return _super !== null && _super.apply(this, arguments) || this;
Expand Down Expand Up @@ -58,14 +84,14 @@ var TSObject = (function (_super) {
TSObject.returnsConstructorMethod = function () {
return TSObject;
};
TSObject.property = 1;
TSObject.ObjCExposedMethods = {
'voidSelector': { returns: interop.types.void },
'variadicSelector:x:': { returns: NSObject, params: [NSString, interop.types.int32] }
};
return TSObject;
}(TNSDerivedInterface));
TSObject.property = 1;
TSObject.ObjCExposedMethods = {
'voidSelector': { returns: interop.types.void },
'variadicSelector:x:': { returns: NSObject, params: [NSString, interop.types.int32] }
};
var TSObject1 = (function (_super) {
var TSObject1 = /** @class */ (function (_super) {
__extends(TSObject1, _super);
function TSObject1() {
return _super !== null && _super.apply(this, arguments) || this;
Expand Down Expand Up @@ -98,10 +124,10 @@ var TSObject1 = (function (_super) {
enumerable: true,
configurable: true
});
TSObject1.ObjCProtocols = [TNSBaseProtocol2];
return TSObject1;
}(NSObject));
TSObject1.ObjCProtocols = [TNSBaseProtocol2];
var TSDecoratedObject = (function (_super) {
var TSDecoratedObject = /** @class */ (function (_super) {
__extends(TSDecoratedObject, _super);
function TSDecoratedObject() {
return _super !== null && _super.apply(this, arguments) || this;
Expand All @@ -116,16 +142,22 @@ var TSDecoratedObject = (function (_super) {
TSDecoratedObject.staticFunc = function (x) {
TNSLog('staticFunc:' + x + ' called');
};
__decorate([
ObjCMethod(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], TSDecoratedObject.prototype, "voidSelector", null);
__decorate([
ObjCMethod('variadicSelector:x:', NSObject),
__param(0, ObjCParam(NSString)), __param(1, ObjCParam(interop.types.int32)),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", void 0)
], TSDecoratedObject.prototype, "variadicSelectorX", null);
return TSDecoratedObject;
}(TNSDerivedInterface));
__decorate([
ObjCMethod()
], TSDecoratedObject.prototype, "voidSelector", null);
__decorate([
ObjCMethod('variadicSelector:x:', NSObject),
__param(0, ObjCParam(NSString)), __param(1, ObjCParam(interop.types.int32))
], TSDecoratedObject.prototype, "variadicSelectorX", null);
var TSDecoratedObject1 = (function (_super) {
var TSDecoratedObject1 = /** @class */ (function (_super) {
__extends(TSDecoratedObject1, _super);
function TSDecoratedObject1() {
return _super !== null && _super.apply(this, arguments) || this;
Expand Down Expand Up @@ -158,12 +190,12 @@ var TSDecoratedObject1 = (function (_super) {
enumerable: true,
configurable: true
});
TSDecoratedObject1 = __decorate([
ObjCClass(TNSBaseProtocol2)
], TSDecoratedObject1);
return TSDecoratedObject1;
}(NSObject));
TSDecoratedObject1 = __decorate([
ObjCClass(TNSBaseProtocol2)
], TSDecoratedObject1);
var UnusedConstructor = (function (_super) {
var UnusedConstructor = /** @class */ (function (_super) {
__extends(UnusedConstructor, _super);
function UnusedConstructor() {
var _this = _super !== null && _super.apply(this, arguments) || this;
Expand Down Expand Up @@ -238,6 +270,78 @@ describe(module.id, function () {
expect(TNSGetOutput()).toBe('voidSelector called' +
'variadicSelector:native x:9 called');
});
it("MethodOverrides: errors", function () {
expect(function () {
var TSObjectErr1 = /** @class */ (function (_super) {
__extends(TSObjectErr1, _super);
function TSObjectErr1() {
return _super !== null && _super.apply(this, arguments) || this;
}
Object.defineProperty(TSObjectErr1.prototype, "isEqual", {
get: function () { return false; },
enumerable: true,
configurable: true
});
return TSObjectErr1;
}(NSObject));
return TSObjectErr1.alloc();
}).toThrowError(/Cannot override native method "isEqual" with a property, define it as a JS function instead./);
expect(function () {
var TSObjectErr2 = /** @class */ (function (_super) {
__extends(TSObjectErr2, _super);
function TSObjectErr2() {
return _super !== null && _super.apply(this, arguments) || this;
}
return TSObjectErr2;
}(TNSDerivedInterface));
TSObjectErr2.prototype.isEqual = true;
return TSObjectErr2.alloc();
}).toThrowError(/true cannot override native method "isEqual"./);
});
it('ExposeWithWrongParams', function () {
expect(function () {
var ExposeWithWrongParams = /** @class */ (function (_super) {
__extends(ExposeWithWrongParams, _super);
function ExposeWithWrongParams() {
return _super !== null && _super.apply(this, arguments) || this;
}
ExposeWithWrongParams.prototype.wrongRet = function () { };
ExposeWithWrongParams.ObjCExposedMethods = {
'wrongRet': { returns: "a string", params: [interop.types.selector] }
};
return ExposeWithWrongParams;
}(NSObject));
return ExposeWithWrongParams.alloc();
}).toThrowError("\"a string\" Method wrongRet has an invalid return type encoding");
expect(function () {
var ExposeWithWrongParams2 = /** @class */ (function (_super) {
__extends(ExposeWithWrongParams2, _super);
function ExposeWithWrongParams2() {
return _super !== null && _super.apply(this, arguments) || this;
}
ExposeWithWrongParams2.prototype.wrongArg = function () { };
ExposeWithWrongParams2.ObjCExposedMethods = {
'wrongArg': { returns: interop.types.selector, params: [3] }
};
return ExposeWithWrongParams2;
}(NSObject));
return ExposeWithWrongParams2.alloc();
}).toThrowError("3 Method wrongArg has an invalid type encoding for argument 1");
expect(function () {
var ExposeWithWrongParams3 = /** @class */ (function (_super) {
__extends(ExposeWithWrongParams3, _super);
function ExposeWithWrongParams3() {
return _super !== null && _super.apply(this, arguments) || this;
}
ExposeWithWrongParams3.prototype.wrongArg = function () { };
ExposeWithWrongParams3.ObjCExposedMethods = {
'wrongArg': { returns: interop.types.void, params: { notArray: true } }
};
return ExposeWithWrongParams3;
}(NSObject));
return ExposeWithWrongParams3.alloc();
}).toThrowError("Object The 'params' property of method wrongArg is not an array");
});
it('AddedNewProperty', function () {
var object = TSObject.alloc().init();
expect(object.method()).toBe(1);
Expand All @@ -257,12 +361,12 @@ describe(module.id, function () {
'baseProtocolProperty1Optional called');
});
it('PlainExtends', function () {
var A = (function () {
var A = /** @class */ (function () {
function A() {
}
return A;
}());
var B = (function (_super) {
var B = /** @class */ (function (_super) {
__extends(B, _super);
function B() {
return _super !== null && _super.apply(this, arguments) || this;
Expand All @@ -273,7 +377,7 @@ describe(module.id, function () {
});
it('Scope', function () {
global["Derived"] = 3;
var Derived = (function (_super) {
var Derived = /** @class */ (function (_super) {
__extends(Derived, _super);
function Derived() {
return _super !== null && _super.apply(this, arguments) || this;
Expand Down
65 changes: 61 additions & 4 deletions tests/TestRunner/app/Inheritance/TypeScriptTests.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
// TODO: Use TypeScript definitions when they get ready

declare function afterEach(param);
declare function describe(name, func);
declare function expect(param);
declare function it(name, func);
declare function TNSClearOutput();
declare function TNSLog(message);
declare function TNSGetOutput();
Expand All @@ -10,8 +14,16 @@ declare var module;
declare function NSStringFromClass(klass);
declare function NSClassFromString(klassName);

declare function ObjCClass(param);
declare function ObjCMethod(name?, param?);
declare function ObjCParam(param);

declare var interop;
declare var jasmine;

declare var global;

declare var NSString: any;


declare class NSObject {
public static alloc();
Expand Down Expand Up @@ -297,6 +309,52 @@ describe(module.id, function () {
);
});

it("MethodOverrides: errors", function () {
expect(() => {
class TSObjectErr1 extends NSObject {
get isEqual() { return false; }
}
return TSObjectErr1.alloc();
}).toThrowError(/Cannot override native method "isEqual" with a property, define it as a JS function instead./);

expect(() => {
class TSObjectErr2 extends TNSDerivedInterface {
}
(TSObjectErr2.prototype as any).isEqual = true;
return TSObjectErr2.alloc();
}).toThrowError(/true cannot override native method "isEqual"./);
});

it('ExposeWithWrongParams', function () {
expect(() => {
class ExposeWithWrongParams extends NSObject {
wrongRet() {}
public static ObjCExposedMethods = {
'wrongRet': { returns: "a string", params: [interop.types.selector] }
};
}
return ExposeWithWrongParams.alloc();
}).toThrowError("\"a string\" Method wrongRet has an invalid return type encoding");
expect(() => {
class ExposeWithWrongParams2 extends NSObject {
wrongArg() {}
public static ObjCExposedMethods = {
'wrongArg': { returns: interop.types.selector, params: [3] }
};
}
return ExposeWithWrongParams2.alloc();
}).toThrowError("3 Method wrongArg has an invalid type encoding for argument 1");
expect(() => {
class ExposeWithWrongParams3 extends NSObject {
wrongArg() {}
public static ObjCExposedMethods = {
'wrongArg': { returns: interop.types.void, params: { notArray: true } }
};
}
return ExposeWithWrongParams3.alloc();
}).toThrowError("Object The 'params' property of method wrongArg is not an array");
});

it('AddedNewProperty', function () {
var object = TSObject.alloc().init();

Expand Down Expand Up @@ -345,7 +403,7 @@ describe(module.id, function () {
it('TypeScriptDecoratedShim', function () {
expect(global.__decorate).toBeDefined();
expect(global.__param).toBeDefined();
});
});

it('TypeScriptDecoratedProtocolImplementation', function () {
var object = TSDecoratedObject1.alloc().init();
Expand All @@ -368,7 +426,7 @@ describe(module.id, function () {
'variadicSelector:native x:9 called'
);
});

it('TypeScriptDecoratedExposedMethodsCalledFromJs', function () {
var object = TSDecoratedObject.alloc().init();

Expand All @@ -382,5 +440,4 @@ describe(module.id, function () {
'staticFunc:9 called'
);
});

});
9 changes: 6 additions & 3 deletions tests/TestRunner/app/Marshalling/ReferenceTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,14 +153,17 @@ describe(module.id, function () {

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

const result = functionWithUCharPtr(ptr);

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

const strUtf8 = utf8.encode(str);
for (i in strUtf8) {
const actual = strUtf8.charCodeAt(i);
const expected = result[i];
const actual = result[i];
const expected = strUtf8.charCodeAt(i);
expect(actual).toBe(expected, `Char code difference at index ${i} ("${actual}" vs "${expected}")`);
}
});
Expand Down
2 changes: 1 addition & 1 deletion tests/TestRunner/app/shared
Submodule shared updated 1 files
+14 −2 WeakRef.js
16 changes: 16 additions & 0 deletions tests/TestRunner/app/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"noEmitHelpers": false,
"noEmitOnError": true,
"lib": [
"es6",
"dom"
]
},
"exclude": [
]
}