Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2195)

Unified Diff: tools/nixysa/nixysa/js_header_generator.py

Issue 2043006: WTF NPAPI extension. Early draft. Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: Created 10 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/nixysa/nixysa/java_utils.py ('k') | tools/nixysa/nixysa/js_utils.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/nixysa/nixysa/js_header_generator.py
===================================================================
--- tools/nixysa/nixysa/js_header_generator.py (revision 0)
+++ tools/nixysa/nixysa/js_header_generator.py (revision 0)
@@ -0,0 +1,534 @@
+#!/usr/bin/python2.4
+#
+# Copyright 2008 Google Inc.
+#
+# 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.
+
+"""Javascript JSCompiler and JSDocToolkit file generator.
+
+This module generates JSCompiler and JSDocToolkit file for a
+javascript documentation file from the parsed syntax tree.
+"""
+
+import gflags
+import re
+import cpp_utils
+import js_utils
+import java_utils
+import naming
+import log
+import syntax_tree
+
+
+class UndocumentedError(Exception):
+ """Error raised when a member is undocumented."""
+
+ def __init__(self, obj):
+ Exception.__init__(self)
+ self.object = obj
+
+
+class JSHeaderGenerator(object):
+ """Header generator class.
+
+ This class takes care of the details of generating JavaScript JSDocToolkit
+ format files suitable for either documenation generation or for externs for
+ the JSCompiler.
+
+ It contains a list of functions named after each of the Definition classes in
+ syntax_tree, with a common signature. The appropriate function will be called
+ for each definition, to generate the code for that definition.
+
+ Attributes:
+ _output_dir: output directory
+ force_documentation: whether to force all members to have documentation
+ """
+ _start_whitespace_re = re.compile(r'^(\s+)')
+ _param_re = re.compile(r'\\param (\w+) ')
+ _return_re = re.compile(r'\\return ')
+ _code_re = re.compile(r'\\code')
+ _li_re = re.compile(r'\\li')
+ _var_re = re.compile(r'\\var')
+ _sa_re = re.compile(r'\\sa')
+ _endcode_re = re.compile(r'\\endcode')
+
+ def __init__(self, output_dir):
+ self._output_dir = output_dir
+
+ def GetSectionFromAttributes(self, parent_section, defn):
+ """Gets the code section appropriate for a given definition.
+
+ Classes have 3 definition sections: private, protected and public. This
+ function will pick one of the sections, based on the attributes of the
+ definition, if its parent is a class. For other scopes (namespaces) it will
+ return the parent scope main section.
+
+ Args:
+ parent_section: the main section for the parent scope.
+ defn: the definition.
+
+ Returns:
+ the appropriate section.
+ """
+ if defn.parent and defn.parent.defn_type == 'Class':
+ if 'private' in defn.attributes:
+ return parent_section.GetSection('private:') or parent_section
+ elif 'protected' in defn.attributes:
+ return parent_section.GetSection('protected:') or parent_section
+ else:
+ return parent_section.GetSection('public:') or parent_section
+ else:
+ return parent_section
+
+ def Verbatim(self, parent_section, scope, obj):
+ """Generates the code for a Verbatim definition.
+
+ Verbatim definitions being written for a particular type of output file,
+ this function will check the 'verbatim' attribute, and only emit the
+ verbatim code if it is 'js_header'.
+
+ Args:
+ parent_section: the main section of the parent scope.
+ scope: the parent scope.
+ obj: the Verbatim definition.
+
+ Returns:
+ a list of (boolean, Definition) pairs, of all the types that need
+ to be declared (boolean is False) or defined (boolean is True) before
+ this definition.
+ """
+ scope = scope # silence gpylint.
+ try:
+ if obj.attributes['verbatim'] == 'js_header':
+ section = self.GetSectionFromAttributes(parent_section, obj)
+ section.EmitCode(obj.text)
+ except KeyError:
+ log.SourceWarning(obj.source,
+ 'ignoring verbatim with no verbatim attribute')
+
+ def Typedef(self, parent_section, scope, obj):
+ """Generates the code for a Typedef definition.
+
+ Args:
+ parent_section: the main section of the parent scope.
+ scope: the parent scope.
+ obj: the Typedef definition.
+ """
+ # typedefs do not get exported for JavaScript.
+ # silence lint
+ parent_section, scope, obj = parent_section, scope, obj
+
+ def Variable(self, parent_section, scope, obj):
+ """Generates the code for a Variable definition.
+
+ This function will generate the member/global variable declaration, as well
+ as the setter/getter functions if specified in the attributes.
+
+ Args:
+ parent_section: the main section of the parent scope.
+ scope: the parent scope.
+ obj: the Variable definition.
+ """
+ member_section = self.GetSectionFromAttributes(parent_section, obj)
+
+ bm = obj.type_defn.binding_model
+ id_prefix = js_utils.GetFullyQualifiedScopePrefix(scope)
+ proto = 'prototype.'
+ # Note: There is no static in javascript
+ field_name = naming.Normalize(obj.name, naming.Java)
+ extra = ''
+ if 'getter' in obj.attributes and 'setter' not in obj.attributes:
+ extra = '\n\nThis property is read-only.'
+ elif 'getter' not in obj.attributes and 'setter' in obj.attributes:
+ extra = '\n\nThis property is write-only.'
+ type_string = '\n@type {%s}' % js_utils.GetFullyQualifiedTypeName(
+ obj.type_defn)
+ self.Documentation(member_section, obj, extra + type_string)
+ undef = ''
+ if gflags.FLAGS['properties-equal-undefined'].value:
+ undef = ' = undefined'
+ member_section.EmitCode('%s%s%s%s;' % (id_prefix, proto, field_name, undef))
+ # Note: There are no getter/setter in javascript
+
+ def Function(self, parent_section, scope, obj):
+ """Generates the code for a Function definition.
+
+ Args:
+ parent_section: the main section of the parent scope.
+ scope: the parent scope.
+ obj: the Function definition.
+ """
+ section = self.GetSectionFromAttributes(parent_section, obj)
+ return_type = '**not defined**'
+ if obj.type_defn:
+ return_type = js_utils.GetFullyQualifiedTypeName(obj.type_defn)
+ doc_info = self.Documentation(section, obj, '')
+ if doc_info:
+ # there was a return statement so the return type better NOT be void
+ if return_type == 'void':
+ log.SourceError(obj.source,
+ 'return found for void function: %s' % obj.name)
+ else:
+ # there was no return statement so the return type better be void
+ if (not return_type == 'void') and (not return_type == '**not defined**'):
+ log.SourceError(obj.source,
+ 'return missing for non void function: %s' % obj.name)
+ prototype = js_utils.GetFunctionPrototype(scope, obj, True)
+ section.EmitCode(prototype)
+
+ def OverloadedFunction(self, parent_section, scope, func_array):
+ """Generates the code for an Overloaded Function.
+
+ Args:
+ parent_section: the main section of the parent scope.
+ scope: the parent scope.
+ func_array: an array of function definition objects.
+ """
+ if gflags.FLAGS['overloaded-function-docs'].value:
+ count = 0
+ for func in func_array:
+ old_name = func.name
+ func.name = "%sxxxOVERLOADED%dxxx" % (old_name, count)
+ self.Function(parent_section, scope, func)
+ func.name = old_name
+ count += 1
+ return
+
+ # merge the params.
+ params = []
+ min_params = len(func_array[0].params)
+ for func in func_array:
+ if len(func.params) < min_params:
+ min_params = len(func.params)
+ index = 0
+ # we only take the comment from the first function that documents
+ # a parameter at this position.
+ comments, param_comments = js_utils.GetCommentsForParams(func)
+ for param in func.params:
+ if len(params) <= index:
+ params.append({'orig_name': param.name,
+ 'new_name': param.name,
+ 'docs' : param_comments[param.name],
+ 'params': [{'param': param, 'func': func}]})
+ else:
+ params[index]['params'].append({'param': param, 'func': func})
+ index += 1
+
+ # rename the params.
+ index = 0
+ opt = ''
+ for param in params:
+ if index >= min_params:
+ opt = 'opt_'
+ param['new_name'] = '%sparam%d' % (opt, index + 1)
+ index += 1
+
+ # generate param comments.
+ param_comments = []
+ for param in params:
+ if len(param['params']) == 1:
+ param_string = js_utils.GetFunctionParamType(
+ param['params'][0]['func'],
+ param['params'][0]['param'].name)
+ else:
+ union_strings = set()
+ for option in param['params']:
+ union_strings.add(js_utils.GetFunctionParamType(option['func'],
+ option['param'].name))
+ param_string = '|'.join(union_strings)
+ if len(union_strings) > 1:
+ param_string = '(' + param_string + ')'
+ param_comments += ['@param {%s} %s %s' % (param_string, param['new_name'],
+ param['docs'])]
+
+ first_func = func_array[0]
+
+ # use just the first function's comments.
+ func_comments = (js_utils.GetCommentsForParams(first_func)[0] +
+ '\n'.join(param_comments))
+
+ first_func.attributes['__docs'] = func_comments
+ section = self.GetSectionFromAttributes(parent_section, first_func)
+ self.Documentation(section, first_func, '')
+
+ param_strings = []
+ for param in params:
+ param_strings += [param['new_name']]
+
+ param_string = ', '.join(param_strings)
+ id_prefix = js_utils.GetFullyQualifiedScopePrefix(scope)
+ proto = 'prototype.'
+ prototype = '%s%s%s = function(%s) { };' % (
+ id_prefix, proto, naming.Normalize(first_func.name, naming.Java),
+ param_string)
+ section.EmitCode(prototype)
+
+ def Callback(self, parent_section, scope, obj):
+ """Generates the code for a Callback definition.
+
+ Args:
+ parent_section: the main section of the parent scope.
+ scope: the parent scope.
+ obj: the Class definition.
+ """
+ parent_section, scope, obj = parent_section, scope, obj
+
+ def Class(self, parent_section, scope, obj):
+ """Generates the code for a Class definition.
+
+ This function will recursively generate the code for all the definitions
+ inside the class. These definitions will be output into one of 3 sections
+ (private, protected, public), depending on their attributes. These
+ individual sections will only be output if they are not empty.
+
+ Args:
+ parent_section: the main section of the parent scope.
+ scope: the parent scope.
+ obj: the Class definition.
+ """
+ section = self.GetSectionFromAttributes(parent_section, obj).CreateSection(
+ obj.name)
+ id_prefix = js_utils.GetFullyQualifiedScopePrefix(scope)
+ if 'marshaled' in obj.attributes and not ('no_marshaled_docs' in obj.attributes):
+ found = False
+ for member_defn in obj.defn_list:
+ if member_defn.name == 'marshaled':
+ if isinstance(member_defn, syntax_tree.Variable):
+ type_name = js_utils.GetFullyQualifiedTypeName(
+ member_defn.type_defn)
+ self.Documentation(section, obj, '\n@type {%s}' % type_name)
+ section.EmitCode('%s%s = goog.typedef;' % (id_prefix, obj.name))
+ found = True
+ break
+ if not found:
+ log.SourceError(obj.source,
+ ('no marshaled function found for %s' % obj.name))
+ else:
+ extends = ''
+ if obj.base_type:
+ base = js_utils.GetFullyQualifiedTypeName(obj.base_type)
+ if base[0] == '!':
+ base = base[1:]
+ extends = '\n@extends {%s}' % base
+ self.Documentation(section, obj, '\n@constructor' + extends)
+ section.EmitCode('%s%s = function() { };' % (id_prefix, obj.name))
+
+ public_section = section.CreateSection('public:')
+ protected_section = section.CreateSection('protected:')
+ private_section = section.CreateSection('private:')
+ self.DefinitionList(section, obj, obj.defn_list)
+ # TODO(gman): Delete protected and private sections. Those docs
+ # are not needed for javascript.
+
+ def Namespace(self, parent_section, scope, obj):
+ """Generates the code for a Namespace definition.
+
+ This function will recursively generate the code for all the definitions
+ inside the namespace.
+
+ Args:
+ parent_section: the main section of the parent scope.
+ scope: the parent scope.
+ obj: the Namespace definition.
+ """
+ self.DefinitionList(parent_section, obj, obj.defn_list)
+
+ def Typename(self, parent_section, scope, obj):
+ """Generates the code for a Typename definition.
+
+ Since typenames (undefined types) cannot be expressed in C++, this function
+ will not output code. The definition may be output with a verbatim section.
+
+ Args:
+ parent_section: the main section of the parent scope.
+ scope: the parent scope.
+ obj: the Typename definition.
+ """
+ # silence gpylint.
+ parent_section, scope, obj = parent_section, scope, obj
+
+ def Enum(self, parent_section, scope, obj):
+ """Generates the code for an Enum definition.
+
+ Args:
+ parent_section: the main section of the parent scope.
+ scope: the parent scope.
+ obj: the Enum definition.
+ """
+ section = self.GetSectionFromAttributes(parent_section, obj)
+ type_string = 'number'
+ id_prefix = js_utils.GetFullyQualifiedScopePrefix(scope)
+ self.Documentation(parent_section, obj, '\n@enum {%s}' % type_string)
+ section.EmitCode('%s%s = {' % (id_prefix, obj.name))
+ count = 0
+ for ii in range(0, len(obj.values)):
+ value = obj.values[ii]
+ comma = ','
+ if ii == len(obj.values) - 1:
+ comma = ''
+ if value.value is None:
+ section.EmitCode('%s: %d%s' % (value.name, count, comma))
+ else:
+ section.EmitCode('%s: %s%s' % (value.name, value.value, comma))
+ count = int(value.value)
+ count += 1
+ section.EmitCode('};')
+
+ def DefinitionList(self, parent_section, scope, defn_list):
+ """Generates the code for all the definitions in a list.
+
+ Args:
+ parent_section: the main section of the parent scope.
+ scope: the parent scope.
+ defn_list: the list of definitions.
+ Returns:
+ True if there was a '\returns' tag.
+ """
+ # extract functions and merge functions of the same name and process all
+ # non-functions.
+ function_by_name = {}
+ for obj in defn_list:
+ if 'nojs' in obj.attributes or 'nodocs' in obj.attributes:
+ continue
+ if obj.defn_type == 'Function':
+ if obj.name in function_by_name:
+ function_by_name[obj.name].append(obj)
+ else:
+ function_by_name[obj.name] = [obj]
+ else:
+ func = getattr(self, obj.defn_type)
+ func(parent_section, scope, obj)
+
+ # process functions.
+ for func_array in function_by_name.values():
+ if len(func_array) == 1:
+ self.Function(parent_section, scope, func_array[0])
+ else:
+ self.OverloadedFunction(parent_section, scope, func_array)
+
+ def Documentation(self, parent_section, obj, extra_doc):
+ """Generates the documentation code.
+
+ Args:
+ parent_section: the main section of the parent scope.
+ obj: the object to be documented; may be class, function, enum or field.
+ extra_doc: extra documenation information to be put in comments
+ Raises:
+ UndocumentedError: an error if there is no documentation
+ """
+ try:
+ section = self.GetSectionFromAttributes(parent_section, obj)
+ comment_lines = (obj.attributes['__docs'] + extra_doc).splitlines()
+ # Break up text and insert comment formatting
+ section.EmitCode('/**')
+ # move all blank lines at start of docs.
+ while comment_lines and comment_lines[0].strip() == '':
+ comment_lines = comment_lines[1:]
+ # figure out how much whitespace is on first line and remove
+ # the same amount from all lines
+ start_index = 0
+ if comment_lines:
+ match = self._start_whitespace_re.search(comment_lines[0])
+ if match:
+ start_index = len(match.group(1))
+ flags = {'eat_lines': False};
+ found_returns = False
+ for line in comment_lines:
+ if line[0:start_index].strip() == '':
+ line = line[start_index:]
+ if line.startswith('\\'):
+ flags['eat_lines'] = False
+ if self._return_re.match(line):
+ found_returns = True
+ line = self._param_re.sub(
+ lambda match: js_utils.GetParamSpec(obj, match.group(1)),
+ line)
+ line = self._return_re.sub(
+ lambda match: js_utils.GetReturnSpec(obj, flags) + ' ',
+ line)
+ line = self._code_re.sub('<pre>', line)
+ line = self._li_re.sub('<li>', line)
+ line = self._var_re.sub('', line)
+ line = self._sa_re.sub('@see', line)
+ line = self._endcode_re.sub('</pre>', line)
+ if not flags['eat_lines']:
+ section.EmitCode(' * %s' % (line))
+ section.EmitCode(' */')
+ return found_returns
+
+ except KeyError:
+ log.SourceError(obj.source, 'Documentation not found')
+
+ def FieldFunctionDocumentation(self, member_section, description,
+ type_string, field_name):
+ """Automatically generate the get/set function documentation code.
+
+ Args:
+ member_section: the main section of the getter/setter scope.
+ description: describes the field function.
+ type_string: string defining field type.
+ field_name: getter/setter field name.
+ """
+ member_section.EmitCode('/*!')
+ member_section.EmitCode('* %s for %s %s' %
+ (description, type_string, field_name))
+ member_section.EmitCode('*/')
+
+ def Generate(self, idl_file, namespace, defn_list):
+ """Generates the header file.
+
+ Args:
+ idl_file: the source IDL file containing the definitions, as a
+ idl_parser.File instance.
+ namespace: a Definition for the global namespace.
+ defn_list: the list of top-level definitions.
+
+ Returns:
+ a js_utils.JavascriptFileWriter that contains the javascript header file
+ code.
+
+ Raises:
+ CircularDefinition: circular definitions were found in the file.
+ """
+ filename = idl_file.basename + '.js'
+ writer = js_utils.JavascriptFileWriter('%s/%s' % (self._output_dir,
+ filename), True)
+ code_section = writer.CreateSection('defns')
+ self.DefinitionList(code_section, namespace, defn_list)
+ return writer
+
+
+def ProcessFiles(output_dir, pairs, namespace):
+ """Generates the headers for all input files.
+
+ Args:
+ output_dir: the output directory.
+ pairs: a list of (idl_parser.File, syntax_tree.Definition list) describing
+ the list of top-level definitions in each source file.
+ namespace: a syntax_tree.Namespace for the global namespace.
+
+ Returns:
+ a list of js_utils.JavascriptFileWriter, one for each output header file.
+ """
+ generator = JSHeaderGenerator(output_dir)
+ writer_list = []
+ for (f, defn) in pairs:
+ writer_list.append(generator.Generate(f, namespace, defn))
+ return writer_list
+
+
+def main():
+ pass
+
+if __name__ == '__main__':
+ main()
Property changes on: tools/nixysa/nixysa/js_header_generator.py
___________________________________________________________________
Added: svn:eol-style
+ LF
« no previous file with comments | « tools/nixysa/nixysa/java_utils.py ('k') | tools/nixysa/nixysa/js_utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698