Skip to content

Commit 0c40d75

Browse files
author
hartsantler
committed
abusing @decorator syntax in "with javascript:" for functions that hijack object prototypes and use the special "this" keyword.
1 parent e24c7e9 commit 0c40d75

6 files changed

Lines changed: 43 additions & 28 deletions

File tree

pythonscript.js

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// PythonScript Runtime - regenerated on: Sun Oct 13 21:03:22 2013
1+
// PythonScript Runtime - regenerated on: Mon Oct 14 00:26:47 2013
22
var jsrange = function(num) {
33
"Emulates Python's range function";
44
var i, r;
@@ -33,7 +33,6 @@ var adapt_arguments = function(handler) {
3333
var func = function() {
3434
handler(Array.prototype.slice.call(arguments));
3535
}
36-
window["func"] = func
3736

3837
return func;
3938
}
@@ -67,7 +66,6 @@ init.apply(undefined, arguments);
6766

6867
return object;
6968
}
70-
window["__call__"] = __call__
7169

7270
__call__.pythonscript_function = true;
7371
klass.__call__ = __call__;
@@ -95,7 +93,6 @@ else {
9593
var wrapper = function(args, kwargs) {
9694
return object.apply(undefined, args);
9795
}
98-
window["wrapper"] = wrapper
9996

10097
wrapper.is_wrapper = true;
10198
object.cached_wrapper = wrapper;
@@ -117,7 +114,6 @@ if(typeof(attr) === 'function') {
117114
var wrapper = function(args, kwargs) {
118115
return attr.apply(object, args);
119116
}
120-
window["wrapper"] = wrapper
121117

122118
wrapper.is_wrapper = true;
123119
return wrapper;
@@ -133,7 +129,6 @@ if(typeof(attr) === 'function') {
133129
var wrapper = function(args, kwargs) {
134130
return attr.apply(object, args);
135131
}
136-
window["wrapper"] = wrapper
137132

138133
wrapper.is_wrapper = true;
139134
return wrapper;
@@ -151,7 +146,6 @@ if(typeof(attr) === 'function' && attr.pythonscript_function === undefined && at
151146
var wrapper = function(args, kwargs) {
152147
return attr.apply(object, args);
153148
}
154-
window["wrapper"] = wrapper
155149

156150
wrapper.is_wrapper = true;
157151
return wrapper;
@@ -253,7 +247,6 @@ args = [object];
253247

254248
return attr.apply(undefined, args);
255249
}
256-
window["method"] = method
257250

258251
method.is_wrapper = true;
259252
return method;
@@ -286,7 +279,6 @@ args = [object];
286279

287280
return attr.apply(undefined, args);
288281
}
289-
window["method"] = method
290282

291283
method.is_wrapper = true;
292284
return method;
@@ -307,7 +299,6 @@ if(attribute == "__getitem__") {
307299
var wrapper = function(args, kwargs) {
308300
return object[args[0]];
309301
}
310-
window["wrapper"] = wrapper
311302

312303
wrapper.is_wrapper = true;
313304
return wrapper;
@@ -317,7 +308,6 @@ if(attribute == "__setitem__") {
317308
var wrapper = function(args, kwargs) {
318309
object[args[0]] = args[1];
319310
}
320-
window["wrapper"] = wrapper
321311

322312
wrapper.is_wrapper = true;
323313
return wrapper;
@@ -331,7 +321,6 @@ if(attribute == "__getitem__") {
331321
var wrapper = function(args, kwargs) {
332322
return object[args[0]];
333323
}
334-
window["wrapper"] = wrapper
335324

336325
wrapper.is_wrapper = true;
337326
return wrapper;
@@ -341,7 +330,6 @@ if(attribute == "__setitem__") {
341330
var wrapper = function(args, kwargs) {
342331
object[args[0]] = args[1];
343332
}
344-
window["wrapper"] = wrapper
345333

346334
wrapper.is_wrapper = true;
347335
return wrapper;
@@ -601,10 +589,8 @@ return false;
601589
}
602590

603591
}
604-
window["func"] = func
605592

606-
func.pythonscript_function=true;
607-
set_attribute(String.prototype, "startswith", func);
593+
String.prototype.startswith=func;
608594
var func = function(a) {
609595
if(this.substring(this.length - a.length, this.length) == a) {
610596
return true;
@@ -614,10 +600,8 @@ return false;
614600
}
615601

616602
}
617-
window["func"] = func
618603

619-
func.pythonscript_function=true;
620-
set_attribute(String.prototype, "endswith", func);
604+
String.prototype.endswith=func;
621605
}
622606
window["_setup_str_prototype"] = _setup_str_prototype
623607

pythonscript/python_to_pythonjs.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -760,10 +760,13 @@ def visit_Call(self, node):
760760
def visit_FunctionDef(self, node):
761761
property_decorator = None
762762
decorators = []
763+
with_js_decorators = []
763764
for decorator in reversed(node.decorator_list):
764765
log('@decorator: %s' %decorator)
766+
if self._with_js: ## decorators are special in with-js mode
767+
with_js_decorators.append( self.visit( decorator ) )
765768

766-
if isinstance(decorator, Name) and decorator.id == 'property':
769+
elif isinstance(decorator, Name) and decorator.id == 'property':
767770
property_decorator = decorator
768771
n = node.name + '__getprop__'
769772
self._decorator_properties[ node.original_name ] = dict( get=n, set=None )
@@ -878,12 +881,22 @@ def visit_FunctionDef(self, node):
878881
self._function_return_types[ node.name ] = self._return_type
879882

880883
writer.pull()
881-
if self._with_js:
884+
if self._with_js and with_js_decorators:
885+
## these with-js functions are assigned to a some objects prototype,
886+
## here we assume that they depend on the special "this" variable,
887+
## therefore this function can not be marked as f.pythonscript_function,
888+
## because we need get_attribute(f,'__call__') to dynamically bind "this"
889+
for dec in with_js_decorators:
890+
assert '.prototype.' in dec
891+
writer.write( '%s=%s'%(dec,node.name) )
892+
elif self._with_js: ## this is just an optimization so we can avoid making wrappers at runtime
882893
writer.write('%s.pythonscript_function=true'%node.name)
883894
else:
884895
writer.write('%s.pythonscript_function=True'%node.name)
896+
885897
# apply decorators
886898
for decorator in decorators:
899+
assert not self._with_js
887900
writer.write('%s = %s(create_array(%s))' % (node.name, self.visit(decorator), node.name))
888901

889902
def visit_For(self, node):

pythonscript/pythonjs.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ def visit_ExceptHandler(self, node):
6060
return out
6161

6262
def visit_FunctionDef(self, node):
63+
if not hasattr(self, '_function_stack'): ## track nested functions ##
64+
self._function_stack = []
65+
66+
self._function_stack.append( node.name )
67+
6368
args = self.visit(node.args)
6469
buffer = 'var %s = function(%s) {\n' % (
6570
node.name,
@@ -81,7 +86,11 @@ def visit_FunctionDef(self, node):
8186
body.append(self.visit(child))
8287
buffer += '\n'.join(body)
8388
buffer += '\n}\n'
84-
buffer += 'window["%s"] = %s \n' % (node.name, node.name) ## export to global namespace so Closure will not remove them
89+
90+
if node.name == self._function_stack[0]: ## to be safe do not export nested functions
91+
buffer += 'window["%s"] = %s \n' % (node.name, node.name) ## export to global namespace so Closure will not remove them
92+
93+
assert node.name == self._function_stack.pop()
8594
return buffer
8695

8796
def visit_Subscript(self, node):

runtime/builtins.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,21 @@
55

66
def _setup_str_prototype():
77
with javascript:
8+
9+
@String.prototype.startswith
810
def func(a):
911
if this.substring(0, a.length) == a:
1012
return True
1113
else:
1214
return False
13-
String.prototype.startswith = func
15+
#String.prototype.startswith = func ## the problem with this is "this" gets lost when get_attribute(f,'__call__') is used.
1416

17+
@String.prototype.endswith
1518
def func(a):
1619
if this.substring(this.length-a.length, this.length) == a:
1720
return True
1821
else:
1922
return False
20-
String.prototype.endswith = func
2123

2224
_setup_str_prototype()
2325

tests/test_string.html

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,19 @@
1010
a = 'hello'
1111
b = 'world'
1212
print a+b
13+
1314
with javascript:
1415
if a.startswith('h'):
15-
print 'startswith test passed'
16+
print 'WITH - startswith test passed'
17+
18+
if a.endswith('lo'):
19+
print 'WITH - endswith test passed'
20+
21+
if a.startswith('he'):
22+
print 'startswith test passed'
1623

17-
if a.endswith('h'):
18-
print 'endswith test passed'
24+
if a.endswith('llo'):
25+
print 'endswith test passed'
1926

2027

2128
</script>

tests/threejs_helloworld.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
div.appendChild( ren.domElement )
2727

2828
geo = CubeGeometry( 10, 10, 10 )
29-
mat = MeshBasicMaterial( color={'red':0.9, 'green':0.1, 'blue':0.5}, wireframe=False )
29+
mat = MeshBasicMaterial( color={'red':0.0, 'green':0.9, 'blue':0.1}, wireframe=False )
3030
mesh = Mesh( geo, mat )
3131
scn.add( mesh )
3232

0 commit comments

Comments
 (0)