diff --git a/src/NativeScript/ObjC/Constructor/ObjCConstructorBase.mm b/src/NativeScript/ObjC/Constructor/ObjCConstructorBase.mm index 77a647b56..a0cfcbc18 100644 --- a/src/NativeScript/ObjC/Constructor/ObjCConstructorBase.mm +++ b/src/NativeScript/ObjC/Constructor/ObjCConstructorBase.mm @@ -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); @@ -294,8 +295,13 @@ static JSValue getInitializerForSwiftStyleConstruction(ExecState* execState, Obj ObjCConstructorBase* newTarget = jsCast(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; if (ObjCConstructorNative* nativeConstructor = jsDynamicCast(vm, constructor)) { allocatedPlaceHolder = AllocatedPlaceholder::create(vm, jsCast(execState->lexicalGlobalObject()), nativeConstructor->allocatedPlaceholderStructure(), instance, constructor->instancesStructure()); diff --git a/src/webkit b/src/webkit index 6f47561c4..a483b704b 160000 --- a/src/webkit +++ b/src/webkit @@ -1 +1 @@ -Subproject commit 6f47561c41075d0f6824f303c33ae719acdf5a29 +Subproject commit a483b704b7288588adc977ea5fdbb92c755f46d4 diff --git a/tests/TestRunner/app/Inheritance/TypeScriptTests.js b/tests/TestRunner/app/Inheritance/TypeScriptTests.js index 9b4e4737b..d4337a95a 100644 --- a/tests/TestRunner/app/Inheritance/TypeScriptTests.js +++ b/tests/TestRunner/app/Inheritance/TypeScriptTests.js @@ -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; @@ -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; @@ -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; @@ -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; @@ -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; @@ -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); @@ -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; @@ -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; diff --git a/tests/TestRunner/app/Inheritance/TypeScriptTests.ts b/tests/TestRunner/app/Inheritance/TypeScriptTests.ts index 5eb512a2e..0bc4d3576 100644 --- a/tests/TestRunner/app/Inheritance/TypeScriptTests.ts +++ b/tests/TestRunner/app/Inheritance/TypeScriptTests.ts @@ -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(); @@ -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(); @@ -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(); @@ -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(); @@ -368,7 +426,7 @@ describe(module.id, function () { 'variadicSelector:native x:9 called' ); }); - + it('TypeScriptDecoratedExposedMethodsCalledFromJs', function () { var object = TSDecoratedObject.alloc().init(); @@ -382,5 +440,4 @@ describe(module.id, function () { 'staticFunc:9 called' ); }); - }); diff --git a/tests/TestRunner/app/Marshalling/ReferenceTests.js b/tests/TestRunner/app/Marshalling/ReferenceTests.js index 874382929..246443c2e 100644 --- a/tests/TestRunner/app/Marshalling/ReferenceTests.js +++ b/tests/TestRunner/app/Marshalling/ReferenceTests.js @@ -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}")`); } }); diff --git a/tests/TestRunner/app/shared b/tests/TestRunner/app/shared index 6b32678c3..1844a35e2 160000 --- a/tests/TestRunner/app/shared +++ b/tests/TestRunner/app/shared @@ -1 +1 @@ -Subproject commit 6b32678c3123c2ab5bcae32d8b2e7abf5cefa753 +Subproject commit 1844a35e20ff91f2256af866afe6ed0c4c794089 diff --git a/tests/TestRunner/app/tsconfig.json b/tests/TestRunner/app/tsconfig.json new file mode 100644 index 000000000..6957679f9 --- /dev/null +++ b/tests/TestRunner/app/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es5", + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "noEmitHelpers": false, + "noEmitOnError": true, + "lib": [ + "es6", + "dom" + ] + }, + "exclude": [ + ] +}