Skip to content

Commit 8ee88c2

Browse files
authored
Merge pull request #1203 from NativeScript/trifonov/improve-extend
Improve extend
2 parents 294e86f + 7e1ad52 commit 8ee88c2

File tree

12 files changed

+1459
-956
lines changed

12 files changed

+1459
-956
lines changed

test-app/app/src/main/assets/app/tests/extendedClassesTests.js

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
describe("Tests extended classes ", function () {
22

3-
it("Instance_with_no_extension_shouldnt_use_previously_defined_implementation_object", function () {
3+
it("Instance with no extension shouldn't use previously defined implementation object", function () {
44
var MyButton = com.tns.tests.Button1.extend({
55
toString: function () {
66
return "overriden toString method of chronometer instance";
@@ -21,8 +21,68 @@ describe("Tests extended classes ", function () {
2121
expect(labelToString).not.toBe(labelToString1);
2222
expect(labelgetIMAGE_ID_PROP).not.toBe(labelgetIMAGE_ID_PROP1);
2323
});
24+
25+
it("Having a class with static method named 'extend' and extending it in a child class shouldn't crash the app", function (){
26+
27+
/* JS below the comment is generated from the following TS code
28+
29+
class Base{
30+
static extend(){
31+
return "expectedValue";
32+
}
33+
}
34+
class Child extends Base{
35+
}
36+
37+
class SecondChild extends Child{
38+
}
39+
40+
41+
const superProto = Object.getPrototypeOf(Child.prototype)
42+
const Super = superProto.constructor;
43+
//console.log(Super.extend());
44+
45+
var child = Object.create(Child);
46+
//console.log(child.extend());
47+
48+
//console.log(Child.extend());
49+
50+
*/
51+
52+
var Base = /** @class */ (function () {
53+
function Base() {
54+
}
55+
Base.extend = function () {
56+
return "expectedValue";
57+
};
58+
return Base;
59+
}());
60+
var Child = /** @class */ (function (_super) {
61+
__extends(Child, _super);
62+
function Child() {
63+
return _super !== null && _super.apply(this, arguments) || this;
64+
}
65+
return Child;
66+
}(Base));
67+
var SecondChild = /** @class */ (function (_super) {
68+
__extends(SecondChild, _super);
69+
function SecondChild() {
70+
return _super !== null && _super.apply(this, arguments) || this;
71+
}
72+
return SecondChild;
73+
}(Child));
74+
75+
var superProto = Object.getPrototypeOf(Child.prototype);
76+
var Super = superProto.constructor;
77+
expect(Super.extend()).toBe("expectedValue");
78+
79+
var child = Object.create(Child);
80+
expect(child.extend()).toBe("expectedValue");
81+
82+
expect(Child.extend()).toBe("expectedValue");
83+
});
2484

25-
it("Instance_with_extension_shouldnt_use_previously_defined_implementation_object", function () {
85+
it("Instance with extension shouldn't use previously defined implementation object", function () {
2686

2787
var MyButton = com.tns.tests.Button1.extend({
2888
toString: function () {
@@ -53,7 +113,7 @@ describe("Tests extended classes ", function () {
53113
expect(labelgetIMAGE_ID_PROP).not.toBe(labelgetIMAGE_ID_PROP1);
54114
});
55115

56-
it("Newly_created_instances_should_behave_the_same_and_not_use_previously_defined_implementation_objects", function () {
116+
it("Newly created instances should behave the same and not use previously defined implementation objects", function () {
57117

58118
var button1 = new com.tns.tests.Button1();
59119
var labelgetIMAGE_ID_PROP1 = button1.getIMAGE_ID_PROP();
@@ -74,7 +134,7 @@ describe("Tests extended classes ", function () {
74134
expect(labelgetIMAGE_ID_PROP1).toBe(labelgetIMAGE_ID_PROP2);
75135
});
76136

77-
it("should not crash with no exception when incorrectly calling extended class constructor", function () {
137+
it("Should not crash with no exception when incorrectly calling extended class constructor", function () {
78138
let MyObj = java.lang.Object.extend({
79139
toString: () => { return "It's MyObj" }
80140
});
Lines changed: 121 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,4 @@
1-
(function() {
2-
var __extends_ts = function (d, b) {
3-
if (!b.extend) {
4-
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
5-
}
6-
7-
function __() { this.constructor = d; }
8-
__.prototype = b.prototype;
9-
d.prototype = new __();
10-
};
1+
(function () {
112

123
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
134
var c = arguments.length;
@@ -27,102 +18,138 @@
2718
};
2819

2920
// For backward compatibility.
30-
var __native = function(thiz) {
31-
// we are setting the __container__ property to the base class when the super method is called
32-
// if the constructor returns the __native(this) call we will use the old implementation
33-
// copying all the properties to the result
34-
// otherwise if we are using the result from the super() method call we won't need such logic
35-
// as thiz already contains the parent properties
36-
// this way we now support both implementations in typescript generated constructors:
37-
// 1: super(); return __native(this);
38-
// 2: return super() || this;
39-
if(thiz.__container__) {
40-
var result = thiz.__proto__;
41-
42-
for (var prop in thiz)
43-
{
44-
if (thiz.hasOwnProperty(prop))
45-
{
46-
thiz.__proto__[prop] = thiz[prop];
47-
delete thiz[prop];
48-
}
49-
}
50-
51-
thiz.constructor = undefined;
52-
thiz.__proto__ = undefined;
53-
Object.freeze(thiz);
54-
Object.preventExtensions(thiz);
55-
return result;
21+
var __native = function (thiz) {
22+
// we are setting the __container__ property to the base class when the super method is called
23+
// if the constructor returns the __native(this) call we will use the old implementation
24+
// copying all the properties to the result
25+
// otherwise if we are using the result from the super() method call we won't need such logic
26+
// as thiz already contains the parent properties
27+
// this way we now support both implementations in typescript generated constructors:
28+
// 1: super(); return __native(this);
29+
// 2: return super() || this;
30+
if (thiz.__container__) {
31+
var result = thiz.__proto__;
32+
33+
for (var prop in thiz) {
34+
if (thiz.hasOwnProperty(prop)) {
35+
thiz.__proto__[prop] = thiz[prop];
36+
delete thiz[prop];
37+
}
38+
}
39+
40+
thiz.constructor = undefined;
41+
thiz.__proto__ = undefined;
42+
Object.freeze(thiz);
43+
Object.preventExtensions(thiz);
44+
return result;
5645
} else {
57-
return thiz;
46+
return thiz;
5847
}
5948
};
6049

61-
var __extends = function(Child, Parent) {
50+
var __extends = function (Child, Parent) {
51+
const NATIVE_CODE_REGEX = /\{\s*\[native code\]\s*\}/g;
52+
var extendNativeClass = !!Parent.extend && NATIVE_CODE_REGEX.test(Parent.extend.toString());
53+
if (!extendNativeClass) {
54+
__extends_ts(Child, Parent);
55+
return;
56+
}
57+
if (Parent.__isPrototypeImplementationObject) {
58+
throw new Error("Can not extend an already extended native object.");
59+
}
6260

63-
if (Parent.extend) {
64-
if (Parent.__isPrototypeImplementationObject) {
65-
throw new Error("Can not extend an already extended native object.");
61+
function extend(thiz) {
62+
var child = thiz.__proto__.__child;
63+
if (!child.__extended) {
64+
var parent = thiz.__proto__.__parent;
65+
child.__extended = parent.extend(child.name, child.prototype, true);
66+
// This will deal with "i instanceof child"
67+
child[Symbol.hasInstance] = function (instance) {
68+
return instance instanceof this.__extended;
69+
}
6670
}
71+
return child.__extended;
72+
};
6773

68-
function extend(thiz) {
69-
var child = thiz.__proto__.__child;
70-
if (!child.__extended) {
71-
var parent = thiz.__proto__.__parent;
72-
child.__extended = parent.extend(child.name, child.prototype, true);
73-
// This will deal with "i instanceof child"
74-
child[Symbol.hasInstance] = function(instance) {
75-
return instance instanceof this.__extended;
76-
}
77-
}
78-
return child.__extended;
79-
};
80-
81-
Parent.__activityExtend = function(parent, name, implementationObject) {
82-
__log("__activityExtend called");
83-
return parent.extend(name, implementationObject);
84-
};
85-
86-
Parent.call = function(thiz) {
87-
var Extended = extend(thiz);
88-
thiz.__container__ = true;
89-
if (arguments.length > 1)
90-
{
91-
thiz.__proto__ = new (Function.prototype.bind.apply(Extended, [null].concat(Array.prototype.slice.call(arguments, 1))));
92-
}
93-
else
94-
{
95-
thiz.__proto__ = new Extended()
96-
}
97-
return thiz.__proto__;
98-
};
99-
100-
Parent.apply = function(thiz, args) {
101-
var Extended = extend(thiz);
102-
thiz.__container__ = true;
103-
if (args && args.length > 0)
104-
{
105-
thiz.__proto__ = new (Function.prototype.bind.apply(Extended, [null].concat(args)));
106-
}
107-
else
108-
{
109-
thiz.__proto__ = new Extended();
110-
}
111-
return thiz.__proto__;
112-
};
74+
Parent.__activityExtend = function (parent, name, implementationObject) {
75+
__log("__activityExtend called");
76+
return parent.extend(name, implementationObject);
77+
};
78+
79+
Parent.call = function (thiz) {
80+
var Extended = extend(thiz);
81+
thiz.__container__ = true;
82+
if (arguments.length > 1) {
83+
thiz.__proto__ = new (Function.prototype.bind.apply(Extended, [null].concat(Array.prototype.slice.call(arguments, 1))));
84+
}
85+
else {
86+
thiz.__proto__ = new Extended()
87+
}
88+
return thiz.__proto__;
89+
};
90+
91+
Parent.apply = function (thiz, args) {
92+
var Extended = extend(thiz);
93+
thiz.__container__ = true;
94+
if (args && args.length > 0) {
95+
thiz.__proto__ = new (Function.prototype.bind.apply(Extended, [null].concat(args)));
96+
}
97+
else {
98+
thiz.__proto__ = new Extended();
99+
}
100+
return thiz.__proto__;
101+
};
102+
__extends_ns(Child, Parent);
103+
Child.__isPrototypeImplementationObject = true;
104+
Child.__proto__ = Parent;
105+
Child.prototype.__parent = Parent;
106+
Child.prototype.__child = Child;
107+
}
108+
109+
var __extends_ts = function (child, parent) {
110+
extendStaticFunctions(child, parent);
111+
assignPrototypeFromParentToChild(parent, child);
112+
};
113+
114+
var __extends_ns = function (child, parent) {
115+
if (!parent.extend) {
116+
assignPropertiesFromParentToChild(parent, child);
113117
}
114118

115-
__extends_ts(Child, Parent);
119+
assignPrototypeFromParentToChild(parent, child);
120+
};
116121

122+
var extendStaticFunctions =
123+
Object.setPrototypeOf
124+
|| (hasInternalProtoProperty() && function (child, parent) { child.__proto__ = parent; })
125+
|| assignPropertiesFromParentToChild;
117126

118-
if (Parent.extend) {
119-
Child.__isPrototypeImplementationObject = true;
120-
Child.__proto__ = Parent;
121-
Child.prototype.__parent = Parent;
122-
Child.prototype.__child = Child;
127+
function hasInternalProtoProperty() {
128+
return { __proto__: [] } instanceof Array;
129+
}
130+
131+
function assignPropertiesFromParentToChild(parent, child) {
132+
for (var property in parent) {
133+
if (parent.hasOwnProperty(property)) {
134+
child[property] = parent[property];
135+
}
123136
}
124137
}
125138

139+
function assignPrototypeFromParentToChild(parent, child) {
140+
function __() {
141+
this.constructor = child;
142+
}
143+
144+
if (parent === null) {
145+
child.prototype = Object.create(null);
146+
} else {
147+
__.prototype = parent.prototype;
148+
child.prototype = new __();
149+
}
150+
}
151+
152+
126153
function JavaProxy(className) {
127154
return function (target) {
128155
var extended = target.extend(className, target.prototype)
@@ -133,7 +160,7 @@
133160

134161
function Interfaces(interfacesArr) {
135162
return function (target) {
136-
if(interfacesArr instanceof Array) {
163+
if (interfacesArr instanceof Array) {
137164
// attach interfaces: [] to the object
138165
target.prototype.interfaces = interfacesArr;
139166
}
@@ -146,4 +173,4 @@
146173

147174
global.JavaProxy = JavaProxy;
148175
global.Interfaces = Interfaces;
149-
})()
176+
})()

test-app/build-tools/android-metadata-generator/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ buildscript {
1717
}
1818

1919
dependencies {
20-
classpath 'com.android.tools.build:gradle:3.1.4'
20+
classpath 'com.android.tools.build:gradle:3.2.1'
2121
}
2222
}
2323

0 commit comments

Comments
 (0)