Skip to content

Commit 8aea2c1

Browse files
committed
Fixed broken __core__
1 parent b65a1df commit 8aea2c1

File tree

2 files changed

+280
-10
lines changed

2 files changed

+280
-10
lines changed
Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
__pragma__ ('stripcomments')
2+
3+
/* Nested module-object creator, part of the nesting may already exist and have attributes
4+
5+
A Transcrypt applicaton consists of a main module and additional modules.
6+
Transcrypt modules constitute a unique, unambiguous tree by their dotted names, no matter which of the alternative module root paths they come from.
7+
The main module is represented by a main function with the name of the application.
8+
The locals of this function constitute the outer namespace of the Transcrypt application.
9+
References to all local variables of this function are also assigned to attributes of local variable __all__, using the variable names as an attribute names.
10+
The main function returns this local variable __all__ (that inside the function is also known by the name __world__)
11+
Normally this function result is assigned to window.<application name>.
12+
The function may than be exitted (unless its main line starts an ongoing activity), but the application namespace stays alive by the reference that window has to it.
13+
In case of the ongoing activity including the script is enough to start it, in other cases it has to be started explicitly by calling window.<application name>.<entrypoint function>.
14+
There may be multiple such entrypoint functions.
15+
16+
Additional modules are represented by objects rather than functions, nested into __world__ (so into __all__ of the main function).
17+
This nesting can be directly or indirectly, according to the dotted paths of the additional modules.
18+
One of the methods of the module object is the __init__ function, that's executed once at module initialisation time.
19+
20+
The additional modules also have an __all__ variable, an attribute rather than a local variable.
21+
However this __all__ object is passed to the __init__ function, so becomes a local variable there.
22+
Variables in additional modules first become locals to the __init__ function but references to all of them are assigned to __all__ under their same names.
23+
This resembles the cause of affairs in the main function.
24+
However __world__ only referes to the __all__ of the main module, not of any additional modules.
25+
Importing a module boils down to adding all members of its __all__ to the local namespace, directly or via dotted access, depending on the way of import.
26+
27+
In each local namespace of the module function (main function for main module, __init__ for additional modules) there's a variable __name__ holding the name of the module.
28+
Classes are created inside the static scope of a particular module, and at that (class creation) time their variable __module__ gets assigned a reference to __name__.
29+
This assignement is generated explicitly by the compiler, as the class creation function __new__ of the metaclass isn't in the static scope containing __name__.
30+
31+
In case of
32+
import a
33+
import a.b
34+
a will have been created at the moment that a.b is imported,
35+
so all a.b. is allowed to do is an extra attribute in a, namely a reference to b,
36+
not recreate a, since that would destroy attributes previously present in a
37+
38+
In case of
39+
import a.b
40+
import a
41+
a will have to be created at the moment that a.b is imported
42+
43+
In general in a chain
44+
import a.b.c.d.e
45+
a, a.b, a.b.c and a.b.c.d have to exist before e is created, since a.b.c.d should hold a reference to e.
46+
Since this applies recursively, if e.g. c is already created, we can be sure a and a.b. will also be already created.
47+
48+
So to be able to create e, we'll have to walk the chain a.b.c.d, starting with a.
49+
As soon as we encounter a module in the chain that isn't already there, we'll have to create the remainder (tail) of the chain.
50+
51+
e.g.
52+
import a.b.c.d.e
53+
import a.b.c
54+
55+
will generate
56+
var modules = {};
57+
__nest__ (a, 'b.c.d.e', __init__ (__world__.a.b.c.d.e));
58+
__nest__ (a, 'b.c', __init__ (__world__.a.b.c));
59+
60+
The task of the __nest__ function is to start at the head object and then walk to the chain of objects behind it (tail),
61+
creating the ones that do not exist already, and insert the necessary module reference attributes into them.
62+
*/
63+
64+
export function __nest__ (headObject, tailNames, value) {
65+
var current = headObject;
66+
// In some cases this will be <main function>.__all__,
67+
// which is the main module and is also known under the synonym <main function.__world__.
68+
// N.B. <main function> is the entry point of a Transcrypt application,
69+
// Carrying the same name as the application except the file name extension.
70+
71+
if (tailNames != '') { // Split on empty string doesn't give empty list
72+
// Find the last already created object in tailNames
73+
var tailChain = tailNames.split ('.');
74+
var firstNewIndex = tailChain.length;
75+
for (var index = 0; index < tailChain.length; index++) {
76+
if (!current.hasOwnProperty (tailChain [index])) {
77+
firstNewIndex = index;
78+
break;
79+
}
80+
current = current [tailChain [index]];
81+
}
82+
83+
// Create the rest of the objects, if any
84+
for (var index = firstNewIndex; index < tailChain.length; index++) {
85+
current [tailChain [index]] = {};
86+
current = current [tailChain [index]];
87+
}
88+
}
89+
90+
// Insert its new properties, it may have been created earlier and have other attributes
91+
for (let attrib of Object.getOwnPropertyNames (value)) {
92+
Object.defineProperty (current, attrib, {
93+
get () {return value [attrib];},
94+
enumerable: true,
95+
configurable: true
96+
});
97+
}
98+
};
99+
100+
// Initialize module if not yet done and return its globals
101+
export function __init__ (module) {
102+
if (!module.__inited__) {
103+
module.__all__.__init__ (module.__all__);
104+
module.__inited__ = true;
105+
}
106+
return module.__all__;
107+
};
108+
109+
// Since we want to assign functions, a = b.f should make b.f produce a bound function
110+
// So __get__ should be called by a property rather then a function
111+
// Factory __get__ creates one of three curried functions for func
112+
// Which one is produced depends on what's to the left of the dot of the corresponding JavaScript property
113+
export function __get__ (aThis, func, quotedFuncName) { // Param aThis is thing before the dot, if it's there
114+
if (aThis) {
115+
if (aThis.hasOwnProperty ('__class__') || typeof aThis == 'string' || aThis instanceof String) { // Object before the dot
116+
if (quotedFuncName) { // Memoize call since fcall is on, by installing bound function in instance
117+
Object.defineProperty (aThis, quotedFuncName, { // Will override the non-own property, next time it will be called directly
118+
value: function () { // So next time just call curry function that calls function
119+
var args = [] .slice.apply (arguments);
120+
return func.apply (null, [aThis] .concat (args));
121+
},
122+
writable: true,
123+
enumerable: true,
124+
configurable: true
125+
});
126+
}
127+
return function () { // Return bound function, code duplication for efficiency if no memoizing
128+
var args = [] .slice.apply (arguments); // So multilayer search prototype, apply __get__, call curry func that calls func
129+
130+
// Note that if aThis has a proxy, self of func should be the proxy, since another __getattr__ or __setattr__ may be done on self
131+
return func.apply (null, [aThis.__proxy__ ? aThis.__proxy__ : aThis] .concat (args)); // Prepend aThis or its proxy before other actual params
132+
133+
134+
};
135+
}
136+
else { // Class before the dot
137+
return func; // Return static method
138+
}
139+
}
140+
else { // Nothing before the dot
141+
return func; // Return free function
142+
}
143+
};
144+
145+
export function __getcm__ (aThis, func, quotedFuncName) {
146+
if (aThis.hasOwnProperty ('__class__')) {
147+
return function () {
148+
var args = [] .slice.apply (arguments);
149+
return func.apply (null, [aThis.__class__] .concat (args));
150+
};
151+
}
152+
else {
153+
return function () {
154+
var args = [] .slice.apply (arguments);
155+
return func.apply (null, [aThis] .concat (args));
156+
};
157+
}
158+
};
159+
160+
export function __getsm__ (aThis, func, quotedFuncName) {
161+
return func;
162+
};
163+
164+
// Mother of all metaclasses
165+
export var py_metatype = {
166+
__name__: 'type',
167+
__bases__: [],
168+
169+
// Overridable class creation worker
170+
__new__: function (meta, name, bases, attribs) {
171+
// Create the class cls, a functor, which the class creator function will return
172+
var cls = function () { // If cls is called with arg0, arg1, etc, it calls its __new__ method with [arg0, arg1, etc]
173+
var args = [] .slice.apply (arguments); // It has a __new__ method, not yet but at call time, since it is copied from the parent in the loop below
174+
return cls.__new__ (args); // Each Python class directly or indirectly derives from object, which has the __new__ method
175+
}; // If there are no bases in the Python source, the compiler generates [object] for this parameter
176+
177+
// Copy all methods, including __new__, properties and static attributes from base classes to new cls object
178+
// The new class object will simply be the prototype of its instances
179+
// JavaScript prototypical single inheritance will do here, since any object has only one class
180+
// This has nothing to do with Python multiple inheritance, that is implemented explictly in the copy loop below
181+
for (var index = bases.length - 1; index >= 0; index--) { // Reversed order, since class vars of first base should win
182+
var base = bases [index];
183+
for (var attrib in base) {
184+
var descrip = Object.getOwnPropertyDescriptor (base, attrib);
185+
if (descrip == null) { // Another library modified Function.prototype
186+
continue;
187+
}
188+
Object.defineProperty (cls, attrib, descrip);
189+
}
190+
for (let symbol of Object.getOwnPropertySymbols (base)) {
191+
let descrip = Object.getOwnPropertyDescriptor (base, symbol);
192+
Object.defineProperty (cls, symbol, descrip);
193+
}
194+
}
195+
196+
// Add class specific attributes to the created cls object
197+
cls.__metaclass__ = meta;
198+
cls.__name__ = name.startsWith ('py_') ? name.slice (3) : name;
199+
cls.__bases__ = bases;
200+
201+
// Add own methods, properties and own static attributes to the created cls object
202+
for (var attrib in attribs) {
203+
var descrip = Object.getOwnPropertyDescriptor (attribs, attrib);
204+
Object.defineProperty (cls, attrib, descrip);
205+
}
206+
for (let symbol of Object.getOwnPropertySymbols (attribs)) {
207+
let descrip = Object.getOwnPropertyDescriptor (attribs, symbol);
208+
Object.defineProperty (cls, symbol, descrip);
209+
}
210+
211+
// Return created cls object
212+
return cls;
213+
}
214+
};
215+
py_metatype.__metaclass__ = py_metatype;
216+
217+
// Mother of all classes
218+
export var object = {
219+
__init__: function (self) {},
220+
221+
__metaclass__: py_metatype, // By default, all classes have metaclass type, since they derive from object
222+
__name__: 'object',
223+
__bases__: [],
224+
225+
// Object creator function, is inherited by all classes (so could be global)
226+
__new__: function (args) { // Args are just the constructor args
227+
// In JavaScript the Python class is the prototype of the Python object
228+
// In this way methods and static attributes will be available both with a class and an object before the dot
229+
// The descriptor produced by __get__ will return the right method flavor
230+
var instance = Object.create (this, {__class__: {value: this, enumerable: true}});
231+
232+
if ('__getattr__' in this || '__setattr__' in this) {
233+
instance.__proxy__ = new Proxy (instance, {
234+
get: function (target, name) {
235+
let result = target [name];
236+
if (result == undefined) { // Target doesn't have attribute named name
237+
return target.__getattr__ (name);
238+
}
239+
else {
240+
return result; // Will be bound to the target.__proxy__ to allow call chaining (as in issue #587)
241+
}
242+
},
243+
set: function (target, name, value) {
244+
try {
245+
target.__setattr__ (name, value);
246+
}
247+
catch (exception) { // Target doesn't have a __setattr__ method
248+
target [name] = value;
249+
}
250+
return true;
251+
}
252+
})
253+
instance == instance.__proxy__;
254+
}
255+
256+
// Call constructor
257+
this.__init__.apply (null, [instance] .concat (args));
258+
259+
// Return constructed instance
260+
return instance;
261+
}
262+
};
263+
264+
// Class creator facade function, calls class creation worker
265+
export function __class__ (name, bases, attribs, meta) { // Parameter meta is optional
266+
if (meta === undefined) {
267+
meta = bases [0] .__metaclass__;
268+
}
269+
270+
return meta.__new__ (meta, name, bases, attribs);
271+
};
272+
273+
// Define __pragma__ to preserve '<all>' and '</all>', since it's never generated as a function, must be done early, so here
274+
export function __pragma__ () {};

transcrypt/modules/org/transcrypt/__core__.js

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,14 @@ export function __init__ (module) {
110110
// So __get__ should be called by a property rather then a function
111111
// Factory __get__ creates one of three curried functions for func
112112
// Which one is produced depends on what's to the left of the dot of the corresponding JavaScript property
113-
export function __get__ (aThis, func, quotedFuncName) { // Param aThis is thing before the dot, if it's there
113+
export function __get__ (aThis, func, quotedFuncName) {// Param aThis is thing before the dot, if it's there
114114
if (aThis) {
115115
if (aThis.hasOwnProperty ('__class__') || typeof aThis == 'string' || aThis instanceof String) { // Object before the dot
116116
if (quotedFuncName) { // Memoize call since fcall is on, by installing bound function in instance
117-
Object.defineProperty (aThis, quotedFuncName, { // Will override the non-own property, next time it will be called directly
117+
Object.defineProperty (aThis, quotedFuncName, { // Will override the non-own property, next time it will be called directly
118118
value: function () { // So next time just call curry function that calls function
119119
var args = [] .slice.apply (arguments);
120-
return func.apply (null, [aThis] .concat (args));
120+
return func.apply (null, [aThis.__proxy__ ? aThis.__proxy__ : aThis] .concat (args));
121121
},
122122
writable: true,
123123
enumerable: true,
@@ -126,11 +126,7 @@ export function __get__ (aThis, func, quotedFuncName) { // Param aThis is thing
126126
}
127127
return function () { // Return bound function, code duplication for efficiency if no memoizing
128128
var args = [] .slice.apply (arguments); // So multilayer search prototype, apply __get__, call curry func that calls func
129-
130-
// Note that if aThis has a proxy, self of func should be the proxy, since another __getattr__ or __setattr__ may be done on self
131-
return func.apply (null, [aThis.__proxy__ ? aThis.__proxy__ : aThis] .concat (args)); // Prepend aThis or its proxy before other actual params
132-
133-
129+
return func.apply (null, [aThis] .concat (args));
134130
};
135131
}
136132
else { // Class before the dot
@@ -237,7 +233,7 @@ export var object = {
237233
return target.__getattr__ (name);
238234
}
239235
else {
240-
return result; // Will be bound to the target.__proxy__ to allow call chaining (as in issue #587)
236+
return result; // Will be bound to the target.__proxy__ to allow call chaining (as in issue #587)
241237
}
242238
},
243239
set: function (target, name, value) {
@@ -250,7 +246,7 @@ export var object = {
250246
return true;
251247
}
252248
})
253-
instance == instance.__proxy__;
249+
instance = instance.__proxy__
254250
}
255251

256252
// Call constructor

0 commit comments

Comments
 (0)