Chromium Code Reviews| 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): |