1#!/usr/bin/env python 2# Copyright (c) 2013 Google Inc. All rights reserved. 3# 4# Redistribution and use in source and binary forms, with or without 5# modification, are permitted provided that the following conditions are 6# met: 7# 8# * Redistributions of source code must retain the above copyright 9# notice, this list of conditions and the following disclaimer. 10# * Redistributions in binary form must reproduce the above 11# copyright notice, this list of conditions and the following disclaimer 12# in the documentation and/or other materials provided with the 13# distribution. 14# * Neither the name of Google Inc. nor the names of its 15# contributors may be used to endorse or promote products derived from 16# this software without specific prior written permission. 17# 18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 30import re 31import sys 32import json 33 34 35def parse_idl_file(idlFileName): 36 idlFile = open(idlFileName, "r") 37 source = idlFile.read() 38 idlFile.close() 39 40 source = re.sub(r"//.*\n", "", source) # Remove line comments 41 source = re.sub(r"\s+", " ", source) # Line breaks to spaces, collapse spaces 42 source = re.sub(r"/\*.*?\*/", "", source) # Remove block comments 43 source = re.sub(r"\[.*?\]", "", source) # Remove method parameters and array type suffixes 44 source = re.sub(r"\?", "", source) # Remove optional type suffixes 45 46 parsed_webgl_calls = [] 47 48 # Search for method signatures 49 for line in source.split(";"): 50 match = re.match(r"^\s*(\w[\w\s]*)\s+(\w+)\s*\(([^()]*)\)", line) 51 if not match: 52 continue 53 54 return_type = match.group(1).strip() 55 function_name = match.group(2) 56 arguments_string = match.group(3) 57 58 # Search for argument signatures 59 argument_types = [] 60 for argument in arguments_string.split(","): 61 match = re.match(r"^\s*(\w[\w\s]*)\s+(\w+)\s*$", argument) 62 if not match: 63 continue 64 argument_types.append(match.group(1).strip()) 65 66 # Special case for texParameterf/texParameteri and getTexParameter: treat the parameter as GLenum regardless of the IDL specification: 67 # void texParameterf(GLenum target, GLenum pname, GLfloat param) 68 # void texParameteri(GLenum target, GLenum pname, GLint param) 69 # any getTexParameter(GLenum target, GLenum pname) 70 if function_name == "texParameterf" or function_name == "texParameteri": 71 argument_types[2] = "GLenum" 72 if function_name == "getTexParameter": 73 return_type = "GLenum" 74 75 parsed_webgl_calls.append({"function_name": function_name, "return_type": return_type, "argument_types": argument_types}) 76 77 return parsed_webgl_calls 78 79 80def generate_json_lines(parsed_webgl_calls): 81 enum_types = ["GLenum", "GLbitfield"] 82 hints = { 83 "blendFunc": ["ZERO", "ONE"], 84 "blendFuncSeparate": ["ZERO", "ONE"], 85 "stencilOp": ["ZERO", "ONE"], 86 "stencilOpSeparate": ["ZERO", "ONE"], 87 "drawArrays": ["POINTS", "LINES"], 88 "drawElements": ["POINTS", "LINES"], 89 "getError": ["NO_ERROR"], 90 } 91 92 json_lines = [] 93 for call in parsed_webgl_calls: 94 function_name = call["function_name"] 95 return_type = call["return_type"] 96 argument_types = call["argument_types"] 97 98 if not (return_type in enum_types or set(enum_types).intersection(argument_types)): 99 continue 100 101 # Using "aname" instead of "name" to make it the first parameter after sorting (for readability sake). 102 result = {"aname": function_name} 103 if return_type in enum_types: 104 result["returnType"] = return_type[2:] 105 106 for enum_type in enum_types: 107 if not enum_type in argument_types: 108 continue 109 result[enum_type[2:]] = [i for i in range(len(argument_types)) if argument_types[i] == enum_type] 110 111 if function_name in hints: 112 result["hints"] = hints[function_name] 113 114 result_json = json.dumps(result, sort_keys=True) 115 if result_json in json_lines: 116 continue 117 json_lines.append(result_json) 118 119 return json_lines 120 121 122def check_injected_script_js_file(jsFileName, json_lines): 123 jsFile = open(jsFileName, "r") 124 source = jsFile.read() 125 jsFile.close() 126 127 missing_lines = [] 128 for line in json_lines: 129 if not line in source: 130 missing_lines.append(line) 131 132 if len(missing_lines): 133 print "ERROR: Injected script file is missing %d line(s) of generated code: " % len(missing_lines) 134 for line in missing_lines: 135 print " %s" % line 136 else: 137 print "OK" 138 139 140def main(argv): 141 if len(argv) < 2: 142 print('Usage: %s path/to/WebGLRenderingContext.idl [path/to/InjectedScriptCanvasModuleSource.js]' % argv[0]) 143 return 1 144 145 parsed_webgl_calls = parse_idl_file(argv[1]) 146 json_lines = generate_json_lines(parsed_webgl_calls) 147 148 if len(json_lines) < 50: 149 print "WARNING: too few WebGL methods parsed: %d! Something wrong with the IDL file parsing?" % len(json_lines) 150 151 if len(argv) > 2: 152 check_injected_script_js_file(argv[2], json_lines) 153 154if __name__ == '__main__': 155 sys.exit(main(sys.argv)) 156