Skip to content

Commit 026fe9d

Browse files
author
hartsantler
committed
GPU class: @gpu.object class decorator allows its methods decorated with @gpu.method to be translated with the GLSL backend.
In the @gpu.main function an list of MyObject can be iterated over and methods called.
1 parent 1ece051 commit 026fe9d

7 files changed

Lines changed: 228 additions & 91 deletions

File tree

pythonjs/ast_utils.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,13 @@ def retrieve_vars(body):
6464
pass ## skips assignment to an attribute `a.x = y`
6565

6666
if user_typedef: ## `int x`
67-
if not isinstance(n.value, ast.Name):
67+
if isinstance(n.value, ast.Name):
68+
local_vars.add( '%s=%s' %(user_typedef, n.value.id))
69+
elif isinstance(n.value, ast.Num):
70+
local_vars.add( '%s=%s' %(user_typedef, n.value.n))
71+
else:
6872
raise SyntaxError(n.value)
69-
local_vars.add( '%s=%s' %(user_typedef, n.value.id))
73+
7074

7175
elif isinstance(n, ast.Global):
7276
global_vars.update( n.names )

pythonjs/python_to_pythonjs.py

Lines changed: 79 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -807,22 +807,43 @@ def _visit_dart_classdef(self, node):
807807

808808
writer.pull()
809809

810+
def is_gpu_method(self, n):
811+
for dec in n.decorator_list:
812+
if isinstance(dec, Attribute) and isinstance(dec.value, Name) and dec.value.id == 'gpu':
813+
if dec.attr == 'method':
814+
return True
815+
816+
810817
def _visit_js_classdef(self, node):
811818
name = node.name
812-
log('JavaScript-ClassDef: %s'%name)
813819
self._js_classes[ name ] = node
814820
self._in_js_class = True
821+
class_decorators = []
822+
gpu_object = False
823+
824+
for decorator in node.decorator_list: ## class decorators
825+
if isinstance(decorator, Attribute) and isinstance(decorator.value, Name) and decorator.value.id == 'gpu':
826+
if decorator.attr == 'object':
827+
gpu_object = True
828+
else:
829+
raise SyntaxError( self.format_error('invalid gpu class decorator') )
830+
else:
831+
class_decorators.append( decorator )
815832

816833
methods = {}
817834
class_vars = []
835+
818836
for item in node.body:
819837
if isinstance(item, FunctionDef):
820838
methods[ item.name ] = item
821-
item.args.args = item.args.args[1:] ## remove self
822-
finfo = inspect_function( item )
823-
for n in finfo['name_nodes']:
824-
if n.id == 'self':
825-
n.id = 'this'
839+
if self.is_gpu_method( item ):
840+
item.args.args[0].id = name ## change self to the class name
841+
else:
842+
item.args.args = item.args.args[1:] ## remove self
843+
finfo = inspect_function( item )
844+
for n in finfo['name_nodes']:
845+
if n.id == 'self':
846+
n.id = 'this'
826847
elif isinstance(item, ast.Expr) and isinstance(item.value, Str): ## skip doc strings
827848
pass
828849
else:
@@ -845,17 +866,22 @@ def _visit_js_classdef(self, node):
845866
writer.write('def %s(%s):' %(name,','.join(args)))
846867
writer.push()
847868
if init:
869+
tail = ''
870+
if gpu_object:
871+
tail = 'this.__struct_name__="%s"' %name
872+
873+
848874
#for b in init.body:
849875
# line = self.visit(b)
850876
# if line: writer.write( line )
851877

852878
if hasattr(init, '_code'): ## cached ##
853879
code = init._code
854880
elif args:
855-
code = '%s.__init__(this, %s)'%(name, ','.join(args))
881+
code = '%s.__init__(this, %s); %s'%(name, ','.join(args), tail)
856882
init._code = code
857883
else:
858-
code = '%s.__init__(this)'%name
884+
code = '%s.__init__(this); %s'%(name, tail)
859885
init._code = code
860886

861887
writer.write(code)
@@ -879,12 +905,25 @@ def _visit_js_classdef(self, node):
879905
keys.sort()
880906
for mname in keys:
881907
method = methods[mname]
882-
writer.write('@%s.prototype'%name)
883-
line = self.visit(method)
884-
if line: writer.write( line )
885-
#writer.write('%s.prototype.%s = %s'%(name,mname,mname))
886-
f = 'function () { return %s.prototype.%s.apply(arguments[0], Array.prototype.slice.call(arguments,1)) }' %(name, mname)
887-
writer.write('%s.%s = JS("%s")'%(name,mname,f))
908+
gpu_method = False
909+
for dec in method.decorator_list:
910+
if isinstance(dec, Attribute) and isinstance(dec.value, Name) and dec.value.id == 'gpu':
911+
if dec.attr == 'method':
912+
gpu_method = True
913+
914+
if gpu_method:
915+
method.name = '%s_%s' %(name, method.name)
916+
line = self.visit(method)
917+
if line: writer.write( line )
918+
919+
else:
920+
921+
writer.write('@%s.prototype'%name)
922+
line = self.visit(method)
923+
if line: writer.write( line )
924+
#writer.write('%s.prototype.%s = %s'%(name,mname,mname))
925+
f = 'function () { return %s.prototype.%s.apply(arguments[0], Array.prototype.slice.call(arguments,1)) }' %(name, mname)
926+
writer.write('%s.%s = JS("%s")'%(name,mname,f))
888927

889928
for base in node.bases:
890929
base = self.visit(base)
@@ -906,6 +945,9 @@ def _visit_js_classdef(self, node):
906945
self.visit(item) # this will output the code for the assign
907946
writer.write('%s.prototype.%s = %s' % (name, item_name, item.targets[0].id))
908947

948+
if gpu_object:
949+
## TODO check class variables ##
950+
writer.write('%s.prototype.__struct_name__ = "%s"' %(name,name))
909951

910952
## TODO support property decorators in javascript-mode ##
911953
writer.write('%s.prototype.__properties__ = {}' %name)
@@ -923,7 +965,6 @@ def visit_ClassDef(self, node):
923965
return
924966

925967
name = node.name
926-
log('ClassDef: %s'%name)
927968
self._in_class = name
928969
self._classes[ name ] = list() ## method names
929970
self._class_parents[ name ] = set()
@@ -933,19 +974,17 @@ def visit_ClassDef(self, node):
933974
self._decorator_class_props[ name ] = self._decorator_properties
934975
self._instances[ 'self' ] = name
935976

977+
self._injector = [] ## DEPRECATED
936978
class_decorators = []
937-
self._injector = []
979+
gpu_object = False
980+
938981
for decorator in node.decorator_list: ## class decorators
939-
if isinstance(decorator, Attribute) and isinstance(decorator.value, Name) and decorator.value.id == 'pythonjs':
940-
if decorator.attr == 'property_callbacks':
941-
self._injector.append('set')
942-
elif decorator.attr == 'init_callbacks':
943-
self._injector.append('init')
982+
if isinstance(decorator, Attribute) and isinstance(decorator.value, Name) and decorator.value.id == 'gpu':
983+
if decorator.attr == 'object':
984+
gpu_object = True
944985
else:
945-
raise SyntaxError( 'unsupported pythonjs class decorator' )
946-
986+
raise SyntaxError( self.format_error('invalid gpu class decorator') )
947987
else:
948-
#raise SyntaxError( 'unsupported class decorator' )
949988
class_decorators.append( decorator )
950989

951990
## always catch attributes ##
@@ -1025,9 +1064,11 @@ def visit_ClassDef(self, node):
10251064
self._in_class = False
10261065

10271066
writer.write('%s = __create_class__("%s", __%s_parents, __%s_attrs, __%s_properties)' % (name, name, name, name, name))
1028-
if 'init' in self._injector:
1029-
writer.write('%s.init_callbacks = JSArray()' %name)
1030-
self._injector = []
1067+
1068+
## DEPRECATED
1069+
#if 'init' in self._injector:
1070+
# writer.write('%s.init_callbacks = JSArray()' %name)
1071+
#self._injector = []
10311072

10321073
for dec in class_decorators:
10331074
writer.write('%s = __get__(%s,"__call__")( [%s], JSObject() )' % (name, self.visit(dec), name))
@@ -2433,6 +2474,7 @@ def visit_FunctionDef(self, node):
24332474
gpu = False
24342475
gpu_main = False
24352476
gpu_vectorize = False
2477+
gpu_method = False
24362478
local_typedefs = []
24372479

24382480
## deprecated?
@@ -2487,8 +2529,8 @@ def visit_FunctionDef(self, node):
24872529
gpu_vectorize = True
24882530
elif decorator.attr == 'main':
24892531
gpu_main = True
2490-
elif decorator.attr == 'typedef':
2491-
pass
2532+
elif decorator.attr == 'method':
2533+
gpu_method = True
24922534
else:
24932535
raise NotImplementedError(decorator)
24942536

@@ -2592,12 +2634,19 @@ def visit_FunctionDef(self, node):
25922634

25932635
if gpu_vectorize:
25942636
writer.write('@gpu.vectorize')
2637+
if gpu_method:
2638+
writer.write('@gpu.method')
2639+
25952640
## force python variable scope, and pass user type information to second stage of translation.
25962641
## the dart backend can use this extra type information.
25972642
vars = []
25982643
local_typedef_names = set()
25992644
if not self._with_coffee:
2600-
local_vars, global_vars = retrieve_vars(node.body)
2645+
try:
2646+
local_vars, global_vars = retrieve_vars(node.body)
2647+
except SyntaxError as err:
2648+
raise SyntaxError( self.format_error(err) )
2649+
26012650
local_vars = local_vars-global_vars
26022651
if local_vars:
26032652
args_typedefs = []

0 commit comments

Comments
 (0)