| #!/usr/bin/python | |
| # Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| # for details. All rights reserved. Use of this source code is governed by a | |
| # BSD-style license that can be found in the LICENSE file. | |
| """This module providesfunctionality for systems to generate | |
| Elemental interfaces from the IDL database.""" | |
| import pdb | |
| import os | |
| import json | |
| import systembaseelemental | |
| from generator_java import * | |
| class ElementalJsoSystem(systembaseelemental.SystemElemental): | |
| def __init__(self, templates, database, emitters, output_dir): | |
| super(ElementalJsoSystem, self).__init__( | |
| templates, database, emitters, output_dir) | |
| self._dart_interface_file_paths = [] | |
| def InterfaceGenerator(self, | |
| interface, | |
| common_prefix, | |
| super_interface_name, | |
| source_filter): | |
| """.""" | |
| module = getModule(interface.annotations) | |
| if super_interface_name is not None: | |
| interface_name = super_interface_name | |
| else: | |
| interface_name = interface.id | |
| template_file = 'jso_impl_%s.darttemplate' % interface_name | |
| template = self._templates.TryLoad(template_file) | |
| if not template: | |
| template = self._templates.Load('jso_impl.darttemplate') | |
| if interface_name in self._mixins or interface_name.endswith("Callback") or interface_name.endswith('Handler'): | |
| return NullInterfaceGenerator(module, self._database, | |
| interface, None, | |
| template, | |
| common_prefix, super_interface_name, | |
| source_filter) | |
| dart_interface_file_path = self._FilePathForElementalInterface(module, interface_name) | |
| self._dart_interface_file_paths.append(dart_interface_file_path) | |
| dart_interface_code = self._emitters.FileEmitter(dart_interface_file_path) | |
| return ElementalInterfaceGenerator(module, self._database, | |
| interface, dart_interface_code, | |
| template, | |
| common_prefix, super_interface_name, | |
| source_filter, self._mixins) | |
| def ProcessCallback(self, interface, info): | |
| pass | |
| def _FilePathForElementalInterface(self, module, interface_name): | |
| """Returns the file path of the Dart interface definition.""" | |
| return os.path.join(self._output_dir, 'src', 'elemental', "js", module, | |
| 'Js%s.java' % interface_name) | |
| # ------------------------------------------------------------------------------ | |
| # Used to suppress generation of JSO classes that are not needed | |
| class NullInterfaceGenerator(systembaseelemental.ElementalBase): | |
| def __init__(self, module, database, interface, emitter, template, | |
| common_prefix, super_interface, source_filter): | |
| pass | |
| def StartInterface(self): | |
| pass | |
| def AddOperation(self, x, inherited): | |
| pass | |
| def AddIndexer(self, x): | |
| pass | |
| def AddConstant(self, x): | |
| pass | |
| def AddAttribute(self, x, y, inheritedGetter, inheritedSetter): | |
| pass | |
| def FinishInterface(self): | |
| pass | |
| class ElementalInterfaceGenerator(systembaseelemental.ElementalBase): | |
| """Generates Elemental Interface definition for one DOM IDL interface.""" | |
| def __init__(self, module, database, interface, emitter, template, | |
| common_prefix, super_interface, source_filter, mixins): | |
| """Generates Dart code for the given interface. | |
| Args: | |
| interface -- an IDLInterface instance. It is assumed that all types have | |
| been converted to Dart types (e.g. int, String), unless they are in the | |
| same package as the interface. | |
| common_prefix -- the prefix for the common library, if any. | |
| super_interface -- the name of the common interface that this interface | |
| implements, if any. | |
| source_filter -- if specified, rewrites the names of any superinterfaces | |
| that are not from these sources to use the common prefix. | |
| """ | |
| super(self.__class__, self).__init__() | |
| self._module = module | |
| self._database = database | |
| self._interface = interface | |
| self._emitter = emitter | |
| self._template = template | |
| self._common_prefix = common_prefix | |
| self._super_interface = super_interface | |
| self._source_filter = source_filter | |
| self._mixins = mixins | |
| current_dir = os.path.dirname(__file__) | |
| self._docdatabase = json.load(open(os.path.join(current_dir, '..', 'docs/database.json'))) | |
| def addImport(self, imports, typeid): | |
| # skip primitive types that are all lowercase first letter | |
| etype = DartType(typeid) | |
| if '<' not in typeid and etype[0].isupper(): | |
| rawtype = etype.split('<')[0] | |
| if rawtype in ['Indexable', 'Settable', 'Mappable']: | |
| pmodule = 'util' | |
| imports['import elemental.js.%s.%s;\n' % (pmodule, 'Js' + rawtype)]=1 | |
| imports['import elemental.%s.%s;\n' % (pmodule, rawtype)]=1 | |
| elif etype not in java_lang and self._database.HasInterface(typeid): | |
| pinterface = self._database.GetInterface(typeid) | |
| pmodule = getModule(pinterface.annotations) | |
| if pmodule != self._module and not rawtype.endswith("Callback") and not rawtype.endswith("Handler") and not rawtype == 'EventTarget': | |
| imports['import elemental.js.%s.%s;\n' % (pmodule, 'Js' + rawtype)]=1 | |
| imports['import elemental.%s.%s;\n' % (pmodule, rawtype)]=1 | |
| def StartInterface(self): | |
| if self._super_interface: | |
| typename = self._super_interface | |
| else: | |
| typename = self._interface.id | |
| implements = [] | |
| implements_raw = {} | |
| extends = '' | |
| suppressed_extends = [] | |
| imports = {} | |
| alreadyImplemented = {} | |
| for p in self._interface.parents: | |
| self.getImplements(alreadyImplemented, p) | |
| for attr in self._interface.attributes: | |
| self.addImport(imports, attr.type.id) | |
| for oper in self._interface.operations: | |
| self.addImport(imports, oper.type.id) | |
| for arg in oper.arguments: | |
| self.addImport(imports, arg.type.id) | |
| for parent in self._interface.parents: | |
| if not parent.type.id in alreadyImplemented or parent.type.id == 'ElementalMixinBase': | |
| self.addImport(imports, parent.type.id) | |
| # TODO(vsm): Remove source_filter. | |
| rawtype = DartType(parent.type.id).split('<')[0] | |
| if rawtype == 'Object': | |
| continue | |
| if MatchSourceFilter(self._source_filter, parent) and DartType(parent.type.id) != 'Object' and rawtype not in implements_raw: | |
| # Parent is a DOM type. | |
| implements.append(DartType(parent.type.id)) | |
| implements_raw[rawtype]=1 | |
| #TODO(cromwellian) add in Indexable/IndexableInt/IndexableNumber | |
| # elif '<' in parent.type.id: | |
| # Parent is a Dart collection type. | |
| # TODO(vsm): Make this check more robust. | |
| # extends.append(parent.type.id) | |
| # else: | |
| # suppressed_extends.append('%s.%s' % | |
| # (self._common_prefix, parent.type.id)) | |
| comment = ' extends' | |
| implements_str = '' | |
| # the Mixin base class is special, it extends JSO | |
| if self._interface.id == 'ElementalMixinBase': | |
| extends = ' extends JsElementalBase' | |
| filtered_mixins = [] | |
| for mixin in self._mixins: | |
| filtered_mixins.append(DartType(mixin)) | |
| implements.extend(filtered_mixins) | |
| else: | |
| # default, every type extends the Mixin base | |
| extends = ' extends JsElementalMixinBase' | |
| rawtype = DartType(self._interface.id).split('<')[0] | |
| if rawtype not in implements_raw and rawtype != 'Object': | |
| implements.insert(0, DartType(self._interface.id)) | |
| self.addImport(imports, self._interface.id) | |
| # parents[0] is the implementing superclass, if exists, but not a mixin, reference its JSO impl | |
| if len(self._interface.parents) > 0 and not self._interface.parents[0].type.id in self._mixins: | |
| extends = ' extends Js' + DartType(self._interface.parents[0].type.id) | |
| self.addImport(imports, self._interface.parents[0].type.id) | |
| if implements: | |
| implements_str += ' implements ' + ', '.join(implements) | |
| comment = ',' | |
| factory_provider = None | |
| constructor_info = AnalyzeConstructor(self._interface) | |
| # TODO(vsm): Add appropriate package / namespace syntax. | |
| (self._members_emitter, | |
| self._top_level_emitter) = self._emitter.Emit( | |
| self._template + '$!TOP_LEVEL', | |
| ID='Js' + typename, | |
| IMPLEMENTS=implements_str, | |
| EXTENDS=extends, PACKAGE='elemental.js.' + self._module, | |
| IMPORTS = ''.join(imports.keys())) | |
| # TODO(cromwellian) auto-generate factory classes? | |
| # if constructor_info: | |
| # self._members_emitter.Emit( | |
| # '\n' | |
| # ' $CTOR($PARAMS);\n', | |
| # CTOR=typename, | |
| # PARAMS=constructor_info.ParametersInterfaceDeclaration()); | |
| # element_type = MaybeTypedArrayElementType(self._interface) | |
| # if element_type: | |
| # self._members_emitter.Emit( | |
| # '\n' | |
| # ' $CTOR(int length);\n' | |
| # '\n' | |
| # ' $CTOR.fromList(List<$TYPE> list);\n' | |
| # '\n' | |
| # ' $CTOR.fromBuffer(ArrayBuffer buffer,' | |
| # ' [int byteOffset, int length]);\n', | |
| # CTOR=self._interface.id, | |
| # TYPE=DartType(element_type)) | |
| def FinishInterface(self): | |
| # add all create* method implementations to Document | |
| if self._interface.id == 'Document': | |
| for iface in self._database.GetInterfaces(): | |
| if iface.id.endswith("Element"): | |
| # try to determine tag name from interface name | |
| elementName = iface.id; | |
| elementName = elementName[0:len(elementName) - len("Element")].lower() | |
| # tablecaption -> <caption> | |
| if elementName == 'tablecaption': | |
| elementName = 'caption' | |
| # SVG is special, it needs createElementNS, strip off prefix | |
| if elementName.startswith("svg"): | |
| elementName = elementName[3:] | |
| # special case, SVGElement is a root class, not a createable element | |
| if elementName != '': | |
| callName = DartType(iface.id) | |
| # SVGSVGElement -> createSVGElement with tag as <svg> | |
| if elementName == 'svg': | |
| callName = 'SVGElement' | |
| self._members_emitter.Emit('\n public final Js$TYPE create$CALL() {\n return createSvgElement("$ELEMENT").cast();\n }\n', | |
| TYPE=DartType(iface.id), | |
| CALL = callName, | |
| ELEMENT=elementName) | |
| elif elementName != '': | |
| self._members_emitter.Emit('\n public final Js$TYPE create$TYPE() {\n return createElement("$ELEMENT").cast();\n }\n', | |
| TYPE=DartType(iface.id), | |
| ELEMENT=elementName) | |
| if self._interface.id == 'Window': | |
| for iface in self._database.GetInterfaces(): | |
| element_type = MaybeTypedArrayElementType(iface) | |
| if element_type: | |
| self._members_emitter.Emit( | |
| '\n' | |
| ' public final native Js$TYPE new$CTOR(int length) /*-{ return new $TYPE(length); }-*/;\n' | |
| '\n' | |
| ' public final native Js$TYPE new$CTOR(IndexableNumber list) /*-{ return new $TYPE(list); }-*/;\n' | |
| '\n' | |
| ' public final native Js$TYPE new$CTOR(ArrayBuffer buffer,' | |
| ' int byteOffset, int length) /*-{ return new $TYPE(buffer, byteOffset, length); }-*/;\n', | |
| CTOR=iface.id, | |
| TYPE=iface.id) | |
| constructor_info = AnalyzeConstructor(iface) | |
| if constructor_info: | |
| self._members_emitter.Emit( | |
| '\n' | |
| ' public final native Js$TYPE new$CTOR($PARAMS) /*-{ return new $JSTYPE($ARGS); }-*/;\n', | |
| TYPE=iface.id, | |
| CTOR=iface.id, | |
| JSTYPE=JsType(iface.id), | |
| PARAMS=constructor_info.ParametersInterfaceDeclaration(), | |
| ARGS=constructor_info.ParametersAsArgumentList(self._database)); | |
| def AddConstant(self, constant): | |
| pass | |
| def AddAttribute(self, getter, setter, inheritedGetter, inheritedSetter): | |
| # you can't override methods in a JSO superclass | |
| if getter and not inheritedGetter: | |
| if getter.type.id == 'EventListener': | |
| self._members_emitter.Emit('\n public final native $TYPE $NAME() /*-{\n return @elemental.js.dom.JsElementalMixinBase::getListenerFor(Lcom/google/gwt/core/client/JavaScriptObject;)(this.$FIELD);\n }-*/;\n', | |
| NAME=getterName(getter), | |
| TYPE=TypeOrVar(DartType(getter.type.id), | |
| getter.type.id), | |
| FIELD=getter.id) | |
| else: | |
| field = 'this.$FIELD' | |
| if getter.id in self.reserved_keywords: | |
| field_template = "this['$FIELD']" | |
| else: | |
| field_template = "this.$FIELD" | |
| self._members_emitter.Emit('\n public final native $TYPE $NAME() /*-{\n return %s;\n }-*/;\n' % field_template , | |
| NAME=getterName(getter), | |
| TYPE=JsoTypeOrVar(DartType(getter.type.id), self._mixins), | |
| FIELD=getter.id) | |
| if setter and not inheritedSetter: | |
| if setter.type.id == 'EventListener': | |
| self._members_emitter.Emit('\n public final native void $NAME($TYPE listener) /*-{\n this.$FIELD = @elemental.js.dom.JsElementalMixinBase::getHandlerFor(Lelemental/events/EventListener;)(listener);\n }-*/;', | |
| NAME=setterName(setter), | |
| TYPE=TypeOrVar(DartType(setter.type.id), | |
| setter.type.id), | |
| FIELD=setter.id) | |
| else: | |
| if setter.id in self.reserved_keywords: | |
| field_template = "this['$FIELD']" | |
| else: | |
| field_template = "this.$FIELD" | |
| self._members_emitter.Emit('\n public final native void $NAME($TYPE param_$FIELD) /*-{\n %s = param_$FIELD;\n }-*/;\n' % field_template, | |
| NAME=setterName(setter), | |
| TYPE=TypeOrVar(DartType(setter.type.id)), | |
| FIELD=setter.id) | |
| def AddIndexer(self, element_type): | |
| # Interface inherits all operations from List<element_type>. | |
| pass | |
| def AddOperation(self, info, inherited): | |
| """ | |
| Arguments: | |
| operations - contains the overloads, one or more operations with the same | |
| name. | |
| """ | |
| # you can't override methods on a JSO superclass | |
| if inherited: | |
| return | |
| # implemented on mixin base template (hack) | |
| if info.name == 'addEventListener' or info.name == 'removeEventListener': | |
| return | |
| get_attrs = [] | |
| set_attrs = [] | |
| for attr in self._interface.attributes: | |
| get_attrs.append(getterName(attr)) | |
| set_attrs.append('set%s' % ucfirst(attr.id)) | |
| if info.name in get_attrs or info.name in set_attrs: | |
| return | |
| body = '' | |
| if info.type_name != 'void': | |
| body += 'return ' | |
| # if op.is_fc_deleter: | |
| # w('delete ') | |
| # if info.id is None: | |
| # w('this[') | |
| # else: | |
| args = info.ParametersAsArgumentList(self._database) | |
| if info.name in self.reserved_keywords: | |
| body += "this['%s'](%s" % (info.name, args) | |
| else: | |
| body += 'this.%s(%s' % (info.name, args) | |
| #if op.id is None: | |
| # w('];\n') | |
| # else: | |
| body += ');' | |
| self._members_emitter.Emit('\n public final native $TYPE $NAME($PARAMS) /*-{\n $BODY\n }-*/;\n', | |
| TYPE=JsoTypeOrVar(info.type_name, self._mixins), | |
| NAME=self.fixReservedKeyWords(info.name), | |
| PARAMS=info.ParametersInterfaceDeclaration(), | |
| BODY=body) | |
| def AddStaticOperation(self, info, inherited): | |
| pass | |
| # Interfaces get secondary members directly via the superinterfaces. | |
| def AddSecondaryAttribute(self, interface, getter, setter): | |
| pass | |
| def AddSecondaryOperation(self, interface, attr): | |
| pass | |
| def ucfirst(string): | |
| """Upper cases the 1st character in a string""" | |
| if len(string): | |
| return '%s%s' % (string[0].upper(), string[1:]) | |
| return string | |
| def getterName(attr): | |
| name = DartDomNameOfAttribute(attr) | |
| if attr.type.id == 'boolean': | |
| if name.startswith('is'): | |
| name = name[2:] | |
| return 'is%s' % ucfirst(name) | |
| return 'get%s' % ucfirst(name) | |
| def setterName(attr): | |
| name = DartDomNameOfAttribute(attr) | |
| return 'set%s' % ucfirst(name) | |
| def getModule(annotations): | |
| htmlaliases = ['audio', 'webaudio', 'inspector', 'offline', 'p2p', 'window', 'websockets', 'threads', 'view', 'storage', 'fileapi'] | |
| module = "dom" | |
| if 'WebKit' in annotations and 'module' in annotations['WebKit']: | |
| module = annotations['WebKit']['module'] | |
| if module in htmlaliases: | |
| return "html" | |
| if module == 'core': | |
| return 'dom' | |
| return module | |
| java_lang = ['Object', 'String', 'Exception', 'DOMTimeStamp', 'DOMString'] | |