Home | History | Annotate | Download | only in scripts
      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 
     30 import re
     31 import sys
     32 import json
     33 
     34 
     35 def 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 
     80 def 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 
    122 def 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 
    140 def 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 
    154 if __name__ == '__main__':
    155     sys.exit(main(sys.argv))
    156