Index: tools/dom/scripts/generate_blink_file.py |
diff --git a/tools/dom/scripts/generate_blink_file.py b/tools/dom/scripts/generate_blink_file.py |
index 6f274da35b6ae30b6cf35c5375cb3db6e271a321..4beb0a56fa4ce7635874a88d5b07860222268252 100644 |
--- a/tools/dom/scripts/generate_blink_file.py |
+++ b/tools/dom/scripts/generate_blink_file.py |
@@ -7,9 +7,85 @@ |
"""Generates sdk/lib/_blink/dartium/_blink_dartium.dart file.""" |
import os |
- |
+from sets import Set |
from generator import AnalyzeOperation, AnalyzeConstructor |
+# This is list of all methods with native c++ implementations |
+# If performing a dartium merge, the best practice is to comment out this list, |
+# ensure everything runs, and then uncomment this list which might possibly |
+# introduce breaking changes due to changes to these method signatures. |
+_js_custom_members = Set([ |
+ 'Document.createElement', |
+ 'Element.id', |
+ 'Element.tagName', |
+ 'Element.className', |
+ 'Element.setAttribute', |
+ 'Element.getAttribute', |
+ # Consider adding this method so there is a fast path to access only |
+ # element children. |
+ # 'NonDocumentTypeChildNode.nextElementSibling', |
+ 'Node.appendChild', # actually not removed, just native implementation. |
+ 'Node.cloneNode', |
+ 'Node.insertBefore', |
+ 'Node.lastChild', |
+ 'Node.firstChild', |
+ 'Node.parentElement', |
+ 'Node.parentNode', |
+ 'Node.childNodes', |
+ 'Node.removeChild', |
+ 'Node.contains', |
+ 'Node.nextSibling', |
+ 'Node.previousSibling', |
+ 'ChildNode.remove', |
+ 'Document.createTextNode', |
+ 'Window.location', |
+ 'Location.href', |
+ 'Node.querySelector', |
+ |
+ 'HTMLElement.hidden', |
+ 'HTMLElement.style', |
+ 'Element.attributes', |
+ 'Window.innerWidth', |
+ |
+ 'NodeList.length', |
+ 'NodeList.item', |
+ 'ParentNode.children', |
+ 'ParentNode.firstElementChild', |
+ 'ParentNode.lastElementChild', |
+ 'Event.target', |
+ 'MouseEvent.clientY', |
+ 'MouseEvent.clientX', |
+ |
+ 'Node.nodeType', |
+ 'Node.textContent', |
+ |
+ 'HTMLCollection.length', |
+ 'HTMLCollection.item', |
+ 'Node.lastElementChild', |
+ 'Node.firstElementChild', |
+ 'HTMLElement_tabIndex', |
+ |
+ 'Element.clientWidth', |
+ 'Element.clientHeight', |
+ 'Document.body', |
+ 'Element.removeAttribute', |
+ 'Element.getBoundingClientRect', |
+ 'CSSStyleDeclaration.getPropertyValue', |
+ 'CSSStyleDeclaration.setProperty', |
+ 'CSSStyleDeclaration.__propertyQuery__', |
+ |
+ # TODO(jacobr): consider implementing these methods as well as they show |
+ # up in benchmarks for some sample applications. |
+ #'Document.createEvent', |
+ #'Document.initEvent', |
+ #'EventTarget.dispatchEvent', |
+ ]) |
+ |
+# Uncomment out this line to short circuited native methods and run all of |
+# dart:html through JS interop except for createElement which is slightly more |
+# tightly natively wired. |
+# _js_custom_members = Set([]) |
+ |
HEADER = """/* Copyright (c) 2014, 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. |
@@ -50,13 +126,14 @@ class Blink_Utils { |
static register(document, tag, customType, extendsTagName) native "Utils_register"; |
- static createElement(document, tagName) native "Utils_createElement"; |
- |
- static constructElement(element_type, jsObject) native "Utils_constructor_create"; |
+ // Defines an interceptor if there is an appropriate JavaScript prototype to define it on. |
+ // In any case, returns a typed JS wrapper compatibile with dart:html and the new |
+ // typed JS Interop. |
+ static defineInterceptorCustomElement(jsObject, Type type) native "Utils_defineInterceptorCustomElement"; |
+ static setInstanceInterceptor(o, Type type, {bool customElement: false}) native "Utils_setInstanceInterceptor"; |
+ // This method will throw if the element isn't actually a real Element. |
static initializeCustomElement(element) native "Utils_initializeCustomElement"; |
- |
- static changeElementWrapper(element, type) native "Utils_changeElementWrapper"; |
} |
class Blink_DOMWindowCrossFrame { |
@@ -106,9 +183,63 @@ class Blink_DOMStringMap { |
} |
// Calls through JsNative but returns DomException instead of error strings. |
+class Stats { |
+ Stats(this.name) { |
+ counts = new Map<String, int>(); |
+ } |
+ |
+ String name; |
+ Map<String, int> counts; |
+ clear() { |
+ counts.clear(); |
+ } |
+ |
+ track(String v) { |
+ counts[v] = counts.putIfAbsent(v, ()=> 0) + 1; |
+ } |
+ toString() { |
+ StringBuffer sb = new StringBuffer(); |
+ sb.write('================'); |
+ sb.write('$name ${counts.length}'); |
+ var keys = counts.keys.toList(); |
+ keys.sort((a,b) => counts[b].compareTo(counts[a])); |
+ for (var key in keys) { |
+ print("$key => ${counts[key]}"); |
+ } |
+ sb.write('---------------'); |
+ sb.write('================'); |
+ return sb; |
+ } |
+} |
+ |
+bool TRACK_STATS = true; |
+dumpStats() { |
+ print("------------ STATS ----------------"); |
+ print(Blink_JsNative_DomException.getPropertyStats.toString()); |
+ print(Blink_JsNative_DomException.setPropertyStats.toString()); |
+ print(Blink_JsNative_DomException.callMethodStats.toString()); |
+ print(Blink_JsNative_DomException.constructorStats.toString()); |
+ print("-----------------------------------"); |
+} |
+ |
+clearStats() { |
+ Blink_JsNative_DomException.getPropertyStats.clear(); |
+ Blink_JsNative_DomException.setPropertyStats.clear(); |
+ Blink_JsNative_DomException.callMethodStats.clear(); |
+ Blink_JsNative_DomException.constructorStats.clear(); |
+} |
+ |
class Blink_JsNative_DomException { |
- static getProperty(js.JsObject o, name) { |
+ static var getPropertyStats = new Stats('get property'); |
+ static var setPropertyStats = new Stats('set property'); |
+ static var callMethodStats = new Stats('call method'); |
+ static var constructorStats = new Stats('constructor'); |
+ |
+ static var constructors = new Map<String, dynamic>(); |
+ |
+ static getProperty(o, String name) { |
try { |
+ if (TRACK_STATS) getPropertyStats.track(name); |
return js.JsNative.getProperty(o, name); |
} catch (e) { |
// Re-throw any errors (returned as a string) as a DomException. |
@@ -116,8 +247,51 @@ class Blink_JsNative_DomException { |
} |
} |
- static callMethod(js.JsObject o, String method, List args) { |
+ static propertyQuery(o, String name) { |
try { |
+ if (TRACK_STATS) getPropertyStats.track('__propertyQuery__'); |
+ return js.JsNative.getProperty(o, name); |
+ } catch (e) { |
+ // Re-throw any errors (returned as a string) as a DomException. |
+ throw new DomException.jsInterop(e); |
+ } |
+ } |
+ |
+ static callConstructor0(String name) { |
+ try { |
+ if (TRACK_STATS) constructorStats.track(name); |
+ var constructor = constructors.putIfAbsent(name, () => js.context[name]); |
+ return js.JsNative.callConstructor0(constructor); |
+ } catch (e) { |
+ // Re-throw any errors (returned as a string) as a DomException. |
+ throw new DomException.jsInterop(e); |
+ } |
+ } |
+ |
+ static callConstructor(String name, List args) { |
+ try { |
+ if (TRACK_STATS) constructorStats.track(name); |
+ var constructor = constructors.putIfAbsent(name, () => js.context[name]); |
+ return js.JsNative.callConstructor(constructor, args); |
+ } catch (e) { |
+ // Re-throw any errors (returned as a string) as a DomException. |
+ throw new DomException.jsInterop(e); |
+ } |
+ } |
+ |
+ static setProperty(o, String name, value) { |
+ try { |
+ if (TRACK_STATS) setPropertyStats.track(name); |
+ return js.JsNative.setProperty(o, name, value); |
+ } catch (e) { |
+ // Re-throw any errors (returned as a string) as a DomException. |
+ throw new DomException.jsInterop(e); |
+ } |
+ } |
+ |
+ static callMethod(o, String method, List args) { |
+ try { |
+ if (TRACK_STATS) callMethodStats.track(method); |
return js.JsNative.callMethod(o, method, args); |
} catch (e) { |
// Re-throw any errors (returned as a string) as a DomException. |
@@ -137,38 +311,73 @@ CLASS_DEFINITION_EXTENDS = """class Blink%s extends Blink%s { |
""" |
#(interface_name) |
-CONSTRUCTOR_0 = ' constructorCallback_0_() => new js.JsObject(Blink_JsNative_DomException.getProperty(js.context, "%s"), []);\n\n' |
+ |
+# |
+CONSTRUCTOR_0 = [' constructorCallback_0_()', |
+ ' => Blink_JsNative_DomException.callConstructor0("%s");\n\n', |
+ ' native "Blink_Constructor_%s";\n\n'] |
#(argument_count, arguments, interface_name, arguments) |
-CONSTRUCTOR_ARGS = ' constructorCallback_%s_(%s) => new js.JsObject(Blink_JsNative_DomException.getProperty(js.context, "%s"), [%s]);\n\n' |
+CONSTRUCTOR_ARGS = [' constructorCallback_%s_(%s)', |
+ ' => Blink_JsNative_DomException.callConstructor("%s", [%s]);\n\n', |
+ ' native "Blink_Constructor_Args_%s" /* %s */;\n\n'] |
#(attribute_name, attribute_name) |
-ATTRIBUTE_GETTER = ' %s_Getter_(mthis) => Blink_JsNative_DomException.getProperty(mthis, "%s");\n\n' |
-ATTRIBUTE_SETTER = ' %s_Setter_(mthis, __arg_0) => mthis["%s"] = __arg_0;\n\n' |
+ATTRIBUTE_GETTER = [' %s_Getter_(mthis)', |
+ ' => Blink_JsNative_DomException.getProperty(mthis /* %s */, "%s");\n\n', |
+ ' native "Blink_Getter_%s_%s";\n\n' |
+ ] |
+ |
+ATTRIBUTE_SETTER = [' %s_Setter_(mthis, __arg_0)', |
+ ' => Blink_JsNative_DomException.setProperty(mthis /* %s */, "%s", __arg_0);\n\n', |
+ ' native "Blink_Setter_%s_%s";\n\n' |
+ ] |
#(operation_name, operationName) |
-OPERATION_0 = ' %s_Callback_0_(mthis) => Blink_JsNative_DomException.callMethod(mthis, "%s", []);\n\n' |
+OPERATION_0 = [' %s_Callback_0_(mthis)', |
+ ' => Blink_JsNative_DomException.callMethod(mthis /* %s */, "%s", []);\n\n', |
+ ' native "Blink_Operation_0_%s_%s";\n\n' |
+ ] |
# getter, setter, deleter and propertyQuery code |
-OPERATION_1 = ' $%s_Callback_1_(mthis, __arg_0) => Blink_JsNative_DomException.callMethod(mthis, "%s", [__arg_0]);\n\n' |
-OPERATION_2 = ' $%s_Callback_2_(mthis, __arg_0, __arg_1) => Blink_JsNative_DomException.callMethod(mthis, "%s", [__arg_0, __arg_1]);\n\n' |
-OPERATION_PQ = ' $%s_Callback_1_(mthis, __arg_0) => mthis[__arg_0];\n\n' |
+OPERATION_1 = [' $%s_Callback_1_(mthis, __arg_0)', |
+ ' => Blink_JsNative_DomException.callMethod(mthis /* %s */, "%s", [__arg_0]);\n\n', |
+ ' native "Blink_Operation_1_%s_%s";\n\n' |
+ ] |
+ |
+OPERATION_2 = [' $%s_Callback_2_(mthis, __arg_0, __arg_1)', |
+ ' => Blink_JsNative_DomException.callMethod(mthis /* %s */, "%s", [__arg_0, __arg_1]);\n\n', |
+ ' native "Blink_Operation_2_%s_%s";\n\n'] |
+ |
+OPERATION_PQ = [' $%s_Callback_1_(mthis, __arg_0)', |
+ ' => Blink_JsNative_DomException.propertyQuery(mthis, __arg_0); /* %s */ \n\n', |
+ ' native "Blink_Operation_PQ_%s";\n\n'] |
#(operation_name, argument_count, arguments, operation_name, arguments) |
ARGUMENT_NUM = "__arg_%s" |
-OPERATION_ARGS = ' %s_Callback_%s_(mthis, %s) => Blink_JsNative_DomException.callMethod(mthis, "%s", [%s]);\n\n' |
+OPERATION_ARGS = [' %s_Callback_%s_(mthis, %s)', |
+ ' => Blink_JsNative_DomException.callMethod(mthis /* %s */, "%s", [%s]);\n\n', |
+ ' native "Blink_Operation_%s_%s"; /* %s */\n\n'] |
+ |
+ |
# get class property to make static call. |
CLASS_STATIC = 'Blink_JsNative_DomException.getProperty(js.context, "%s")' |
# name, classname_getproperty, name |
-STATIC_ATTRIBUTE_GETTER = ' %s_Getter_() => Blink_JsNative_DomException.getProperty(%s, "%s");\n\n' |
+STATIC_ATTRIBUTE_GETTER = [' %s_Getter_()', |
+ ' => Blink_JsNative_DomException.getProperty(%s /* %s */, "%s");\n\n', |
+ ' /* %s */ native "Blink_Static_getter_%s_%s"'] |
# name, classname_getproperty, name |
-STATIC_OPERATION_0 = ' %s_Callback_0_() => Blink_JsNative_DomException.callMethod(%s, "%s", []);\n\n' |
+STATIC_OPERATION_0 = [' %s_Callback_0_()', |
+ ' => Blink_JsNative_DomException.callMethod(%s /* %s */, "%s", []);\n\n', |
+ ' /* %s */ native "Blink_Static_Operation_0_%s_%s'] |
# name, argsCount, args, classname_getproperty, name, args |
-STATIC_OPERATION_ARGS = ' %s_Callback_%s_(%s) => Blink_JsNative_DomException.callMethod(%s, "%s", [%s]);\n\n' |
+STATIC_OPERATION_ARGS = [' %s_Callback_%s_(%s)', |
+ ' => Blink_JsNative_DomException.callMethod(%s /* %s */, "%s", [%s]);\n\n', |
+ ' /* %s */ native "Blink_Static_Operations_%s_%s" /* %s */ \n\n'] |
CLASS_DEFINITION_END = """} |
@@ -197,6 +406,27 @@ constructor_renames = { |
def rename_constructor(name): |
return constructor_renames[name] if name in constructor_renames else name |
+ |
+def _Find_Match(interface_id, member, member_prefix, candidates): |
+ member_name = interface_id + '.' + member |
+ if member_name in candidates: |
+ return member_name |
+ member_name = interface_id + '.' + member_prefix + member |
+ if member_name in candidates: |
+ return member_name |
+ member_name = interface_id + '.*' |
+ if member_name in candidates: |
+ return member_name |
+ |
+def _Is_Native(interface, member): |
+ return _Find_Match(interface, member, '', _js_custom_members) |
+ |
+def Select_Stub(template, is_native): |
+ if is_native: |
+ return template[0] + template[2] |
+ else: |
+ return template[0] + template[1] |
+ |
def Generate_Blink(output_dir, database, type_registry): |
blink_filename = os.path.join(output_dir, '_blink_dartium.dart') |
blink_file = open(blink_filename, 'w') |
@@ -226,7 +456,7 @@ def Generate_Blink(output_dir, database, type_registry): |
_Emit_Blink_Constructors(blink_file, analyzed_constructors) |
elif 'Constructor' in interface.ext_attrs: |
# Zero parameter constructor. |
- blink_file.write(CONSTRUCTOR_0 % rename_constructor(name)) |
+ blink_file.write(Select_Stub(CONSTRUCTOR_0, _Is_Native(name, 'constructor')) % rename_constructor(name)) |
_Process_Attributes(blink_file, interface, interface.attributes) |
_Process_Operations(blink_file, interface, interface.operations) |
@@ -250,27 +480,29 @@ def _Emit_Blink_Constructors(blink_file, analyzed_constructors): |
for callback_index in range(arg_min_count, arg_max_count): |
if callback_index == 0: |
- blink_file.write(CONSTRUCTOR_0 % (rename_constructor(name))) |
+ blink_file.write(Select_Stub(CONSTRUCTOR_0, _Is_Native(name, 'constructor')) % (rename_constructor(name))) |
else: |
arguments = [] |
for i in range(0, callback_index): |
arguments.append(ARGUMENT_NUM % i) |
argument_list = ', '.join(arguments) |
- blink_file.write(CONSTRUCTOR_ARGS % (callback_index, argument_list, rename_constructor(name), argument_list)) |
+ blink_file.write( |
+ Select_Stub(CONSTRUCTOR_ARGS, _Is_Native(name, 'constructor')) % (callback_index, argument_list, rename_constructor(name), argument_list)) |
def _Process_Attributes(blink_file, interface, attributes): |
# Emit an interface's attributes and operations. |
for attribute in sorted(attributes, ConstantOutputOrder): |
name = attribute.id |
+ is_native = _Is_Native(interface.id, name) |
if attribute.is_read_only: |
if attribute.is_static: |
class_property = CLASS_STATIC % interface.id |
- blink_file.write(STATIC_ATTRIBUTE_GETTER % (name, class_property, name)) |
+ blink_file.write(Select_Stub(STATIC_ATTRIBUTE_GETTER, is_native) % (name, class_property, interface.id, name)) |
else: |
- blink_file.write(ATTRIBUTE_GETTER % (name, name)) |
+ blink_file.write(Select_Stub(ATTRIBUTE_GETTER, is_native) % (name, interface.id, name)) |
else: |
- blink_file.write(ATTRIBUTE_GETTER % (name, name)) |
- blink_file.write(ATTRIBUTE_SETTER % (name, name)) |
+ blink_file.write(Select_Stub(ATTRIBUTE_GETTER, is_native) % (name, interface.id, name)) |
+ blink_file.write(Select_Stub(ATTRIBUTE_SETTER, is_native) % (name, interface.id, name)) |
def _Process_Operations(blink_file, interface, operations): |
analyzeOperations = [] |
@@ -292,6 +524,7 @@ def _Emit_Blink_Operation(blink_file, interface, analyzeOperations): |
analyzed = AnalyzeOperation(interface, analyzeOperations) |
(arg_min_count, arg_max_count) = generate_parameter_entries(analyzed.param_infos) |
name = analyzed.js_name |
+ is_native = _Is_Native(interface.id, name) |
operation = analyzeOperations[0] |
if (name.startswith('__') and \ |
@@ -299,13 +532,13 @@ def _Emit_Blink_Operation(blink_file, interface, analyzeOperations): |
'setter' in operation.specials or \ |
'deleter' in operation.specials)): |
if name == '__propertyQuery__': |
- blink_file.write(OPERATION_PQ % (name)) |
+ blink_file.write(Select_Stub(OPERATION_PQ, is_native) % (name, interface.id)) |
else: |
arg_min_count = arg_max_count |
if arg_max_count == 2: |
- blink_file.write(OPERATION_1 % (name, name)) |
+ blink_file.write(Select_Stub(OPERATION_1, is_native) % (name, interface.id, name)) |
elif arg_max_count == 3: |
- blink_file.write(OPERATION_2 % (name, name)) |
+ blink_file.write(Select_Stub(OPERATION_2, is_native) % (name, interface.id, name)) |
else: |
print "FATAL ERROR: _blink emitter operator %s.%s" % (interface.id, name) |
exit |
@@ -316,9 +549,9 @@ def _Emit_Blink_Operation(blink_file, interface, analyzeOperations): |
if callback_index == 0: |
if operation.is_static: |
class_property = CLASS_STATIC % interface.id |
- blink_file.write(STATIC_OPERATION_0 % (name, class_property, name)) |
+ blink_file.write(Select_Stub(STATIC_OPERATION_0, is_native) % (name, class_property, interface.id, name)) |
else: |
- blink_file.write(OPERATION_0 % (name, name)) |
+ blink_file.write(Select_Stub(OPERATION_0, is_native) % (name, interface.id, name)) |
else: |
arguments = [] |
for i in range(0, callback_index): |
@@ -326,6 +559,6 @@ def _Emit_Blink_Operation(blink_file, interface, analyzeOperations): |
argument_list = ', '.join(arguments) |
if operation.is_static: |
class_property = CLASS_STATIC % interface.id |
- blink_file.write(STATIC_OPERATION_ARGS % (name, callback_index, argument_list, class_property, name, argument_list)) |
+ blink_file.write(Select_Stub(STATIC_OPERATION_ARGS, is_native) % (name, callback_index, argument_list, class_property, interface.id, name, argument_list)) |
else: |
- blink_file.write(OPERATION_ARGS % (name, callback_index, argument_list, name, argument_list)) |
+ blink_file.write(Select_Stub(OPERATION_ARGS, is_native) % (name, callback_index, argument_list, interface.id, name, argument_list)) |