2017-04-26 21:56:40 +00:00
#!/usr/bin/env python2
2018-07-18 09:40:26 +00:00
# Copyright 2017 The Dawn Authors
2017-04-20 18:38:20 +00:00
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
2019-06-07 08:59:17 +00:00
import json , os , sys
2017-04-20 18:38:20 +00:00
from collections import namedtuple
2019-06-07 08:59:17 +00:00
2019-01-15 20:49:53 +00:00
import common
2019-06-07 08:59:17 +00:00
from common import Name
from generator_lib import Generator , run_generator , FileRender
2019-01-15 20:49:53 +00:00
import wire_cmd
2018-05-17 20:55:53 +00:00
2017-04-20 18:38:20 +00:00
############################################################
# PARSE
############################################################
2017-04-20 18:42:36 +00:00
def is_native_method ( method ) :
return method . return_type . category == " natively defined " or \
any ( [ arg . type . category == " natively defined " for arg in method . arguments ] )
2018-12-05 17:49:04 +00:00
def link_object ( obj , types ) :
def make_method ( json_data ) :
2019-01-15 20:49:53 +00:00
arguments = common . linked_record_members ( json_data . get ( ' args ' , [ ] ) , types )
return common . Method ( Name ( json_data [ ' name ' ] ) , types [ json_data . get ( ' returns ' , ' void ' ) ] , arguments )
2018-12-05 17:49:04 +00:00
methods = [ make_method ( m ) for m in obj . json_data . get ( ' methods ' , [ ] ) ]
2017-04-20 18:42:36 +00:00
obj . methods = [ method for method in methods if not is_native_method ( method ) ]
obj . native_methods = [ method for method in methods if is_native_method ( method ) ]
2017-04-20 18:38:20 +00:00
2018-05-17 20:55:53 +00:00
def link_structure ( struct , types ) :
2019-01-15 20:49:53 +00:00
struct . members = common . linked_record_members ( struct . json_data [ ' members ' ] , types )
2018-05-17 20:55:53 +00:00
2018-09-18 12:49:22 +00:00
# Sort structures so that if struct A has struct B as a member, then B is listed before A
# This is a form of topological sort where we try to keep the order reasonably similar to the
# original order (though th sort isn't technically stable).
# It works by computing for each struct type what is the depth of its DAG of dependents, then
# resorting based on that depth using Python's stable sort. This makes a toposort because if
# A depends on B then its depth will be bigger than B's. It is also nice because all nodes
# with the same depth are kept in the input order.
def topo_sort_structure ( structs ) :
for struct in structs :
struct . visited = False
struct . subdag_depth = 0
def compute_depth ( struct ) :
if struct . visited :
return struct . subdag_depth
max_dependent_depth = 0
for member in struct . members :
2018-12-05 17:49:04 +00:00
if member . type . category == ' structure ' :
2018-09-18 12:49:22 +00:00
max_dependent_depth = max ( max_dependent_depth , compute_depth ( member . type ) + 1 )
struct . subdag_depth = max_dependent_depth
struct . visited = True
return struct . subdag_depth
for struct in structs :
compute_depth ( struct )
result = sorted ( structs , key = lambda struct : struct . subdag_depth )
for struct in structs :
del struct . visited
del struct . subdag_depth
return result
2017-04-20 18:38:20 +00:00
def parse_json ( json ) :
category_to_parser = {
2019-01-15 20:49:53 +00:00
' bitmask ' : common . BitmaskType ,
' enum ' : common . EnumType ,
' native ' : common . NativeType ,
' natively defined ' : common . NativelyDefined ,
' object ' : common . ObjectType ,
' structure ' : common . StructureType ,
2017-04-20 18:38:20 +00:00
}
types = { }
by_category = { }
for name in category_to_parser . keys ( ) :
by_category [ name ] = [ ]
2018-12-05 17:49:04 +00:00
for ( name , json_data ) in json . items ( ) :
2017-04-20 18:38:20 +00:00
if name [ 0 ] == ' _ ' :
continue
2018-12-05 17:49:04 +00:00
category = json_data [ ' category ' ]
parsed = category_to_parser [ category ] ( name , json_data )
2017-04-20 18:38:20 +00:00
by_category [ category ] . append ( parsed )
types [ name ] = parsed
for obj in by_category [ ' object ' ] :
link_object ( obj , types )
2018-05-17 20:55:53 +00:00
for struct in by_category [ ' structure ' ] :
link_structure ( struct , types )
2017-04-20 18:38:20 +00:00
for category in by_category . keys ( ) :
by_category [ category ] = sorted ( by_category [ category ] , key = lambda typ : typ . name . canonical_case ( ) )
2018-09-18 12:49:22 +00:00
by_category [ ' structure ' ] = topo_sort_structure ( by_category [ ' structure ' ] )
2019-01-15 20:49:53 +00:00
for struct in by_category [ ' structure ' ] :
struct . update_metadata ( )
2017-04-20 18:38:20 +00:00
return {
' types ' : types ,
' by_category ' : by_category
}
#############################################################
2019-06-07 08:59:17 +00:00
# Generator
2017-04-20 18:38:20 +00:00
#############################################################
def as_varName ( * names ) :
return names [ 0 ] . camelCase ( ) + ' ' . join ( [ name . CamelCase ( ) for name in names [ 1 : ] ] )
def as_cType ( name ) :
if name . native :
return name . concatcase ( )
else :
2019-03-11 16:52:42 +00:00
return ' Dawn ' + name . CamelCase ( )
2017-04-20 18:38:20 +00:00
def as_cppType ( name ) :
if name . native :
return name . concatcase ( )
else :
return name . CamelCase ( )
2019-05-15 18:55:22 +00:00
def convert_cType_to_cppType ( typ , annotation , arg , indent = 0 ) :
if typ . category == ' native ' :
return arg
if annotation == ' value ' :
if typ . category == ' object ' :
return ' {} ::Acquire( {} ) ' . format ( as_cppType ( typ . name ) , arg )
elif typ . category == ' structure ' :
converted_members = [
convert_cType_to_cppType (
member . type , member . annotation ,
' {} . {} ' . format ( arg , as_varName ( member . name ) ) ,
indent + 1 )
for member in typ . members ]
converted_members = [ ( ' ' * 4 ) + m for m in converted_members ]
converted_members = ' , \n ' . join ( converted_members )
return as_cppType ( typ . name ) + ' { \n ' + converted_members + ' \n } '
else :
return ' static_cast< {} >( {} ) ' . format ( as_cppType ( typ . name ) , arg )
else :
return ' reinterpret_cast< {} {} >( {} ) ' . format ( as_cppType ( typ . name ) , annotation , arg )
2017-04-20 18:38:20 +00:00
def decorate ( name , typ , arg ) :
if arg . annotation == ' value ' :
return typ + ' ' + name
2019-05-15 18:55:22 +00:00
elif arg . annotation == ' * ' :
return typ + ' * ' + name
2017-04-20 18:38:20 +00:00
elif arg . annotation == ' const* ' :
return typ + ' const * ' + name
2019-01-21 08:29:01 +00:00
elif arg . annotation == ' const*const* ' :
return ' const ' + typ + ' * const * ' + name
2017-04-20 18:38:20 +00:00
else :
assert ( False )
def annotated ( typ , arg ) :
name = as_varName ( arg . name )
return decorate ( name , typ , arg )
def as_cEnum ( type_name , value_name ) :
assert ( not type_name . native and not value_name . native )
2018-07-18 13:12:52 +00:00
return ' DAWN ' + ' _ ' + type_name . SNAKE_CASE ( ) + ' _ ' + value_name . SNAKE_CASE ( )
2017-04-20 18:38:20 +00:00
def as_cppEnum ( value_name ) :
assert ( not value_name . native )
if value_name . concatcase ( ) [ 0 ] . isdigit ( ) :
return " e " + value_name . CamelCase ( )
return value_name . CamelCase ( )
def as_cMethod ( type_name , method_name ) :
assert ( not type_name . native and not method_name . native )
2018-07-18 13:12:52 +00:00
return ' dawn ' + type_name . CamelCase ( ) + method_name . CamelCase ( )
2017-04-20 18:38:20 +00:00
def as_MethodSuffix ( type_name , method_name ) :
assert ( not type_name . native and not method_name . native )
return type_name . CamelCase ( ) + method_name . CamelCase ( )
def as_cProc ( type_name , method_name ) :
assert ( not type_name . native and not method_name . native )
2019-03-11 16:52:42 +00:00
return ' Dawn ' + ' Proc ' + type_name . CamelCase ( ) + method_name . CamelCase ( )
2017-04-20 18:38:20 +00:00
2018-08-01 13:12:10 +00:00
def as_frontendType ( typ ) :
2017-04-20 18:38:20 +00:00
if typ . category == ' object ' :
2019-04-01 21:48:38 +00:00
return typ . name . CamelCase ( ) + ' Base* '
2018-08-01 13:12:10 +00:00
elif typ . category in [ ' bitmask ' , ' enum ' ] :
return ' dawn:: ' + typ . name . CamelCase ( )
elif typ . category == ' structure ' :
return as_cppType ( typ . name )
2017-04-20 18:38:20 +00:00
else :
return as_cType ( typ . name )
2017-05-08 13:17:44 +00:00
def cpp_native_methods ( types , typ ) :
2019-04-01 21:48:38 +00:00
return typ . methods + typ . native_methods
2017-05-08 13:17:44 +00:00
2017-04-20 18:42:36 +00:00
def c_native_methods ( types , typ ) :
2017-05-08 13:17:44 +00:00
return cpp_native_methods ( types , typ ) + [
2019-01-15 20:49:53 +00:00
common . Method ( Name ( ' reference ' ) , types [ ' void ' ] , [ ] ) ,
common . Method ( Name ( ' release ' ) , types [ ' void ' ] , [ ] ) ,
2017-04-20 18:42:36 +00:00
]
2017-10-25 19:39:00 +00:00
def js_native_methods ( types , typ ) :
return cpp_native_methods ( types , typ )
2017-04-20 18:38:20 +00:00
def debug ( text ) :
print ( text )
2019-02-11 19:39:46 +00:00
def do_assert ( expr ) :
assert expr
return ' '
2019-06-07 08:59:17 +00:00
class MultiGeneratorFromDawnJSON ( Generator ) :
def get_description ( self ) :
return ' Generates code for various target from Dawn.json. '
def add_commandline_arguments ( self , parser ) :
allowed_targets = [ ' dawn_headers ' , ' libdawn ' , ' mock_dawn ' , ' dawn_wire ' , " dawn_native_utils " ]
parser . add_argument ( ' --dawn-json ' , required = True , type = str , help = ' The DAWN JSON definition to use. ' )
parser . add_argument ( ' --wire-json ' , default = None , type = str , help = ' The DAWN WIRE JSON definition to use. ' )
parser . add_argument ( ' -T ' , ' --targets ' , required = True , type = str , help = ' Comma-separated subset of targets to output. Available targets: ' + ' , ' . join ( allowed_targets ) )
def get_file_renders ( self , args ) :
with open ( args . dawn_json ) as f :
loaded_json = json . loads ( f . read ( ) )
api_params = parse_json ( loaded_json )
targets = args . targets . split ( ' , ' )
wire_json = None
if args . wire_json :
with open ( args . wire_json ) as f :
wire_json = json . loads ( f . read ( ) )
base_params = {
' enumerate ' : enumerate ,
' format ' : format ,
' len ' : len ,
' debug ' : debug ,
' assert ' : do_assert ,
' Name ' : lambda name : Name ( name ) ,
' as_annotated_cType ' : lambda arg : annotated ( as_cType ( arg . type . name ) , arg ) ,
' as_annotated_cppType ' : lambda arg : annotated ( as_cppType ( arg . type . name ) , arg ) ,
' as_cEnum ' : as_cEnum ,
' as_cppEnum ' : as_cppEnum ,
' as_cMethod ' : as_cMethod ,
' as_MethodSuffix ' : as_MethodSuffix ,
' as_cProc ' : as_cProc ,
' as_cType ' : as_cType ,
' as_cppType ' : as_cppType ,
' convert_cType_to_cppType ' : convert_cType_to_cppType ,
' as_varName ' : as_varName ,
' decorate ' : decorate ,
}
renders = [ ]
c_params = { ' native_methods ' : lambda typ : c_native_methods ( api_params [ ' types ' ] , typ ) }
cpp_params = { ' native_methods ' : lambda typ : cpp_native_methods ( api_params [ ' types ' ] , typ ) }
if ' dawn_headers ' in targets :
renders . append ( FileRender ( ' api.h ' , ' dawn/dawn.h ' , [ base_params , api_params , c_params ] ) )
renders . append ( FileRender ( ' apicpp.h ' , ' dawn/dawncpp.h ' , [ base_params , api_params , cpp_params ] ) )
if ' libdawn ' in targets :
additional_params = { ' native_methods ' : lambda typ : cpp_native_methods ( api_params [ ' types ' ] , typ ) }
renders . append ( FileRender ( ' api.c ' , ' dawn/dawn.c ' , [ base_params , api_params , c_params ] ) )
renders . append ( FileRender ( ' apicpp.cpp ' , ' dawn/dawncpp.cpp ' , [ base_params , api_params , cpp_params ] ) )
if ' mock_dawn ' in targets :
renders . append ( FileRender ( ' mock_api.h ' , ' mock/mock_dawn.h ' , [ base_params , api_params , c_params ] ) )
renders . append ( FileRender ( ' mock_api.cpp ' , ' mock/mock_dawn.cpp ' , [ base_params , api_params , c_params ] ) )
if ' dawn_native_utils ' in targets :
frontend_params = [
base_params ,
api_params ,
c_params ,
{
' as_frontendType ' : lambda typ : as_frontendType ( typ ) , # TODO as_frontendType and friends take a Type and not a Name :(
' as_annotated_frontendType ' : lambda arg : annotated ( as_frontendType ( arg . type ) , arg )
}
]
renders . append ( FileRender ( ' dawn_native/ValidationUtils.h ' , ' dawn_native/ValidationUtils_autogen.h ' , frontend_params ) )
renders . append ( FileRender ( ' dawn_native/ValidationUtils.cpp ' , ' dawn_native/ValidationUtils_autogen.cpp ' , frontend_params ) )
renders . append ( FileRender ( ' dawn_native/api_structs.h ' , ' dawn_native/dawn_structs_autogen.h ' , frontend_params ) )
renders . append ( FileRender ( ' dawn_native/api_structs.cpp ' , ' dawn_native/dawn_structs_autogen.cpp ' , frontend_params ) )
renders . append ( FileRender ( ' dawn_native/ProcTable.cpp ' , ' dawn_native/ProcTable.cpp ' , frontend_params ) )
if ' dawn_wire ' in targets :
additional_params = wire_cmd . compute_wire_params ( api_params , wire_json )
wire_params = [
base_params ,
api_params ,
c_params ,
{
' as_wireType ' : lambda typ : typ . name . CamelCase ( ) + ' * ' if typ . category == ' object ' else as_cppType ( typ . name )
} ,
additional_params
]
renders . append ( FileRender ( ' dawn_wire/WireCmd.h ' , ' dawn_wire/WireCmd_autogen.h ' , wire_params ) )
renders . append ( FileRender ( ' dawn_wire/WireCmd.cpp ' , ' dawn_wire/WireCmd_autogen.cpp ' , wire_params ) )
renders . append ( FileRender ( ' dawn_wire/client/ApiObjects.h ' , ' dawn_wire/client/ApiObjects_autogen.h ' , wire_params ) )
renders . append ( FileRender ( ' dawn_wire/client/ApiProcs.cpp ' , ' dawn_wire/client/ApiProcs_autogen.cpp ' , wire_params ) )
renders . append ( FileRender ( ' dawn_wire/client/ApiProcs.h ' , ' dawn_wire/client/ApiProcs_autogen.h ' , wire_params ) )
renders . append ( FileRender ( ' dawn_wire/client/ClientBase.h ' , ' dawn_wire/client/ClientBase_autogen.h ' , wire_params ) )
renders . append ( FileRender ( ' dawn_wire/client/ClientHandlers.cpp ' , ' dawn_wire/client/ClientHandlers_autogen.cpp ' , wire_params ) )
renders . append ( FileRender ( ' dawn_wire/client/ClientPrototypes.inc ' , ' dawn_wire/client/ClientPrototypes_autogen.inc ' , wire_params ) )
renders . append ( FileRender ( ' dawn_wire/server/ServerBase.h ' , ' dawn_wire/server/ServerBase_autogen.h ' , wire_params ) )
renders . append ( FileRender ( ' dawn_wire/server/ServerDoers.cpp ' , ' dawn_wire/server/ServerDoers_autogen.cpp ' , wire_params ) )
renders . append ( FileRender ( ' dawn_wire/server/ServerHandlers.cpp ' , ' dawn_wire/server/ServerHandlers_autogen.cpp ' , wire_params ) )
renders . append ( FileRender ( ' dawn_wire/server/ServerPrototypes.inc ' , ' dawn_wire/server/ServerPrototypes_autogen.inc ' , wire_params ) )
return renders
def get_dependencies ( self , args ) :
deps = [ os . path . abspath ( args . dawn_json ) ]
if args . wire_json != None :
deps + = [ os . path . abspath ( args . wire_json ) ]
return deps
2017-04-20 18:38:20 +00:00
if __name__ == ' __main__ ' :
2019-06-07 08:59:17 +00:00
sys . exit ( run_generator ( MultiGeneratorFromDawnJSON ( ) ) )