Index: Source/bindings/scripts/code_generator_v8.py |
diff --git a/Source/bindings/scripts/code_generator_v8.py b/Source/bindings/scripts/code_generator_v8.py |
index d2f933044a5a171b5f1109ef2dc94fa95f660842..a7f00c03085f9e4c54cdad094e9600ea1909ac82 100644 |
--- a/Source/bindings/scripts/code_generator_v8.py |
+++ b/Source/bindings/scripts/code_generator_v8.py |
@@ -26,16 +26,860 @@ |
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
-"""Generate Blink V8 bindings (.h and .cpp files). |
+""" |
+Generate Blink V8 bindings (.h and .cpp files). |
Input: An object of class IdlDefinitions, containing an IDL interface X |
Output: V8X.h and V8X.cpp |
- |
-FIXME: Currently a stub, as part of landing the parser and code generator |
-incrementally. Only implements generation of dummy .cpp and .h files. |
""" |
+import os |
import os.path |
Nils Barth (inactive)
2013/07/24 11:41:20
If you import os, you don't need to also import os
Nils Barth (inactive)
2013/07/25 03:30:54
For manipulating paths for C++ #include statements
|
+import sys |
+import re |
+ |
+import idl_definitions |
+import idl_reader |
+ |
+current_dir = os.path.dirname(os.path.realpath(__file__)) |
+# jinja2 is in chromium's third_party directory |
+sys.path.append(os.path.join(current_dir, *([os.pardir] * 4))) |
Nils Barth (inactive)
2013/07/24 11:41:20
Slick!
Maybe a bit too slick (compared with just l
kojih
2013/07/25 09:08:37
I'll use code below from blink_idl_parser.py:
mod
|
+import jinja2 |
+ |
+ |
+def pathsplit(path): |
+ dirs = [] |
+ while True: |
+ (dirname, basename) = os.path.split(path) |
+ if basename == "": |
+ break |
+ dirs.append(basename) |
do-not-use
2013/07/24 11:17:59
Or we could simply dir.insert(0, baseline) to avoi
kojih
2013/07/25 09:08:37
I'll use dir.insert(0, basename) here.
|
+ path = dirname |
+ dirs.reverse() |
+ return dirs |
do-not-use
2013/07/24 10:56:20
return reversed(dirs) ?
|
+ |
+ |
+def abs2rel(path, basedir): |
Nils Barth (inactive)
2013/07/25 03:30:54
os.path.relpath
...or rather:
posixpath.relpath
..
kojih
2013/07/25 09:08:37
Oh thanks!
|
+ path_dirs = pathsplit(os.path.abspath(path)) |
+ basedir_dirs = pathsplit(os.path.abspath(basedir)) |
+ common_dirs = 0 |
+ for i in range(min(len(path_dirs), len(basedir_dirs))): |
+ if path_dirs[i] != basedir_dirs[i]: |
+ break |
+ common_dirs += 1 |
+ relpath = [] |
Nils Barth (inactive)
2013/07/25 03:30:54
Naming-wise (if we still need this), relpath_dirs,
|
+ for i in range(len(basedir_dirs) - common_dirs): |
+ relpath.append("..") |
Nils Barth (inactive)
2013/07/24 11:41:20
os.pardir instead of '..' ?
|
+ relpath.extend(path_dirs[common_dirs:]) |
+ return os.sep.join(relpath) |
do-not-use
2013/07/24 10:56:20
Shouldn't this be '/'.join(relpath)? We don't want
|
+ |
+ |
+def get_path(dic, keys): |
do-not-use
2013/07/24 10:56:20
This function is a bit obscure and does not appear
|
+ node = dic |
+ for key in keys: |
+ if key not in node: |
+ return None |
+ node = node[key] |
+ return node |
+ |
+ |
+def apply_template(path_to_template, params): |
+ dirname, basename = os.path.split(path_to_template) |
+ path_to_templates = os.path.join(current_dir, "templates") |
+ jinja_env = jinja2.Environment(trim_blocks=True, loader=jinja2.FileSystemLoader([dirname, path_to_templates])) |
+ template = jinja_env.get_template(basename) |
+ return template.render(params) |
+ |
+ |
+def create_arguments(arguments): |
do-not-use
2013/07/24 11:17:59
something like create_arguments_string(arguments)
kojih
2013/07/25 09:08:37
ok.
|
+ return ", ".join([argument for argument in arguments if argument]) |
+ |
+ |
+def get_v8_class_name(interface): |
+ return "V8" + interface.name |
+ |
+ |
+def get_impl_name(interface_or_function): |
+ implementedAs = interface_or_function.extended_attributes.get("ImplementedAs") |
+ if implementedAs is not None: |
+ return implementedAs |
+ return interface_or_function.name |
+ |
+ |
+def get_conditional_string(interface_or_attribute_or_operation): |
+ conditional = interface_or_attribute_or_operation.extended_attributes.get("Conditional") |
+ if conditional is None: |
+ return "" |
+ else: |
+ operator = "" |
+ if "&" in conditional: |
+ operator = "&" |
+ if "|" in conditional: |
+ operator = "|" |
+ if operator == "": |
+ return "ENABLE(%s)" % conditional |
+ else: |
+ # Avoid duplicated conditions. |
+ conditions = dict([(expression, True) for expression in conditional.split(operator)]) |
do-not-use
2013/07/24 10:56:20
Can we use a set to avoid duplicated conditions?
kojih
2013/07/25 09:08:37
done.
|
+ return (" %s%s " % (operator, operator)).join(["ENABLE(%s)" % expression for expression in sorted(conditions.keys())]) |
+ |
+ |
+def get_includes_for_type(data_type): |
+ if skip_include_header(data_type): |
+ return [] |
+ includes = [] |
+ if data_type == "EventListener": |
+ includes.append("core/dom/EventListener.h") |
+ elif data_type == "SerializedScriptValue": |
+ includes.append("bindings/v8/SerializedScriptValue.h") |
+ elif data_type == "any" or is_callback_function_type(data_type): |
+ includes.append("bindings/v8/ScriptValue.h") |
+ else: |
+ includes.append("V8%s.h" % data_type) |
+ return includes |
+ |
+ |
+def get_includes_for_parameter(parameter): |
+ includes = [] |
+ array_or_sequence_type = get_array_type(parameter.data_type) or get_sequence_type(parameter.data_type) |
+ if array_or_sequence_type: |
+ if is_ref_ptr_type(array_or_sequence_type): |
+ includes += get_includes_for_type(array_or_sequence_type) |
+ else: |
+ includes += get_includes_for_type(parameter.data_type) |
+ return includes |
+ |
+ |
+def link_overloaded_functions(interface): |
+ name_to_operations = {} |
+ for operation in interface.operations: |
+ name = operation.name |
+ if not name: |
+ operation.overloads = [] |
+ operation.overload_index = 0 |
+ continue |
+ if name not in name_to_operations: |
do-not-use
2013/07/24 10:56:20
I believe you can write these 3 lines as one:
name
|
+ name_to_operations[name] = [] |
+ name_to_operations[name].append(operation) |
+ operation.overloads = name_to_operations[name] |
+ operation.overload_index = len(name_to_operations[name]) |
do-not-use
2013/07/24 11:17:59
What is overload_index for? Seems it is just the l
kojih
2013/07/25 09:08:37
overload_index is identifier among set of overload
|
+ |
+ |
+def sort_and_remove_duplicate(includes): |
+ return sorted(list(set(includes))) |
+ |
+ |
+def get_pass_owner_expression(data_type, expression): |
+ if is_ref_ptr_type(data_type): |
+ return expression + ".release()" |
+ return expression |
+ |
+ |
+def get_convert_to_V8StringResource(attribute_or_parameter, native_type, native_variable_name, native_value): |
+ if not re.match("^V8StringResource", native_type): |
+ raise Exception("Wrong native data_type passed: %s" % native_type) |
+ if attribute_or_parameter.data_type == "DOMString" or is_enum_type(attribute_or_parameter.data_type): |
+ return "V8TRYCATCH_FOR_V8STRINGRESOURCE_VOID(%s, %s, %s);" % (native_type, native_variable_name, native_value) |
+ else: |
+ return "%s %s(%s, true);" % (native_type, native_variable_name, native_value) |
+ |
+# IDL data_type: [element's C++ data_type, V8 data_type] |
+typed_arrays = { |
+ "ArrayBuffer": [], |
+ "ArrayBufferView": [], |
+ "Uint8Array": ["unsigned char", "v8::kExternalUnsignedByteArray"], |
do-not-use
2013/07/24 10:56:20
Seems like we could use tuples instead of lists?
Nils Barth (inactive)
2013/07/25 03:30:54
Good point.
We could even use dictionaries or obje
kojih
2013/07/25 09:08:37
I'll use class NativeTypeAndJSType
|
+ "Uint8ClampedArray": ["unsigned char", "v8::kExternalPixelArray"], |
+ "Uint16Array": ["unsigned short", "v8::kExternalUnsignedShortArray"], |
+ "Uint32Array": ["unsigned int", "v8::kExternalUnsignedIntArray"], |
+ "Int8Array": ["signed char", "v8::kExternalByteArray"], |
+ "Int16Array": ["short", "v8::kExternalShortArray"], |
+ "Int32Array": ["int", "v8::kExternalIntArray"], |
+ "Float32Array": ["float", "v8::kExternalFloatArray"], |
+ "Float64Array": ["double", "v8::kExternalDoubleArray"], |
+} |
+ |
+primitive_types = { |
do-not-use
2013/07/24 10:56:20
Maybe we can use a set? We don't really need the a
kojih
2013/07/25 09:08:37
done.
|
+ "boolean": True, |
+ "void": True, |
+ "Date": True, |
+ "byte": True, |
+ "octet": True, |
+ "short": True, |
+ "long": True, |
+ "long long": True, |
+ "unsigned short": True, |
+ "unsigned long": True, |
+ "unsigned long long": True, |
+ "float": True, |
+ "double": True, |
+} |
+ |
+enum_types = { |
Nils Barth (inactive)
2013/07/25 03:30:54
enum_types and callback_function_types
should prop
kojih
2013/07/25 09:08:37
moved to CodeGeneratorV8.
|
+} |
+ |
+callback_function_types = { |
+} |
+ |
+non_wrapper_types = { |
do-not-use
2013/07/24 10:56:20
Ditto.
|
+ "CompareHow": True, |
+ "DOMTimeStamp": True, |
+ "Dictionary": True, |
+ "EventListener": True, |
+ "MediaQueryListListener": True, |
+ "NodeFilter": True, |
+ "SerializedScriptValue": True, |
+ "any": True, |
+} |
+ |
+dom_node_types = { |
do-not-use
2013/07/24 10:56:20
Ditto.
|
+ "Attr": True, |
+ "CDATASection": True, |
+ "CharacterData": True, |
+ "Comment": True, |
+ "Document": True, |
+ "DocumentFragment": True, |
+ "DocumentType": True, |
+ "Element": True, |
+ "Entity": True, |
+ "HTMLDocument": True, |
+ "Node": True, |
+ "Notation": True, |
+ "ProcessingInstruction": True, |
+ "ShadowRoot": True, |
+ "SVGDocument": True, |
+ "Text": True, |
+ "TestNode": True, |
+} |
+ |
+ |
+def get_sequence_type(data_type): |
+ matched = re.match("^sequence<([\w\d_\s]+)>.*", data_type) |
do-not-use
2013/07/24 10:56:20
Why do we accept whitespace in the sequence type?
Nils Barth (inactive)
2013/07/24 11:41:20
You need to use raw strings for regexes with backs
kojih
2013/07/25 09:08:37
whitespace is necessary to deal with "sequence<uns
kojih
2013/07/25 09:08:37
done.
|
+ if matched: |
+ return matched.group(1) |
+ return "" |
do-not-use
2013/07/24 10:56:20
Why not return None?
|
+ |
+ |
+def get_array_type(data_type): |
+ matched = re.match("^([\w\d_\s]+)\[\]", data_type) |
do-not-use
2013/07/24 10:56:20
White space in array type?
|
+ if matched: |
+ return matched.group(1) |
+ return "" |
do-not-use
2013/07/24 10:56:20
Ditto.
|
+ |
+ |
+def is_wrapper_type(data_type): |
+ if is_union_type(data_type): |
+ return False |
+ if get_array_type(data_type): |
+ return False |
+ if get_sequence_type(data_type): |
+ return False |
+ if is_callback_function_type(data_type): |
+ return False |
+ if is_enum_type(data_type): |
+ return False |
+ if is_primitive_type(data_type): |
+ return False |
+ if data_type == "DOMString": |
+ return False |
Nils Barth (inactive)
2013/07/24 11:41:20
This long string of if's would be clearer as a sin
kojih
2013/07/25 09:08:37
ok.
|
+ return not non_wrapper_types.get(data_type) |
+ |
+ |
+def is_typed_array_type(data_type): |
+ return data_type in typed_arrays |
+ |
+ |
+def is_ref_ptr_type(data_type): |
Nils Barth (inactive)
2013/07/24 11:41:20
Ditto re: boolean expression
|
+ if is_union_type(data_type): |
+ return False |
+ if data_type == "any" or data_type == "DOMString": |
+ return False |
+ if is_primitive_type(data_type): |
+ return False |
+ if get_array_type(data_type): |
+ return False |
+ if get_sequence_type(data_type): |
+ return False |
+ if is_callback_function_type(data_type): |
+ return False |
+ if is_enum_type(data_type): |
+ return False |
+ return True |
+ |
+ |
+def is_primitive_type(data_type): |
+ if data_type in primitive_types: |
Nils Barth (inactive)
2013/07/24 11:41:20
return data_type in primitive_types
|
+ return True |
+ return False |
+ |
+ |
+def is_enum_type(data_type): |
Nils Barth (inactive)
2013/07/24 11:41:20
Ditto.
|
+ if data_type in enum_types: |
+ return True |
+ return False |
+ |
+ |
+def is_callback_function_type(data_type): |
Nils Barth (inactive)
2013/07/24 11:41:20
Ditto.
|
+ if data_type in callback_function_types: |
+ return True |
+ return False |
+ |
+ |
+def is_union_type(data_type): |
Nils Barth (inactive)
2013/07/24 11:41:20
Ditto.
|
+ if isinstance(data_type, idl_definitions.IdlUnionType): |
+ return True |
+ return False |
+ |
+ |
+def skip_include_header(data_type): |
Nils Barth (inactive)
2013/07/24 11:41:20
Ditto.
|
+ if is_primitive_type(data_type): |
+ return True |
+ if is_enum_type(data_type): |
+ return True |
+ if is_callback_function_type(data_type): |
+ return True |
+ if data_type == "DOMString": |
+ return True |
+ return False |
+ |
+ |
+class code_generator_v8: |
Nils Barth (inactive)
2013/07/25 03:30:54
Class names are in CamelCase:
http://www.chromium.
|
+ def __init__(self, definitions, interface_name, output_directory, idl_directories, generate_h=True, generate_cpp=True, verbose=False): |
+ global enum_types |
+ global callback_function_types |
+ self.idl_definitions = definitions |
+ self.interface_name = interface_name |
+ self.idl_directories = idl_directories |
+ self.idl_files = {} |
+ self.output_directory = output_directory |
+ self.generate_h = generate_h |
+ self.generate_cpp = generate_cpp |
+ self.verbose = verbose |
+ self.header = "" |
+ self.implementation = "" |
+ self.cached_interfaces = {} |
+ self.common_template_parameters = {} |
+ self.interface = None |
+ |
+ enum_types = dict([[enum.name, enum.values] for enum in self.idl_definitions.enumerations.values()]) |
+ callback_function_types = self.idl_definitions.callback_functions |
+ self.prepare_idl_files() |
+ |
+ def prepare_idl_files(self): |
+ directories = [os.getcwd() + os.sep + directory for directory in self.idl_directories] |
+ directories = [path for path in directories if os.path.isdir(path)] |
+ directories.append(".") |
+ for directory in directories: |
+ for root, _, filenames in os.walk(directory): |
+ for filename in filenames: |
+ basename, ext = os.path.splitext(filename) |
+ if ext == ".idl": |
+ fullpath = root + os.sep + filename |
do-not-use
2013/07/24 10:56:20
could use os.path.join()
|
+ self.idl_files[basename] = fullpath |
+ |
+ def idl_file_for_interface(self, interface_name): |
+ return self.idl_files.get(interface_name) |
+ |
+ def parse_interface(self, interface_name): |
+ if interface_name in self.cached_interfaces: |
+ return self.cached_interfaces[interface_name] |
+ |
+ # Step #1: Find the IDL file associated with 'interface' |
+ filename = self.idl_file_for_interface(interface_name) |
+ if filename is None: |
Nils Barth (inactive)
2013/07/24 11:41:20
'if not filename' is preferred
(we're not distingu
|
+ raise Exception("Could NOT find IDL file for interface \"%s\"" % interface_name) |
+ |
+ if self.verbose: |
+ print " | |> Parsing parent IDL \"%s\" for interface \"%s\"" % (filename, interface_name) |
+ |
+ # Step #2: Parse the found IDL file (in quiet mode). |
+ definitions = idl_reader.read_idl_file(filename) |
+ for interface in definitions.interfaces.values(): |
+ if interface.name == interface_name or interface.is_partial: |
+ self.cached_interfaces[interface_name] = interface |
+ return interface |
+ |
+ raise Exception("Could NOT find interface definition for %s in %s" % (interface_name, filename)) |
+ |
+ def get_callback_parameter_declaration(self, operation): |
+ parameters = ["%s %s" % (self.get_native_type(parameter.data_type, used_as_parameter=True, called_by_webcore=True), parameter.name) for parameter in operation.arguments] |
+ return ", ".join(parameters) |
do-not-use
2013/07/24 11:17:59
You could reuse create_arguments()
|
+ |
+ def get_js_value_to_native_statement(self, data_type, extended_attributes, js_value, variable_name, isolate): |
+ native_type = self.get_native_type(data_type, extended_attributes=extended_attributes, used_to_assign_js_value=True) |
+ if data_type == "unsigned long" and "IsIndex" in extended_attributes: |
+ # Special-case index arguments because we need to check that they aren't < 0. |
+ native_type = "int" |
+ native_value, includes = self.__get_js_value_to_native(data_type, extended_attributes, js_value, isolate) |
+ code = "" |
+ if data_type == "DOMString" or is_enum_type(data_type): |
+ if not re.search("^V8StringResource", native_type): |
+ raise Exception("Wrong native data_type passed: " + native_type) |
+ code = "V8TRYCATCH_FOR_V8STRINGRESOURCE_VOID(%s, %s, %s);" % (native_type, variable_name, native_value) |
+ elif "EnforceRange" in extended_attributes: |
+ code = "V8TRYCATCH_WITH_TYPECHECK_VOID(%s, %s, %s, %s);" % (native_type, variable_name, native_value, isolate) |
+ else: |
+ code = "V8TRYCATCH_VOID(%s, %s, %s);" % (native_type, variable_name, native_value) |
+ return code, includes |
+ |
+ def __get_js_value_to_native(self, data_type, extended_attributes, js_value, isolate): |
+ """ |
+ @return native_expression, additional_includes |
+ """ |
+ int_conversion = "EnforceRange" in extended_attributes and "EnforceRange" or "NormalConversion" |
+ includes = [] |
+ |
+ if data_type == "boolean": |
+ return "%s->BooleanValue()" % js_value, includes |
+ if data_type == "float" or data_type == "double": |
+ return "static_cast<%s>(%s->NumberValue())" % (data_type, js_value), includes |
+ |
+ arguments = [js_value] |
+ if int_conversion != "NormalConversion": |
+ arguments.append(int_conversion) |
+ arguments.append("ok") |
+ arguments_string = ", ".join(arguments) |
do-not-use
2013/07/24 11:17:59
You could reuse create_arguments()
|
+ |
+ if data_type == "byte": |
+ return "toInt8(%s)" % arguments_string, includes |
+ if data_type == "octet": |
+ return "toUInt8(%s)" % arguments_string, includes |
+ if data_type == "long" or data_type == "short": |
+ return "toInt32(%s)" % arguments_string, includes |
+ if data_type == "unsigned long" or data_type == "unsigned short": |
+ return "toUInt32(%s)" % arguments_string, includes |
+ if data_type == "long long": |
+ return "toInt64(%s)" % arguments_string, includes |
+ if data_type == "unsigned long long": |
+ return "toUInt64(%s)" % arguments_string, includes |
+ if data_type == "CompareHow": |
+ return "static_cast<Range::CompareHow>(%s->Int32Value())" % js_value, includes |
+ if data_type == "Date": |
+ return "toWebCoreDate(%s)" % js_value, includes |
+ if data_type == "DOMStringList": |
+ return "toDOMStringList(%s, %s)" % (js_value, isolate), includes |
+ if data_type == "DOMString" or is_enum_type(data_type): |
+ return js_value, includes |
+ if data_type == "SerializedScriptValue": |
+ includes.append("bindings/v8/SerializedScriptValue.h") |
+ return "SerializedScriptValue::create(%s, %s)" % (js_value, isolate), includes |
+ if data_type == "Dictionary": |
+ includes.append("bindings/v8/Dictionary.h") |
+ return "Dictionary(%s, %s)" % (js_value, isolate), includes |
+ if data_type == "any" or is_callback_function_type(data_type): |
+ includes.append("bindings/v8/ScriptValue.h") |
+ return "ScriptValue(%s)" % js_value, includes |
+ if data_type == "NodeFilter": |
+ return "toNodeFilter(%s)" % js_value, includes |
+ if data_type == "MediaQueryListListener": |
+ includes.append("core/css/MediaQueryListListener.h") |
+ return "MediaQueryListListener::create(%s)" % js_value, includes |
+ if data_type == "EventTarget": |
+ return "V8DOMWrapper::isDOMWrapper(%s) ? toWrapperTypeInfo(v8::Handle<v8::Object>::Cast(%s))->toEventTarget(v8::Handle<v8::Object>::Cast(%s)) : 0" % (js_value, js_value, js_value), includes |
+ if data_type == "ArrayBuffer": |
+ includes += get_includes_for_type(data_type) |
+ return "$value->IsArrayBuffer() ? V8ArrayBuffer::toNative(v8::Handle<v8::ArrayBuffer>::Cast(%s)) : 0" % js_value, includes |
+ if data_type == "XPathNSResolver": |
+ return "toXPathNSResolver(%s, %s)" % (js_value, isolate), includes |
+ |
+ array_or_sequence_type = get_array_type(data_type) or get_sequence_type(data_type) |
+ if array_or_sequence_type: |
+ if is_ref_ptr_type(array_or_sequence_type): |
+ includes.append("V8%s.h" % array_or_sequence_type) |
+ return "(toRefPtrNativeArray<%s, V8%s>(%s, %s))" % (array_or_sequence_type, array_or_sequence_type, js_value, isolate), includes |
+ return "toNativeArray<%s>(%s)" % (self.get_native_type(array_or_sequence_type), js_value), includes |
+ |
+ includes += get_includes_for_type(data_type) |
+ includes.append("V8%s.h" % data_type) |
+ return "V8%s::HasInstance(%s, %s, worldType(%s)) ? V8%s::toNative(v8::Handle<v8::Object>::Cast(%s)) : 0" % (data_type, js_value, isolate, isolate, data_type, js_value), includes |
+ |
+ def get_native_to_js_value_statement(self, data_type, extended_attributes, native_value, \ |
+ receiver="", creation_context="", isolate="", callback_info="", \ |
+ script_wrappable="", for_main_world_suffix="", \ |
+ indent="", used_as_return_value=False): |
+ """ |
+ Create statement which convert native(C++) value into JS value. |
+ |
+ @param[in] data_type IDL data_type |
+ @param[in] extended_attributes |
+ @param[in] native_value e.g. "imp->getImte(index)" |
+ @param[in] indent |
+ @param[in] receiver "%s" will be replaced with JS value. to return something, use used_as_return_value=True |
+ @param[in] creation_context |
+ @param[in] isolate |
+ @param[in] callback_info |
+ @param[in] script_wrappable |
+ @param[in] for_main_world_suffix |
+ @param[in] used_as_return_value |
+ """ |
+ def create_statement(receiver, js_value): |
+ if "%s" in receiver: |
+ return receiver % js_value |
+ return receiver |
+ |
+ def create_statements(receiver, js_value): |
+ if isinstance(receiver, str): |
+ return create_statement(receiver, js_value) |
+ if isinstance(receiver, list): |
+ return "\n".join([create_statement(each_receiver, js_value) for each_receiver in receiver]) |
+ raise Exception("receiver should be string or list") |
+ |
+ if not isolate: |
+ raise Exception("An Isolate is mandatory for native value => JS value conversion.") |
+ |
+ includes = [] |
+ |
+ if is_union_type(data_type): |
+ codes = [] |
+ for i, union_member_type in enumerate(data_type.union_member_types): |
+ union_member_number = i |
+ union_member_variable = "%s%d" % (native_value, union_member_number) |
+ union_member_enabled_variable = "%s%dEnabled" % (native_value, union_member_number) |
+ union_member_native_value = get_pass_owner_expression(union_member_type, union_member_variable) |
+ return_js_value_code, union_member_includes = self.get_native_to_js_value_statement(union_member_type, extended_attributes, union_member_native_value, \ |
+ receiver=receiver, creation_context=creation_context, isolate=isolate, callback_info=callback_info, \ |
+ script_wrappable=script_wrappable, for_main_world_suffix=for_main_world_suffix, \ |
+ indent=indent + indent, used_as_return_value=used_as_return_value) |
+ includes += union_member_includes |
+ code = "" |
+ if used_as_return_value: |
+ code += indent + "if (%s) {\n" % union_member_enabled_variable |
+ code += indent + indent + return_js_value_code + "\n" |
+ code += indent + indent + "return;\n" |
+ code += indent + "}\n" |
+ else: |
+ code += indent + "if (%s) {\n" % union_member_enabled_variable |
+ code += return_js_value_code + "\n" |
+ codes.append(code) |
+ return "\n".join(codes), includes |
+ |
+ native_type = self.get_native_type(data_type) |
+ |
+ if data_type == "boolean": |
+ if used_as_return_value: |
+ receiver = ["%s;"] |
+ return create_statements(receiver, "v8SetReturnValueBool(%s, %s)" % (callback_info, native_value)), includes |
+ return create_statements(receiver, "v8Boolean(%s, %s)" % (native_value, isolate)), includes |
+ |
+ if data_type == "void": |
+ if used_as_return_value: |
+ return "", includes |
+ return create_statements(receiver, "v8Undefined()"), includes |
+ |
+ # HTML5 says that unsigned reflected attributes should be in the range |
+ # [0, 2^31). When a value isn't in this range, a default value (or 0) |
+ # should be returned instead. |
Nils Barth (inactive)
2013/07/24 11:41:20
Spec link?
kojih
2013/07/25 09:08:37
It seems:
http://www.whatwg.org/specs/web-apps/cur
|
+ if "Reflect" in extended_attributes and (data_type == "unsigned long" or data_type == "unsigned short"): |
+ native_value = native_value.replace("getUnsignedIntegralAttribute", "getIntegralAttribute") |
+ if used_as_return_value: |
+ receiver = ["%s;"] |
+ return create_statements(receiver, "v8SetReturnValueUnsigned(%s, std::max(0, %s))" % (callback_info, native_value)), includes |
+ return create_statements(receiver, "v8::Integer::NewFromUnsigned(std::max(0, %s), %s);" % (native_value, isolate)), includes |
+ |
+ if native_type == "int": |
+ if used_as_return_value: |
+ receiver = ["%s;"] |
+ return create_statements(receiver, "v8SetReturnValueInt(%s, %s)" % (callback_info, native_value)), includes |
+ return create_statements(receiver, "v8::Integer::New(%s, %s)" % (native_value, isolate)), includes |
+ |
+ if native_type == "unsigned": |
+ if used_as_return_value: |
+ receiver = ["%s;"] |
+ return create_statements(receiver, "v8SetReturnValueUnsigned(%s, %s)" % (callback_info, native_value)), includes |
+ return create_statements(receiver, "v8::Integer::NewFromUnsigned(%s, %s)" % (native_value, isolate)), includes |
+ |
+ if data_type == "Date": |
+ if used_as_return_value: |
+ receiver = ["v8SetReturnValue(%s, %%s);" % callback_info] |
+ return create_statements(receiver, "v8DateOrNull(%s, %s)" % (native_value, isolate)), includes |
+ |
+ # long long and unsigned long long are not representable in ECMAScript. |
+ if data_type == "long long" or data_type == "unsigned long long" or data_type == "DOMTimeStamp": |
+ if used_as_return_value: |
+ receiver = ["%s;"] |
+ return create_statements(receiver, "v8SetReturnValue(%s, static_cast<double>(%s))" % (callback_info, native_value)), includes |
+ return create_statements(receiver, "v8::Number::New(static_cast<double>(%s))" % native_value), includes |
+ |
+ if is_primitive_type(data_type): |
+ if not (data_type == "float" or data_type == "double"): |
+ raise Exception("unexpected data_type %s" % data_type) |
+ if used_as_return_value: |
+ receiver = ["%s;"] |
+ return create_statements(receiver, "v8SetReturnValue(%s, %s)" % (callback_info, native_value)), includes |
+ return create_statements(receiver, "v8::Number::New(%s)" % native_value), includes |
+ |
+ if native_type == "ScriptValue": |
+ if used_as_return_value: |
+ receiver = ["v8SetReturnValue(%s, %%s);" % callback_info] |
+ js_value = "%s.v8Value()" % native_value |
+ return create_statements(receiver, js_value), includes |
+ |
+ if data_type == "DOMString" or is_enum_type(data_type): |
+ conversion = extended_attributes.get("TreatReturnedNullStringAs") |
+ js_value = "" |
+ function_suffix = "" |
+ arguments = create_arguments([native_value, isolate]) |
+ if conversion is None: |
+ js_value = "v8String(%s)" % arguments |
+ elif conversion == "Null": |
+ js_value = "v8StringOrNull(%s)" % arguments |
+ function_suffix = "OrNull" |
+ elif conversion == "Undefined": |
+ js_value = "v8StringOrUndefined(%s)" % arguments |
+ function_suffix = "OrUndefined" |
+ else: |
+ raise Exception("Unknown value for TreatReturnedNullStringAs extended attribute") |
+ |
+ if used_as_return_value: |
+ receiver = ["v8SetReturnValueString%s(%s, %%s, %s);" % (function_suffix, callback_info, isolate)] |
+ return create_statements(receiver, native_value), includes |
+ return create_statements(receiver, js_value), includes |
+ |
+ array_or_sequence_type = get_array_type(data_type) or get_sequence_type(data_type) |
+ if array_or_sequence_type: |
+ if is_ref_ptr_type(array_or_sequence_type): |
+ includes += get_includes_for_type(array_or_sequence_type) |
+ if used_as_return_value: |
+ receiver = ["v8SetReturnValue(%s, %%s);" % callback_info] |
+ return create_statements(receiver, "v8Array(%s, %s)" % (native_value, isolate)), includes |
+ |
+ includes += get_includes_for_type(data_type) |
+ |
+ if data_type == "SerializedScriptValue": |
+ includes.append("%s.h" % data_type) |
+ if used_as_return_value: |
+ receiver = ["v8SetReturnValue(%s, %%s);" % callback_info] |
+ js_value = "%s ? %s->deserialize() : v8::Handle<v8::Value>(v8::Null(%s))" % (native_value, native_value, isolate) |
+ return create_statements(receiver, js_value), includes |
+ |
+ includes.append("wtf/RefPtr.h") |
+ includes.append("wtf/GetPtr.h") |
+ |
+ if used_as_return_value: |
+ receiver = ["v8SetReturnValue(%s, %%s);" % callback_info] |
+ |
+ if script_wrappable: |
+ # FIXME: Use safe handles |
+ if for_main_world_suffix == "ForMainWorld": |
+ return create_statements(receiver, "toV8ForMainWorld(%s, %s.Holder(), %s.GetIsolate())" % (native_value, callback_info, callback_info)), includes |
+ return create_statements(receiver, "toV8Fast(%s, %s, %s)" % (native_value, callback_info, script_wrappable)), includes |
+ |
+ # FIXME: Use safe handles |
+ return create_statements(receiver, "toV8(%s, %s, %s)" % (native_value, creation_context, isolate)), includes |
+ |
+ def get_native_type(self, data_type, called_by_webcore=False, used_as_parameter=False, used_to_assign_js_value=False, extended_attributes=None): |
+ """ |
+ Return native data_type corresponds to IDL data_type. |
+ @param[in] data_type ... IDL data_type |
+ @param[in] called_by_webcore |
+ @param[in] used_as_parameter |
+ """ |
+ if extended_attributes is None: |
+ extended_attributes = {} |
+ if data_type == "float": |
+ return "float" |
Nils Barth (inactive)
2013/07/24 11:41:20
How about:
if data_type in ['float', 'double', 'lo
kojih
2013/07/25 09:08:37
ok. later.
|
+ if data_type == "double": |
+ return "double" |
+ if data_type == "int" or data_type == "long" or data_type == "short" or data_type == "byte": |
+ return "int" |
Nils Barth (inactive)
2013/07/24 11:41:20
How about:
if data_type in ['int', 'long', 'short'
|
+ if data_type == "unsigned long" or data_type == "unsigned int" or data_type == "unsigned short" or data_type == "octet": |
+ return "unsigned" |
+ if data_type == "long long": |
+ return "long long" |
+ if data_type == "unsigned long long": |
+ return "unsigned long long" |
+ if data_type == "boolean": |
+ return "bool" |
+ if data_type == "DOMString" or is_enum_type(data_type): |
+ if used_to_assign_js_value: |
+ # FIXME: This implements [TreatNullAs=NullString] and [TreatUndefinedAs=NullString], |
+ # but the Web IDL spec requires [TreatNullAs=EmptyString] and [TreatUndefinedAs=EmptyString]. |
+ mode = "" |
+ if extended_attributes.get("TreatNullAs") == "NullString" and extended_attributes.get("TreatUndefinedAs") == "NullString": |
+ mode = "WithUndefinedOrNullCheck" |
+ elif extended_attributes.get("TreatNullAs") == "NullString" or "Reflect" in extended_attributes: |
+ mode = "WithNullCheck" |
+ # FIXME: Add the case for 'elsif ($attributeOrParameter->extendedAttributes->{"TreatUndefinedAs"} and $attributeOrParameter->extendedAttributes->{"TreatUndefinedAs"} eq "NullString"))'. |
+ return "V8StringResource<%s>" % mode |
+ if called_by_webcore: |
+ return "const String&" |
+ return "String" |
+ |
+ # FIXME: remove this |
+ adhoc_rules = { |
+ "CompareHow": "Range::CompareHow", |
+ "DOMTimeStamp": "DOMTimeStamp", |
+ "Date": "double", |
+ "Dictionary": "Dictionary", |
+ "DOMStringList": "RefPtr<DOMStringList>", |
+ "MediaQueryListListener": "RefPtr<MediaQueryListListener>", |
+ "NodeFilter": "RefPtr<NodeFilter>", |
+ "SerializedScriptValue": "RefPtr<SerializedScriptValue>", |
+ "XPathNSResolver": "RefPtr<XPathNSResolver>", |
+ } |
+ if data_type in adhoc_rules: |
+ if used_as_parameter: |
+ return adhoc_rules[data_type].replace("RefPtr", "PassRefPtr") |
+ return adhoc_rules[data_type] |
+ if data_type == "any" or is_callback_function_type(data_type): |
+ return "ScriptValue" |
+ |
+ if is_union_type(data_type): |
+ raise Exception("UnionType is not supported") |
+ |
+ if data_type == "ArrayBuffer": |
+ if used_as_parameter: |
+ return "ArrayBuffer*" |
+ else: |
+ return "RefPtr<ArrayBuffer>" |
+ |
+ # We need to check [ImplementedAs] extended attribute for wrapper types. |
+ if is_wrapper_type(data_type): |
+ interface = self.parse_interface(data_type) |
+ data_type = get_impl_name(interface) |
+ |
+ array_or_sequence_type = get_array_type(data_type) or get_sequence_type(data_type) |
+ if array_or_sequence_type: |
+ data_type = self.get_native_type(array_or_sequence_type) |
+ data_type = data_type.replace(">", "> ") |
+ return "Vector<%s>" % data_type |
+ |
+ if called_by_webcore: |
+ return data_type + "*" |
+ elif used_to_assign_js_value: |
+ return data_type + "*" |
+ elif used_as_parameter: |
+ if re.search(">$", data_type): |
+ data_type += " " |
+ return "PassRefPtr<%s>" % data_type |
+ else: |
+ return "RefPtr<%s>" % data_type |
+ |
+ def header_files_for_interface(self, interface_name, impl_class_name): |
+ includes = [] |
+ if is_typed_array_type(interface_name): |
+ includes.append("wtf/%s.h" % interface_name) |
+ elif not skip_include_header(interface_name): |
+ idl_filename = self.idl_file_for_interface(interface_name) |
+ assert idl_filename |
Nils Barth (inactive)
2013/07/25 03:30:54
Assert is only for internal consistency, not catch
kojih
2013/07/25 09:08:37
ok.
|
+ idlRelPath = "bindings/" + abs2rel(idl_filename, os.getcwd()) |
+ includes.append(os.path.dirname(idlRelPath) + os.sep + impl_class_name + ".h") |
do-not-use
2013/07/24 11:17:59
We likely want '/' not os.sep for include paths
kojih
2013/07/25 09:08:37
done.
|
+ return includes |
+ |
+ def generate_interface(self, interface): |
+ self.interface = interface |
+ link_overloaded_functions(self.interface) |
+ self.common_template_parameters = { |
+ "conditional_string": get_conditional_string(self.interface), |
+ } |
+ if self.generate_h: |
+ if interface.is_callback: |
+ self.generate_callback_header() |
+ else: |
+ self.generate_header() |
+ if self.generate_cpp: |
+ if interface.is_callback: |
+ self.generate_callback_implementation() |
+ else: |
+ self.generate_implementation() |
+ |
+ def write_interface(self): |
+ if self.interface_name in self.idl_definitions.interfaces: |
+ interface = self.idl_definitions.interfaces[self.interface_name] |
+ self.generate_interface(interface) |
+ if self.generate_h: |
+ header_filename = self.output_directory + os.sep + get_v8_class_name(self.interface) + ".h" |
do-not-use
2013/07/24 11:17:59
Could use os.path.join()
kojih
2013/07/25 09:08:37
done.
|
+ with open(header_filename, "w") as f: |
+ f.write(self.header) |
+ if self.generate_cpp: |
+ implementation_filename = self.output_directory + os.sep + get_v8_class_name(self.interface) + ".cpp" |
do-not-use
2013/07/24 11:17:59
Could use os.path.join()
|
+ with open(implementation_filename, "w") as f: |
+ f.write(self.implementation) |
+ else: |
+ raise Exception("%s not in IDL definitions" % self.interface_name) |
+ |
+ def generate_callback_header(self): |
+ impl_class_name = get_impl_name(self.interface) |
+ v8_class_name = get_v8_class_name(self.interface) |
+ includes = [ |
+ "bindings/v8/ActiveDOMCallback.h", |
+ "bindings/v8/DOMWrapperWorld.h", |
+ "bindings/v8/ScopedPersistent.h", |
+ ] |
+ includes += self.header_files_for_interface(self.interface.name, impl_class_name) |
+ includes = sort_and_remove_duplicate(includes) |
+ class_public_definitions = [] |
+ for operation in self.interface.operations: |
+ # only bool used for callback .h |
+ code = "virtual %s %s(%s);" % (self.get_native_type(operation.data_type), operation.name, self.get_callback_parameter_declaration(operation)) |
+ class_public_definitions.append(code) |
+ |
+ template_parameters = self.common_template_parameters.copy() |
+ template_parameters.update({ |
+ "interface_name": self.interface.name, |
+ "impl_class_name": impl_class_name, |
+ "v8_class_name": v8_class_name, |
+ "includes": includes, |
+ "class_public_definitions": class_public_definitions, |
+ }) |
+ |
+ self.header = apply_template("templates/callback.h", template_parameters) |
+ |
+ def generate_callback_implementation(self): |
+ impl_class_name = get_impl_name(self.interface) |
+ v8_class_name = get_v8_class_name(self.interface) |
+ includes = [ |
+ "core/dom/ScriptExecutionContext.h", |
+ "bindings/v8/V8Binding.h", |
+ "bindings/v8/V8Callback.h", |
+ "wtf/Assertions.h", |
+ ] |
+ functions = [] |
+ for operation in self.interface.operations: |
+ if "Custom" in operation.extended_attributes: |
+ continue |
+ includes += get_includes_for_type(operation.data_type) |
+ for parameter in operation.arguments: |
+ includes += get_includes_for_parameter(parameter) |
+ if operation.data_type != "boolean": |
+ raise Exception("We don't yet support callbacks that return non-boolean values.") |
+ parameters = [] |
+ for parameter in operation.arguments: |
+ receiver = "v8::Handle<v8::Value> %sHandle = %%s;" % parameter.name |
+ native_to_js_value_statement, additional_includes = self.get_native_to_js_value_statement(parameter.data_type, parameter.extended_attributes, parameter.name, receiver=receiver, creation_context="v8::Handle<v8::Object>()", isolate="isolate") |
+ includes += additional_includes |
+ parameters.append({ |
+ "name": parameter.name, |
+ "native_to_js_value_statement": native_to_js_value_statement, |
+ }) |
+ definition = { |
+ "return_type": self.get_native_type(operation.data_type), |
+ "name": operation.name, |
+ "parameter_declaration": self.get_callback_parameter_declaration(operation), |
+ "prepare_js_parameters": "", |
+ "handles": ",\n".join(["%sHandle" % parameter.name for parameter in operation.arguments]), |
+ "parameters": parameters, |
+ } |
+ functions.append(definition) |
+ |
+ includes = sort_and_remove_duplicate(includes) |
+ |
+ template_parameters = self.common_template_parameters.copy() |
+ template_parameters.update({ |
+ "interface_name": self.interface.name, |
+ "impl_class_name": impl_class_name, |
+ "v8_class_name": v8_class_name, |
+ "includes": includes, |
+ "functions": functions, |
+ }) |
+ self.implementation = apply_template("templates/callback.cpp", template_parameters) |
+ |
+ def generate_header(self): |
+ # TODO: implement |
+ self.header = "" |
+ |
+ def generate_implementation(self): |
+ # TODO: implement |
+ self.implementation = "" |
def generate_dummy_header_and_cpp(target_interface_name, output_directory): |