Skip to content

Commit 36c3b4e

Browse files
committed
Support "new" operator for creating instances of extended native objects (#3)
1 parent c1c535b commit 36c3b4e

6 files changed

Lines changed: 182 additions & 3 deletions

File tree

‎NativeScript/runtime/ClassBuilder.mm‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,8 +243,8 @@
243243
info.GetReturnValue().Set(v8::Undefined(isolate));
244244
}).ToLocalChecked();
245245

246-
PropertyAttribute readOnlyFlags = static_cast<PropertyAttribute>(PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly);
247-
bool success = global->DefineOwnProperty(context, tns::ToV8String(isolate, "__extends"), extendsFunc, readOnlyFlags).FromMaybe(false);
246+
PropertyAttribute flags = static_cast<PropertyAttribute>(PropertyAttribute::DontDelete);
247+
bool success = global->DefineOwnProperty(context, tns::ToV8String(isolate, "__extends"), extendsFunc, flags).FromMaybe(false);
248248
assert(success);
249249
}
250250

‎NativeScript/runtime/Runtime.mm‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "RuntimeConfig.h"
1212
#include "Helpers.h"
1313
#include "Tasks.h"
14+
#include "TSHelpers.h"
1415
#include "WeakRef.h"
1516
#include "Worker.h"
1617

@@ -55,7 +56,6 @@
5556
this->moduleInternal_.RunModule(isolate, "./");
5657

5758
if (tc.HasCaught()) {
58-
HandleScope scope(this->GetIsolate());
5959
tns::LogError(isolate, tc);
6060
assert(false);
6161
}
@@ -121,6 +121,7 @@
121121

122122
ClassBuilder::RegisterBaseTypeScriptExtendsFunction(isolate); // Register the __extends function to the global object
123123
ClassBuilder::RegisterNativeTypeScriptExtendsFunction(isolate); // Override the __extends function for native objects
124+
TSHelpers::Init(isolate);
124125

125126
InlineFunctions::Init(isolate);
126127

‎NativeScript/runtime/TSHelpers.cpp‎

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#include "TSHelpers.h"
2+
#include <string>
3+
#include "Helpers.h"
4+
5+
using namespace v8;
6+
7+
namespace tns {
8+
9+
void TSHelpers::Init(Isolate* isolate) {
10+
// The purpose of this script is to handle the "new" operator when extending native classes:
11+
//
12+
// var InheritingClass = (function (_super) {
13+
// __extends(InheritingClass, _super);
14+
// function InheritingClass() {
15+
// return _super !== null && _super.apply(this, arguments) || this; // <---- _super.apply and _super.call methods will invoke the original .extend() method on the derived class
16+
// }
17+
// return InheritingClass;
18+
// }(BaseClass));
19+
// var obj = new InheritingClass();
20+
21+
std::string source =
22+
"(() => {"
23+
" var __originalExtends = global.__extends;"
24+
" var __extends = (Child, Parent) => {"
25+
" var extendingNativeClass = !!Parent.extend && (Parent.extend.toString().indexOf(\"[native code]\") > -1);"
26+
" if (!extendingNativeClass) {"
27+
" __extends_ts(Child, Parent);"
28+
" return;"
29+
" }"
30+
""
31+
" if (Parent.__isPrototypeImplementationObject) {"
32+
" throw new Error(\"Can not extend an already extended native object.\");"
33+
" }"
34+
""
35+
" function extend(thiz) {"
36+
" var child = thiz.__proto__.__child;"
37+
" if (!child.__extended) {"
38+
" var parent = thiz.__proto__.__parent;"
39+
" child.__extended = parent.extend(child.prototype, {"
40+
" name: child.name,"
41+
" protocols: child.ObjCProtocols || [],"
42+
" exposedMethods: child.ObjCExposedMethods || {}"
43+
" });"
44+
" child[Symbol.hasInstance] = function (instance) {"
45+
" return instance instanceof this.__extended;"
46+
" }"
47+
" }"
48+
" return child.__extended;"
49+
" }"
50+
""
51+
" Parent.call = function (thiz) {"
52+
" var Extended = extend(thiz);"
53+
" thiz.__container__ = true;"
54+
" if (arguments.length > 1) {"
55+
" thiz.__proto__ = new (Function.prototype.bind.apply(Extended, [null].concat(Array.prototype.slice.call(arguments, 1))));"
56+
" } else {"
57+
" thiz.__proto__ = new Extended()"
58+
" }"
59+
" return thiz.__proto__;"
60+
" };"
61+
""
62+
" Parent.apply = function (thiz, args) {"
63+
" var Extended = extend(thiz);"
64+
" thiz.__container__ = true;"
65+
" if (args && args.length > 0) {"
66+
" thiz.__proto__ = new (Function.prototype.bind.apply(Extended, [null].concat(args)));"
67+
" } else {"
68+
" thiz.__proto__ = new Extended();"
69+
" }"
70+
" return thiz.__proto__;"
71+
" };"
72+
""
73+
" __extends_ns(Child, Parent);"
74+
" Child.__isPrototypeImplementationObject = true;"
75+
" Child.__proto__ = Parent;"
76+
" Child.prototype.__parent = Parent;"
77+
" Child.prototype.__child = Child;"
78+
""
79+
" if (__originalExtends) {"
80+
" __originalExtends(Child, Parent);"
81+
" }"
82+
" };"
83+
""
84+
" var __extends_ts = function (child, parent) {"
85+
" extendStaticFunctions(child, parent);"
86+
" assignPrototypeFromParentToChild(parent, child);"
87+
" };"
88+
""
89+
" var __extends_ns = function (child, parent) {"
90+
" if (!parent.extend) {"
91+
" assignPropertiesFromParentToChild(parent, child);"
92+
" }"
93+
""
94+
" assignPrototypeFromParentToChild(parent, child);"
95+
" };"
96+
""
97+
" var extendStaticFunctions ="
98+
" Object.setPrototypeOf"
99+
" || (hasInternalProtoProperty() && function (child, parent) { child.__proto__ = parent; })"
100+
" || assignPropertiesFromParentToChild;"
101+
""
102+
" function hasInternalProtoProperty() {"
103+
" return { __proto__: [] } instanceof Array;"
104+
" }"
105+
""
106+
" function assignPropertiesFromParentToChild(parent, child) {"
107+
" for (var property in parent) {"
108+
" if (parent.hasOwnProperty(property)) {"
109+
" child[property] = parent[property];"
110+
" }"
111+
" }"
112+
" }"
113+
""
114+
" function assignPrototypeFromParentToChild(parent, child) {"
115+
" function __() {"
116+
" this.constructor = child;"
117+
" }"
118+
""
119+
" if (parent === null) {"
120+
" child.prototype = Object.create(null);"
121+
" } else {"
122+
" __.prototype = parent.prototype;"
123+
" child.prototype = new __();"
124+
" }"
125+
" }"
126+
""
127+
" Object.defineProperty(global, \"__extends\", { value: __extends });"
128+
"})()";
129+
130+
Local<Context> context = isolate->GetCurrentContext();
131+
Local<Script> script;
132+
TryCatch tc(isolate);
133+
if (!Script::Compile(context, tns::ToV8String(isolate, source.c_str())).ToLocal(&script) && tc.HasCaught()) {
134+
tns::LogError(isolate, tc);
135+
assert(false);
136+
}
137+
assert(!script.IsEmpty());
138+
139+
Local<Value> result;
140+
if (!script->Run(context).ToLocal(&result) && tc.HasCaught()) {
141+
tns::LogError(isolate, tc);
142+
assert(false);
143+
}
144+
}
145+
146+
}

‎NativeScript/runtime/TSHelpers.h‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#ifndef TSHelpers_h
2+
#define TSHelpers_h
3+
4+
#include "Common.h"
5+
6+
namespace tns {
7+
8+
class TSHelpers {
9+
public:
10+
static void Init(v8::Isolate* isolate);
11+
};
12+
13+
}
14+
15+
#endif /* TSHelpers_h */

‎TestApp/app/tests/Inheritance/TypeScriptTests.js‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,4 +310,13 @@ describe(module.id, function () {
310310
'variadicSelector:js x:5 called' +
311311
'staticFunc:9 called');
312312
});
313+
it("Can use new operator to instantiate native objects", () => {
314+
var object = new TSObject();
315+
object.baseMethod();
316+
object.derivedMethod();
317+
expect(TNSGetOutput()).toBe('js baseMethod called' +
318+
'instance baseMethod called' +
319+
'js derivedMethod called' +
320+
'instance derivedMethod called');
321+
});
313322
});

‎v8ios.xcodeproj/project.pbxproj‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@
202202
C2937527229FC1600075CB16 /* TNSPrimitives.m in Sources */ = {isa = PBXBuildFile; fileRef = C2937511229FC1600075CB16 /* TNSPrimitives.m */; };
203203
C2937528229FC1600075CB16 /* TNSObjCTypes.m in Sources */ = {isa = PBXBuildFile; fileRef = C2937512229FC1600075CB16 /* TNSObjCTypes.m */; };
204204
C2937529229FC1600075CB16 /* TNSPrimitivePointers.m in Sources */ = {isa = PBXBuildFile; fileRef = C2937515229FC1600075CB16 /* TNSPrimitivePointers.m */; };
205+
C298C027233C9AEA000DDF54 /* TSHelpers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C298C025233C9AEA000DDF54 /* TSHelpers.cpp */; };
206+
C298C028233C9AEA000DDF54 /* TSHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = C298C026233C9AEA000DDF54 /* TSHelpers.h */; };
205207
C2A5DEC82327DCF600A91A76 /* NativeScript.h in Headers */ = {isa = PBXBuildFile; fileRef = C2DDEB34229EAB3B00345BFE /* NativeScript.h */; settings = {ATTRIBUTES = (Public, ); }; };
206208
C2A5DFE92328C4B900A91A76 /* TKLiveSync.m in Sources */ = {isa = PBXBuildFile; fileRef = C2A5DFE82328C4B900A91A76 /* TKLiveSync.m */; };
207209
C2A5DFEA2328C4B900A91A76 /* TKLiveSync.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C2A5DFE72328C4B900A91A76 /* TKLiveSync.h */; };
@@ -681,6 +683,8 @@
681683
C2937514229FC1600075CB16 /* TNSAllocLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TNSAllocLog.h; sourceTree = "<group>"; };
682684
C2937515229FC1600075CB16 /* TNSPrimitivePointers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TNSPrimitivePointers.m; sourceTree = "<group>"; };
683685
C2937516229FC1600075CB16 /* TNSRecords.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TNSRecords.h; sourceTree = "<group>"; };
686+
C298C025233C9AEA000DDF54 /* TSHelpers.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TSHelpers.cpp; sourceTree = "<group>"; };
687+
C298C026233C9AEA000DDF54 /* TSHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TSHelpers.h; sourceTree = "<group>"; };
684688
C2A5DFE52328C4B900A91A76 /* libTKLiveSync.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libTKLiveSync.a; sourceTree = BUILT_PRODUCTS_DIR; };
685689
C2A5DFE72328C4B900A91A76 /* TKLiveSync.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TKLiveSync.h; sourceTree = "<group>"; };
686690
C2A5DFE82328C4B900A91A76 /* TKLiveSync.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TKLiveSync.m; sourceTree = "<group>"; };
@@ -1664,6 +1668,8 @@
16641668
C2DDEB75229EAC8200345BFE /* SymbolLoader.mm */,
16651669
C2DDEB76229EAC8200345BFE /* Tasks.h */,
16661670
C2DDEB8C229EAC8300345BFE /* Tasks.cpp */,
1671+
C298C026233C9AEA000DDF54 /* TSHelpers.h */,
1672+
C298C025233C9AEA000DDF54 /* TSHelpers.cpp */,
16671673
C2DDEB82229EAC8200345BFE /* WeakRef.h */,
16681674
C2DDEB69229EAC8100345BFE /* WeakRef.cpp */,
16691675
C22C092122CA3F370080D176 /* Worker.h */,
@@ -1698,6 +1704,7 @@
16981704
isa = PBXHeadersBuildPhase;
16991705
buildActionMask = 2147483647;
17001706
files = (
1707+
C298C028233C9AEA000DDF54 /* TSHelpers.h in Headers */,
17011708
C2A5DEC82327DCF600A91A76 /* NativeScript.h in Headers */,
17021709
C247C17122F82842001D2CA2 /* v8config.h in Headers */,
17031710
C247C36022F828E3001D2CA2 /* Console.h in Headers */,
@@ -2288,6 +2295,7 @@
22882295
C2DDEBB4229EAC8300345BFE /* DictionaryAdapter.mm in Sources */,
22892296
C247C34D22F828E3001D2CA2 /* v8-debugger-agent-impl.cc in Sources */,
22902297
C247C38322F828E3001D2CA2 /* v8-heap-profiler-agent-impl.cc in Sources */,
2298+
C298C027233C9AEA000DDF54 /* TSHelpers.cpp in Sources */,
22912299
C247C34722F828E3001D2CA2 /* injected-script.cc in Sources */,
22922300
C2FEA16F22A3C75C00A5C0FC /* InlineFunctions.cpp in Sources */,
22932301
C247C32F22F828E3001D2CA2 /* utils.mm in Sources */,

0 commit comments

Comments
 (0)