Skip to content

Commit fe4c714

Browse files
author
hartsantler
committed
core refactored: methods can now use JavaScript this properly bound to the caller.
1 parent 5f58365 commit fe4c714

5 files changed

Lines changed: 141 additions & 33 deletions

File tree

bindings/physijs.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ def Physijs_initialize( worker='/libs/physijs/physijs_worker.js', ammo='/libs/am
1111

1212

1313
def PhysijsMaterial( material, friction=0.8, restitution=0.2): ## TODO should this be wrapped in its own class?
14-
print 'converting material to physijs material', material
1514
with javascript:
1615
return Physijs.createMaterial( material[...], friction, restitution )
1716

pythonjs.js

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// PythonScript Runtime - regenerated on: Thu Nov 21 18:05:10 2013
1+
// PythonScript Runtime - regenerated on: Thu Nov 21 20:42:18 2013
22
__NULL_OBJECT__ = Object.create(null);
33
if ("window" in this && "document" in this) {
44
__NODEJS__ = false;
@@ -135,7 +135,7 @@ get_attribute = function(object, attribute) {
135135
args = [args, Object()];
136136
}
137137
args[0].splice(0, 0, object);
138-
return attr.apply(undefined, args);
138+
return attr.apply(this, args);
139139
}
140140

141141
method.is_wrapper = true;
@@ -154,7 +154,7 @@ get_attribute = function(object, attribute) {
154154
args = [args, Object()];
155155
}
156156
args[0].splice(0, 0, object);
157-
return attr.apply(undefined, args);
157+
return attr.apply(this, args);
158158
}
159159

160160
method.is_wrapper = true;
@@ -182,7 +182,7 @@ get_attribute = function(object, attribute) {
182182
args = [args, Object()];
183183
}
184184
args[0].splice(0, 0, object);
185-
return attr.apply(undefined, args);
185+
return attr.apply(this, args);
186186
}
187187

188188
method.is_wrapper = true;
@@ -428,6 +428,7 @@ create_class = function(class_name, parents, attrs, props) {
428428
klass.__bases__=parents;
429429
klass.__name__=class_name;
430430
klass.__unbound_methods__=Object.create( null );
431+
klass.__all_method_names__=[];
431432
klass.__properties__=props;
432433
klass.__attributes__=attrs;
433434
var iter = attrs;
@@ -437,6 +438,7 @@ create_class = function(class_name, parents, attrs, props) {
437438
var backup = key; key = iter[key];
438439
if (typeof(attrs[key]) == "function") {
439440
klass.__unbound_methods__[ key ] = attrs[ key ];
441+
klass.__all_method_names__.push( key );
440442
}
441443
if (key == "__getattribute__") {
442444
continue;
@@ -463,31 +465,47 @@ create_class = function(class_name, parents, attrs, props) {
463465
if (! (iter instanceof Array) ) { iter = __object_keys__(iter) }
464466
for (var base=0; base < iter.length; base++) {
465467
var backup = base; base = iter[base];
466-
klass.__getters__.concat( base.__getters__ );
467-
klass.__setters__.concat( base.__setters__ );
468+
Array.prototype.push.apply( klass.__getters__,base.__getters__ );
469+
Array.prototype.push.apply( klass.__setters__,base.__setters__ );
470+
Array.prototype.push.apply( klass.__all_method_names__,base.__all_method_names__ );
468471
base = backup;
469472
}
470473
var __call__ = function() {
471-
var init, object, wrapper;
474+
var has_getattr, wrapper, object, has_getattribute;
472475
"Create a PythonJS object";
473476
object = Object.create( null );
474477
object.__class__=klass;
475478
Object.defineProperty( object,"__dict__",{ enumerable:false,value:object,writeable:false,configurable:false } );
476-
var iter = klass.__unbound_methods__;
479+
has_getattribute = false;
480+
has_getattr = false;
481+
var iter = klass.__all_method_names__;
477482

478483
if (! (iter instanceof Array) ) { iter = __object_keys__(iter) }
479484
for (var name=0; name < iter.length; name++) {
480485
var backup = name; name = iter[name];
481-
wrapper = get_attribute( object,name );
482-
if (!wrapper.is_wrapper) {
483-
console.log("ERROR: failed to get wrapper for:", name);
486+
if (name == "__getattribute__") {
487+
has_getattribute = true;
488+
} else {
489+
if (name == "__getattr__") {
490+
has_getattr = true;
491+
} else {
492+
wrapper = get_attribute( object,name );
493+
if (!wrapper.is_wrapper) {
494+
console.log("RUNTIME ERROR: failed to get wrapper for:", name);
495+
}
496+
}
484497
}
485498
name = backup;
486499
}
500+
if (has_getattr) {
501+
get_attribute( object,"__getattr__" );
502+
}
503+
if (has_getattribute) {
504+
get_attribute( object,"__getattribute__" );
505+
}
487506
__bind_property_descriptors__( object,klass );
488-
init = object.__init__;
489-
if (init) {
490-
init.apply( undefined,arguments );
507+
if (object.__init__) {
508+
object.__init__.apply( this,arguments );
491509
}
492510
return object;
493511
}

runtime/builtins.py

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,13 @@ def create_class(class_name, parents, attrs, props):
5555
klass.__name__ = class_name
5656
#klass.__dict__ = attrs
5757
klass.__unbound_methods__ = Object.create(null)
58+
klass.__all_method_names__ = []
5859
klass.__properties__ = props
5960
klass.__attributes__ = attrs
6061
for key in attrs:
6162
if typeof( attrs[key] ) == 'function':
6263
klass.__unbound_methods__[key] = attrs[key]
64+
klass.__all_method_names__.push( key )
6365

6466
if key == '__getattribute__': continue
6567
klass[key] = attrs[key]
@@ -73,8 +75,9 @@ def create_class(class_name, parents, attrs, props):
7375
if prop['set']:
7476
klass.__setters__.push( name )
7577
for base in klass.__bases__:
76-
klass.__getters__.concat( base.__getters__ )
77-
klass.__setters__.concat( base.__setters__ )
78+
Array.prototype.push.apply( klass.__getters__, base.__getters__ )
79+
Array.prototype.push.apply( klass.__setters__, base.__setters__ )
80+
Array.prototype.push.apply( klass.__all_method_names__, base.__all_method_names__ )
7881

7982

8083
def __call__():
@@ -89,21 +92,32 @@ def __call__():
8992
)
9093

9194

92-
## pre-cache all methods on object so they are callable from JavaScript ##
93-
#for name in klass.__attributes__:
94-
# if typeof( klass.__attributes__[name] ) == 'function':
95-
# get_attribute( object, name )
96-
for name in klass.__unbound_methods__:
97-
wrapper = get_attribute(object, name)
98-
if not wrapper.is_wrapper:
99-
print 'ERROR: failed to get wrapper for:',name
95+
has_getattribute = False
96+
has_getattr = False
97+
for name in klass.__all_method_names__:
98+
if name == '__getattribute__':
99+
has_getattribute = True
100+
elif name == '__getattr__':
101+
has_getattr = True
102+
else:
103+
wrapper = get_attribute(object, name)
104+
if not wrapper.is_wrapper:
105+
print 'RUNTIME ERROR: failed to get wrapper for:',name
106+
107+
## to be safe the getters come after other methods are cached ##
108+
if has_getattr:
109+
get_attribute(object, '__getattr__')
110+
111+
if has_getattribute:
112+
get_attribute(object, '__getattribute__')
100113

101114
__bind_property_descriptors__(object, klass)
102115

103-
init = object.__init__
104-
if init:
105-
init.apply(None, arguments)
116+
if object.__init__:
117+
object.__init__.apply(this, arguments)
118+
106119
return object
120+
107121
__call__.pythonscript_function = True
108122
klass.__call__ = __call__
109123
return klass

runtime/pythonpythonjs.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ def get_attribute(object, attribute):
7070
JS("var cached = object.cached_wrapper")
7171
if cached:
7272
return cached
73-
else:
74-
def wrapper(args,kwargs): return object.apply(None, args)
73+
else: ## TODO - double check if this still happens
74+
def wrapper(args,kwargs): return object.apply(None, args) ## TODO, bind this?
7575
wrapper.is_wrapper = True
7676
object.cached_wrapper = wrapper
7777
return wrapper
@@ -144,7 +144,8 @@ def method():
144144
else:
145145
args = [args, JSObject()]
146146
args[0].splice(0, 0, object)
147-
return attr.apply(None, args) ## should we bind `this` here so callback can use this?
147+
return attr.apply(this, args) ## this is bound so that callback methods can use `this` from the caller
148+
148149
method.is_wrapper = True
149150
object[attribute] = method ## cache method - we assume that methods do not change
150151
return method
@@ -164,7 +165,7 @@ def method():
164165
# put the arguments in order to be processed by PythonJS
165166
args = [args, JSObject()]
166167
args[0].splice(0, 0, object)
167-
return attr.apply(None, args) ## should we bind `this` here so callback can use this?
168+
return attr.apply(this, args)
168169
method.is_wrapper = True
169170

170171
object[attribute] = method ## cache method - we assume that methods do not change
@@ -190,7 +191,7 @@ def method():
190191
args = [args, JSObject()]
191192

192193
args[0].splice(0, 0, object)
193-
return attr.apply(None, args)
194+
return attr.apply(this, args)
194195
method.is_wrapper = True
195196

196197
object[attribute] = method ## cache method - we assume that methods do not change

tests/test_this.html

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<html>
2+
<head>
3+
<script src="pythonscript.js"></script>
4+
5+
<script type="text/javascript">
6+
7+
function MyClass( callback ) {
8+
this.callback = callback;
9+
this.foo = 'hello';
10+
this.bar = 'world';
11+
this.call_callback = function() {
12+
return this.callback();
13+
}
14+
}
15+
16+
</script>
17+
18+
<script type="text/python">
19+
20+
21+
def func():
22+
print 'in callback', this
23+
print this.foo
24+
print this.bar
25+
26+
def func2(xxx='mydefault'):
27+
print xxx
28+
print 'in callback', this
29+
print this.foo
30+
print this.bar
31+
32+
33+
class A:
34+
def method(self):
35+
print self
36+
print this
37+
print this.foo, this.bar
38+
39+
def test():
40+
global a
41+
with javascript:
42+
ob = new( MyClass(func) )
43+
print 'testing calling - with javascript'
44+
ob.call_callback()
45+
46+
print 'testing normal call'
47+
ob.call_callback()
48+
49+
50+
with javascript:
51+
c = new( MyClass(func2) )
52+
print 'testing calling - with javascript'
53+
c.call_callback()
54+
55+
print 'testing normal call'
56+
c.call_callback()
57+
58+
59+
a = A()
60+
with javascript:
61+
ob2 = new( MyClass(a.method) )
62+
print 'testing method call - with javascript'
63+
ob2.call_callback()
64+
65+
print 'testing normal method call'
66+
ob2.call_callback()
67+
68+
69+
70+
</script>
71+
72+
</head>
73+
<body>
74+
<button onclick="test()">click me</button>
75+
</body>
76+
</html>

0 commit comments

Comments
 (0)