diff --git a/build/WebIDL.py b/build/WebIDL.py index 62f3c5a3..4b967f01 100644 --- a/build/WebIDL.py +++ b/build/WebIDL.py @@ -1,4 +1,10 @@ -# from http://mxr.mozilla.org/mozilla-central/source/dom/bindings/parser/WebIDL.py +## JavascriptSubtitlesOctopus +## Patched to: +## - add integer pointers (IntPtr) +## - add [Owner] Extended attribute +## From https://github.com/emscripten-core/emscripten/blob/f36f9fcaf83db93e6a6d0f0cdc47ab6379ade139/third_party/WebIDL.py + +# from https://hg.mozilla.org/mozilla-central/file/tip/dom/bindings/parser/WebIDL.py # rev 501baeb3a034 # This Source Code Form is subject to the terms of the Mozilla Public @@ -2858,6 +2864,7 @@ def handleExtendedAttribute(self, attr): identifier == "AvailableIn" or identifier == "Const" or identifier == "Value" or + identifier == "Owner" or identifier == "BoundsChecked" or identifier == "NewObject"): # Known attributes that we don't need to do anything with here @@ -2933,7 +2940,7 @@ def addExtendedAttributes(self, attrs): self.enforceRange = True elif identifier == "TreatNonCallableAsNull": self._allowTreatNonCallableAsNull = True - elif identifier in ['Ref', 'Const']: + elif identifier in ['Ref', 'Const', 'Owner']: # ok in emscripten self._extraAttributes[identifier] = True else: @@ -4923,11 +4930,12 @@ def p_error(self, p): def __init__(self, outputdir='', lexer=None): Tokenizer.__init__(self, outputdir, lexer) - self.parser = yacc.yacc(module=self, + self.parser = yacc.yacc(debug=0, + module=self, outputdir=outputdir, tabmodule='webidlyacc', - errorlog=yacc.NullLogger(), - picklefile='WebIDLGrammar.pkl') + write_tables=0, + errorlog=yacc.NullLogger()) self._globalScope = IDLScope(BuiltinLocation(""), None, None) self._installBuiltins(self._globalScope) self._productions = [] diff --git a/build/patches/webidl_binder/0001-WebIDL-Add-headers-and-make-it-work-under-SubtitlesO.patch b/build/patches/webidl_binder/0001-WebIDL-Add-headers-and-make-it-work-under-SubtitlesO.patch new file mode 100644 index 00000000..32042933 --- /dev/null +++ b/build/patches/webidl_binder/0001-WebIDL-Add-headers-and-make-it-work-under-SubtitlesO.patch @@ -0,0 +1,87 @@ +From d9b7e2738213d631810ace5e5f47cc8bc210fa8c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Thiago=20Fran=C3=A7a=20da=20Silva?= + +Date: Fri, 18 Feb 2022 21:18:04 -0300 +Subject: [PATCH 1/4] WebIDL: Add headers and make it work under + SubtitlesOctopus project + +--- + build/WebIDL.py | 3 +++ + build/tempfiles.py | 3 +++ + build/webidl_binder.py | 20 ++++++++++---------- + 3 files changed, 16 insertions(+), 10 deletions(-) + +diff --git a/build/WebIDL.py b/build/WebIDL.py +index 14689cb..8892616 100644 +--- a/build/WebIDL.py ++++ b/build/WebIDL.py +@@ -1,3 +1,6 @@ ++## JavascriptSubtitlesOctopus ++## From https://github.com/emscripten-core/emscripten/blob/f36f9fcaf83db93e6a6d0f0cdc47ab6379ade139/third_party/WebIDL.py ++ + # from https://hg.mozilla.org/mozilla-central/file/tip/dom/bindings/parser/WebIDL.py + # rev 501baeb3a034 + +diff --git a/build/tempfiles.py b/build/tempfiles.py +index e1c9dcc..6487516 100644 +--- a/build/tempfiles.py ++++ b/build/tempfiles.py +@@ -1,3 +1,6 @@ ++## JavascriptSubtitlesOctopus ++## From https://github.com/emscripten-core/emscripten/blob/c834ef7d69ccb4100239eeba0b0f6573fed063bc/tools/tempfiles.py ++ + # Copyright 2013 The Emscripten Authors. All rights reserved. + # Emscripten is available under two separate licenses, the MIT license and the + # University of Illinois/NCSA Open Source License. Both these licenses can be +diff --git a/build/webidl_binder.py b/build/webidl_binder.py +index da864f8..687a5ba 100644 +--- a/build/webidl_binder.py ++++ b/build/webidl_binder.py +@@ -1,3 +1,6 @@ ++## JavascriptSubtitlesOctopus ++## From https://github.com/emscripten-core/emscripten/blob/f36f9fcaf83db93e6a6d0f0cdc47ab6379ade139/tools/webidl_binder.py ++ + # Copyright 2014 The Emscripten Authors. All rights reserved. + # Emscripten is available under two separate licenses, the MIT license and the + # University of Illinois/NCSA Open Source License. Both these licenses can be +@@ -10,15 +13,12 @@ https://emscripten.org/docs/porting/connecting_cpp_and_javascript/WebIDL-Binder. + + import os + import sys ++from tempfiles import try_delete + +-__scriptdir__ = os.path.dirname(os.path.abspath(__file__)) +-__rootdir__ = os.path.dirname(__scriptdir__) +-sys.path.append(__rootdir__) +- +-from tools import shared, utils ++def path_from_root(*pathelems): ++ return os.path.join(os.path.join('/', 'emsdk', 'upstream', 'emscripten'), *pathelems) + +-sys.path.append(utils.path_from_root('third_party')) +-sys.path.append(utils.path_from_root('third_party/ply')) ++sys.path.append(path_from_root('third_party', 'ply')) + + import WebIDL + +@@ -50,14 +50,14 @@ class Dummy: + input_file = sys.argv[1] + output_base = sys.argv[2] + +-shared.try_delete(output_base + '.cpp') +-shared.try_delete(output_base + '.js') ++try_delete(output_base + '.cpp') ++try_delete(output_base + '.js') + + p = WebIDL.Parser() + p.parse(r''' + interface VoidPtr { + }; +-''' + utils.read_file(input_file)) ++''' + open(input_file).read()) + data = p.finish() + + interfaces = {} +-- +2.35.1 + diff --git a/build/patches/webidl_binder/0002-WebIDL-Implement-Integer-Pointer-type-IntPtr.patch b/build/patches/webidl_binder/0002-WebIDL-Implement-Integer-Pointer-type-IntPtr.patch new file mode 100644 index 00000000..a34b54ba --- /dev/null +++ b/build/patches/webidl_binder/0002-WebIDL-Implement-Integer-Pointer-type-IntPtr.patch @@ -0,0 +1,101 @@ +From 2e7bb7592195c94853c4e3b6718d47677e1555bf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Thiago=20Fran=C3=A7a=20da=20Silva?= + +Date: Fri, 18 Feb 2022 21:19:20 -0300 +Subject: [PATCH 2/4] WebIDL: Implement Integer Pointer type (IntPtr) + +This allows compatibility with `changed` parameter of the `renderImage` function +--- + build/WebIDL.py | 15 +++++++++++++++ + build/webidl_binder.py | 4 ++++ + 2 files changed, 19 insertions(+) + +diff --git a/build/WebIDL.py b/build/WebIDL.py +index 8892616..d140f8f 100644 +--- a/build/WebIDL.py ++++ b/build/WebIDL.py +@@ -1,4 +1,6 @@ + ## JavascriptSubtitlesOctopus ++## Patched to: ++## - add integer pointers (IntPtr) + ## From https://github.com/emscripten-core/emscripten/blob/f36f9fcaf83db93e6a6d0f0cdc47ab6379ade139/third_party/WebIDL.py + + # from https://hg.mozilla.org/mozilla-central/file/tip/dom/bindings/parser/WebIDL.py +@@ -2165,6 +2167,7 @@ class IDLBuiltinType(IDLType): + 'date', + 'void', + # Funny stuff ++ 'IntPtr', + 'ArrayBuffer', + 'ArrayBufferView', + 'Int8Array', +@@ -2184,6 +2187,7 @@ class IDLBuiltinType(IDLType): + Types.short: IDLType.Tags.int16, + Types.unsigned_short: IDLType.Tags.uint16, + Types.long: IDLType.Tags.int32, ++ Types.IntPtr: IDLType.Tags.int32, + Types.unsigned_long: IDLType.Tags.uint32, + Types.long_long: IDLType.Tags.int64, + Types.unsigned_long_long: IDLType.Tags.uint64, +@@ -2380,6 +2384,9 @@ BuiltinTypes = { + IDLBuiltinType.Types.any: + IDLBuiltinType(BuiltinLocation(""), "Any", + IDLBuiltinType.Types.any), ++ IDLBuiltinType.Types.IntPtr: ++ IDLBuiltinType(BuiltinLocation(""), "IntPtr", ++ IDLBuiltinType.Types.IntPtr), + IDLBuiltinType.Types.domstring: + IDLBuiltinType(BuiltinLocation(""), "String", + IDLBuiltinType.Types.domstring), +@@ -3622,6 +3629,7 @@ class Tokenizer(object): + "...": "ELLIPSIS", + "::": "SCOPE", + "Date": "DATE", ++ "IntPtr": "INTPTR", + "DOMString": "DOMSTRING", + "ByteString": "BYTESTRING", + "any": "ANY", +@@ -4507,6 +4515,7 @@ class Parser(Tokenizer): + | DATE + | DOMSTRING + | BYTESTRING ++ | INTPTR + | ANY + | ATTRIBUTE + | BOOLEAN +@@ -4573,6 +4582,12 @@ class Parser(Tokenizer): + """ + p[0] = self.handleModifiers(BuiltinTypes[IDLBuiltinType.Types.any], p[2]) + ++ def p_SingleTypeIntPtrType(self, p): ++ """ ++ SingleType : INTPTR TypeSuffixStartingWithArray ++ """ ++ p[0] = self.handleModifiers(BuiltinTypes[IDLBuiltinType.Types.IntPtr], p[2]) ++ + def p_UnionType(self, p): + """ + UnionType : LPAREN UnionMemberType OR UnionMemberType UnionMemberTypes RPAREN +diff --git a/build/webidl_binder.py b/build/webidl_binder.py +index 687a5ba..e9a56e5 100644 +--- a/build/webidl_binder.py ++++ b/build/webidl_binder.py +@@ -1,4 +1,6 @@ + ## JavascriptSubtitlesOctopus ++## Patched to: ++## - add integer pointers (IntPtr) + ## From https://github.com/emscripten-core/emscripten/blob/f36f9fcaf83db93e6a6d0f0cdc47ab6379ade139/tools/webidl_binder.py + + # Copyright 2014 The Emscripten Authors. All rights reserved. +@@ -337,6 +339,8 @@ def type_to_c(t, non_pointing=False): + ret = 'bool' + elif t == 'Any' or t == 'VoidPtr': + ret = 'void*' ++ elif t == 'IntPtr': ++ ret = 'int*' + elif t in interfaces: + ret = (interfaces[t].getExtendedAttribute('Prefix') or [''])[0] + t + ('' if non_pointing else '*') + else: +-- +2.35.1 + diff --git a/build/patches/webidl_binder/0003-WebIDL-Implement-ByteString-type.patch b/build/patches/webidl_binder/0003-WebIDL-Implement-ByteString-type.patch new file mode 100644 index 00000000..76d3b85b --- /dev/null +++ b/build/patches/webidl_binder/0003-WebIDL-Implement-ByteString-type.patch @@ -0,0 +1,35 @@ +From ea9c45b10d807966510711da723ea1ae558efd45 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Thiago=20Fran=C3=A7a=20da=20Silva?= + +Date: Mon, 14 Feb 2022 22:27:57 -0300 +Subject: [PATCH 3/4] WebIDL: Implement ByteString type + +This allows compatibility with `bitmap` attribute of the ASS_Image struct +--- + build/webidl_binder.py | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/build/webidl_binder.py b/build/webidl_binder.py +index e9a56e5..faedf10 100644 +--- a/build/webidl_binder.py ++++ b/build/webidl_binder.py +@@ -1,6 +1,7 @@ + ## JavascriptSubtitlesOctopus + ## Patched to: + ## - add integer pointers (IntPtr) ++## - implement ByteString type + ## From https://github.com/emscripten-core/emscripten/blob/f36f9fcaf83db93e6a6d0f0cdc47ab6379ade139/tools/webidl_binder.py + + # Copyright 2014 The Emscripten Authors. All rights reserved. +@@ -327,6 +328,8 @@ def type_to_c(t, non_pointing=False): + ret = 'char' + elif t == 'Octet': + ret = 'unsigned char' ++ elif t == 'ByteString': ++ ret = 'unsigned char*' + elif t == 'Void': + ret = 'void' + elif t == 'String': +-- +2.35.1 + diff --git a/build/patches/webidl_binder/0004-WebIDL-Implement-Owner-Extended-Attribute-fix-77.patch b/build/patches/webidl_binder/0004-WebIDL-Implement-Owner-Extended-Attribute-fix-77.patch new file mode 100644 index 00000000..f4b34e31 --- /dev/null +++ b/build/patches/webidl_binder/0004-WebIDL-Implement-Owner-Extended-Attribute-fix-77.patch @@ -0,0 +1,249 @@ +From af1dc9ac94cc057e8f63641fcb7e0dbe1c320c54 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Thiago=20Fran=C3=A7a=20da=20Silva?= + +Date: Fri, 11 Mar 2022 14:56:15 -0300 +Subject: [PATCH] WebIDL: Implement Owner Extended Attribute, fix #77 + +Currently the ensureCache create a temporary pointer that will +transfer the value to the WASM/C part and recycle it with to use in the +next value, but since the libass struct pointers are owned by the +library, the pointers can't be recycled or freed or can lead into +an undefined behavior. + +This configure the binder to tranfer the pointer ownership instead of +recycle it. + +To avoid creating complex code, I decided to fix it in the webidl +binder. +--- + build/WebIDL.py | 4 ++- + build/webidl_binder.py | 78 +++++++++++++++++++++++++++-------------- + 3 files changed, 64 insertions(+), 36 deletions(-) + +diff --git a/build/WebIDL.py b/build/WebIDL.py +index d140f8f..4b967f0 100644 +--- a/build/WebIDL.py ++++ b/build/WebIDL.py +@@ -1,6 +1,7 @@ + ## JavascriptSubtitlesOctopus + ## Patched to: + ## - add integer pointers (IntPtr) ++## - add [Owner] Extended attribute + ## From https://github.com/emscripten-core/emscripten/blob/f36f9fcaf83db93e6a6d0f0cdc47ab6379ade139/third_party/WebIDL.py + + # from https://hg.mozilla.org/mozilla-central/file/tip/dom/bindings/parser/WebIDL.py +@@ -2863,6 +2864,7 @@ class IDLAttribute(IDLInterfaceMember): + identifier == "AvailableIn" or + identifier == "Const" or + identifier == "Value" or ++ identifier == "Owner" or + identifier == "BoundsChecked" or + identifier == "NewObject"): + # Known attributes that we don't need to do anything with here +@@ -2938,7 +2940,7 @@ class IDLArgument(IDLObjectWithIdentifier): + self.enforceRange = True + elif identifier == "TreatNonCallableAsNull": + self._allowTreatNonCallableAsNull = True +- elif identifier in ['Ref', 'Const']: ++ elif identifier in ['Ref', 'Const', 'Owner']: + # ok in emscripten + self._extraAttributes[identifier] = True + else: +diff --git a/build/webidl_binder.py b/build/webidl_binder.py +index faedf10..1627057 100644 +--- a/build/webidl_binder.py ++++ b/build/webidl_binder.py +@@ -2,6 +2,7 @@ + ## Patched to: + ## - add integer pointers (IntPtr) + ## - implement ByteString type ++## - add [Owner] Extended attribute for Pointer Ownership transfer + ## From https://github.com/emscripten-core/emscripten/blob/f36f9fcaf83db93e6a6d0f0cdc47ab6379ade139/tools/webidl_binder.py + + # Copyright 2014 The Emscripten Authors. All rights reserved. +@@ -174,6 +175,7 @@ var ensureCache = { + size: 0, // the size of buffer + pos: 0, // the next free offset in buffer + temps: [], // extra allocations ++ owned: [], // Owned allocations + needed: 0, // the total size we need next time + + prepare: function() { +@@ -197,22 +199,29 @@ var ensureCache = { + } + ensureCache.pos = 0; + }, +- alloc: function(array, view) { ++ alloc: function(array, view, owner) { + assert(ensureCache.buffer); + var bytes = view.BYTES_PER_ELEMENT; + var len = array.length * bytes; + len = (len + 7) & -8; // keep things aligned to 8 byte boundaries + var ret; +- if (ensureCache.pos + len >= ensureCache.size) { +- // we failed to allocate in the buffer, ensureCache time around :( ++ if (owner) { + assert(len > 0); // null terminator, at least + ensureCache.needed += len; + ret = Module['_malloc'](len); +- ensureCache.temps.push(ret); ++ ensureCache.owned.push(ret); + } else { +- // we can allocate in the buffer +- ret = ensureCache.buffer + ensureCache.pos; +- ensureCache.pos += len; ++ if (ensureCache.pos + len >= ensureCache.size) { ++ // we failed to allocate in the buffer, ensureCache time around :( ++ assert(len > 0); // null terminator, at least ++ ensureCache.needed += len; ++ ret = Module['_malloc'](len); ++ ensureCache.temps.push(ret); ++ } else { ++ // we can allocate in the buffer ++ ret = ensureCache.buffer + ensureCache.pos; ++ ensureCache.pos += len; ++ } + } + return ret; + }, +@@ -228,58 +237,73 @@ var ensureCache = { + view[offset + i] = array[i]; + } + }, ++ clear: function(clearOwned) { ++ for (var i = 0; i < ensureCache.temps.length; i++) { ++ Module['_free'](ensureCache.temps[i]); ++ } ++ if (clearOwned) { ++ for (var i = 0; i < ensureCache.owned.length; i++) { ++ Module['_free'](ensureCache.owned[i]); ++ } ++ } ++ ensureCache.temps.length = 0; ++ Module['_free'](ensureCache.buffer); ++ ensureCache.buffer = 0; ++ ensureCache.size = 0; ++ ensureCache.needed = 0; ++ } + }; + + /** @suppress {duplicate} (TODO: avoid emitting this multiple times, it is redundant) */ +-function ensureString(value) { ++function ensureString(value, owner) { + if (typeof value === 'string') { + var intArray = intArrayFromString(value); +- var offset = ensureCache.alloc(intArray, HEAP8); ++ var offset = ensureCache.alloc(intArray, HEAP8, owner); + ensureCache.copy(intArray, HEAP8, offset); + return offset; + } + return value; + } + /** @suppress {duplicate} (TODO: avoid emitting this multiple times, it is redundant) */ +-function ensureInt8(value) { ++function ensureInt8(value, owner) { + if (typeof value === 'object') { +- var offset = ensureCache.alloc(value, HEAP8); ++ var offset = ensureCache.alloc(value, HEAP8, owner); + ensureCache.copy(value, HEAP8, offset); + return offset; + } + return value; + } + /** @suppress {duplicate} (TODO: avoid emitting this multiple times, it is redundant) */ +-function ensureInt16(value) { ++function ensureInt16(value, owner) { + if (typeof value === 'object') { +- var offset = ensureCache.alloc(value, HEAP16); ++ var offset = ensureCache.alloc(value, HEAP16, owner); + ensureCache.copy(value, HEAP16, offset); + return offset; + } + return value; + } + /** @suppress {duplicate} (TODO: avoid emitting this multiple times, it is redundant) */ +-function ensureInt32(value) { ++function ensureInt32(value, owner) { + if (typeof value === 'object') { +- var offset = ensureCache.alloc(value, HEAP32); ++ var offset = ensureCache.alloc(value, HEAP32, owner); + ensureCache.copy(value, HEAP32, offset); + return offset; + } + return value; + } + /** @suppress {duplicate} (TODO: avoid emitting this multiple times, it is redundant) */ +-function ensureFloat32(value) { ++function ensureFloat32(value, owner) { + if (typeof value === 'object') { +- var offset = ensureCache.alloc(value, HEAPF32); ++ var offset = ensureCache.alloc(value, HEAPF32, owner); + ensureCache.copy(value, HEAPF32, offset); + return offset; + } + return value; + } + /** @suppress {duplicate} (TODO: avoid emitting this multiple times, it is redundant) */ +-function ensureFloat64(value) { ++function ensureFloat64(value, owner) { + if (typeof value === 'object') { +- var offset = ensureCache.alloc(value, HEAPF64); ++ var offset = ensureCache.alloc(value, HEAPF64, owner); + ensureCache.copy(value, HEAPF64, offset); + return offset; + } +@@ -390,7 +414,7 @@ def type_to_cdec(raw): + + def render_function(class_name, func_name, sigs, return_type, non_pointer, + copy, operator, constructor, func_scope, +- call_content=None, const=False, array_attribute=False): ++ call_content=None, const=False, owned=False, array_attribute=False): + legacy_mode = CHECKS not in ['ALL', 'FAST'] + all_checks = CHECKS == 'ALL' + +@@ -505,20 +529,20 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, + if not (arg.type.isArray() and not array_attribute): + body += " if ({0} && typeof {0} === 'object') {0} = {0}.ptr;\n".format(js_arg) + if arg.type.isString(): +- body += " else {0} = ensureString({0});\n".format(js_arg) ++ body += " else {0} = ensureString({0}, {1});\n".format(js_arg, "true" if (owned) else "false") + else: + # an array can be received here + arg_type = arg.type.name + if arg_type in ['Byte', 'Octet']: +- body += " if (typeof {0} == 'object') {{ {0} = ensureInt8({0}); }}\n".format(js_arg) ++ body += " if (typeof {0} == 'object') {{ {0} = ensureInt8({0}, {1}); }}\n".format(js_arg, "true" if (owned) else "false") + elif arg_type in ['Short', 'UnsignedShort']: +- body += " if (typeof {0} == 'object') {{ {0} = ensureInt16({0}); }}\n".format(js_arg) ++ body += " if (typeof {0} == 'object') {{ {0} = ensureInt16({0}, {1}); }}\n".format(js_arg, "true" if (owned) else "false") + elif arg_type in ['Long', 'UnsignedLong']: +- body += " if (typeof {0} == 'object') {{ {0} = ensureInt32({0}); }}\n".format(js_arg) ++ body += " if (typeof {0} == 'object') {{ {0} = ensureInt32({0}, {1}); }}\n".format(js_arg, "true" if (owned) else "false") + elif arg_type == 'Float': +- body += " if (typeof {0} == 'object') {{ {0} = ensureFloat32({0}); }}\n".format(js_arg) ++ body += " if (typeof {0} == 'object') {{ {0} = ensureFloat32({0}, {1}); }}\n".format(js_arg, "true" if (owned) else "false") + elif arg_type == 'Double': +- body += " if (typeof {0} == 'object') {{ {0} = ensureFloat64({0}); }}\n".format(js_arg) ++ body += " if (typeof {0} == 'object') {{ {0} = ensureFloat64({0}, {1}); }}\n".format(js_arg, "true" if (owned) else "false") + + c_names = {} + for i in range(min_args, max_args): +@@ -737,6 +761,7 @@ for name in names: + func_scope=interface, + call_content=get_call_content, + const=m.getExtendedAttribute('Const'), ++ owned=m.getExtendedAttribute('Owner'), + array_attribute=m.type.isArray()) + + if m.readonly: +@@ -755,6 +780,7 @@ for name in names: + func_scope=interface, + call_content=set_call_content, + const=m.getExtendedAttribute('Const'), ++ owned=m.getExtendedAttribute('Owner'), + array_attribute=m.type.isArray()) + mid_js += [r''' + Object.defineProperty(%s.prototype, '%s', { get: %s.prototype.%s, set: %s.prototype.%s });''' % (name, attr, name, get_name, name, set_name)] +-- +2.35.1 + diff --git a/build/tempfiles.py b/build/tempfiles.py index f26a1c74..6487516a 100644 --- a/build/tempfiles.py +++ b/build/tempfiles.py @@ -1,9 +1,11 @@ +## JavascriptSubtitlesOctopus +## From https://github.com/emscripten-core/emscripten/blob/c834ef7d69ccb4100239eeba0b0f6573fed063bc/tools/tempfiles.py + # Copyright 2013 The Emscripten Authors. All rights reserved. # Emscripten is available under two separate licenses, the MIT license and the # University of Illinois/NCSA Open Source License. Both these licenses can be # found in the LICENSE file. -from __future__ import print_function import os import shutil import tempfile @@ -28,13 +30,14 @@ def try_delete(pathname): if not os.path.exists(pathname): return - write_bits = stat.S_IWRITE | stat.S_IWGRP | stat.S_IWOTH + # Ensure all files are readable and writable by the current user. + permission_bits = stat.S_IWRITE | stat.S_IREAD def is_writable(path): - return (os.stat(path).st_mode & write_bits) == write_bits + return (os.stat(path).st_mode & permission_bits) != permission_bits def make_writable(path): - os.chmod(path, os.stat(path).st_mode | write_bits) + os.chmod(path, os.stat(path).st_mode | permission_bits) # Some tests make files and subdirectories read-only, so rmtree/unlink will not delete # them. Force-make everything writable in the subdirectory to make it @@ -54,9 +57,9 @@ def make_writable(path): pass -class TempFiles(object): - def __init__(self, tmp, save_debug_files=False): - self.tmp = tmp +class TempFiles: + def __init__(self, tmpdir, save_debug_files): + self.tmpdir = tmpdir self.save_debug_files = save_debug_files self.to_clean = [] @@ -67,17 +70,19 @@ def note(self, filename): def get(self, suffix): """Returns a named temp file with the given prefix.""" - named_file = tempfile.NamedTemporaryFile(dir=self.tmp, suffix=suffix, delete=False) + named_file = tempfile.NamedTemporaryFile(dir=self.tmpdir, suffix=suffix, delete=False) self.note(named_file.name) return named_file def get_file(self, suffix): - """Returns an object representing a RAII-like access to a temp file, that has convenient pythonesque - semantics for being used via a construct 'with TempFiles.get_file(..) as filename:'. The file will be - deleted immediately once the 'with' block is exited.""" - class TempFileObject(object): + """Returns an object representing a RAII-like access to a temp file + that has convenient pythonesque semantics for being used via a construct + 'with TempFiles.get_file(..) as filename:'. + The file will be deleted immediately once the 'with' block is exited. + """ + class TempFileObject: def __enter__(self_): - self_.file = tempfile.NamedTemporaryFile(dir=self.tmp, suffix=suffix, delete=False) + self_.file = tempfile.NamedTemporaryFile(dir=self.tmpdir, suffix=suffix, delete=False) self_.file.close() # NamedTemporaryFile passes out open file handles, but callers prefer filenames (and open their own handles manually if needed) return self_.file.name @@ -88,20 +93,14 @@ def __exit__(self_, type, value, traceback): def get_dir(self): """Returns a named temp directory with the given prefix.""" - directory = tempfile.mkdtemp(dir=self.tmp) + directory = tempfile.mkdtemp(dir=self.tmpdir) self.note(directory) return directory def clean(self): if self.save_debug_files: - print('not cleaning up temp files since in debug-save mode, see them in %s' % (self.tmp,), file=sys.stderr) + print(f'not cleaning up temp files since in debug-save mode, see them in {self.tmpdir}', file=sys.stderr) return for filename in self.to_clean: try_delete(filename) self.to_clean = [] - - def run_and_clean(self, func): - try: - return func() - finally: - self.clean() diff --git a/build/webidl_binder.py b/build/webidl_binder.py index 9d5c1d53..16270570 100644 --- a/build/webidl_binder.py +++ b/build/webidl_binder.py @@ -1,28 +1,27 @@ +## JavascriptSubtitlesOctopus +## Patched to: +## - add integer pointers (IntPtr) +## - implement ByteString type +## - add [Owner] Extended attribute for Pointer Ownership transfer +## From https://github.com/emscripten-core/emscripten/blob/f36f9fcaf83db93e6a6d0f0cdc47ab6379ade139/tools/webidl_binder.py + # Copyright 2014 The Emscripten Authors. All rights reserved. # Emscripten is available under two separate licenses, the MIT license and the # University of Illinois/NCSA Open Source License. Both these licenses can be # found in the LICENSE file. -# This is a patched WebIDL to work with libass - -''' -WebIDL binder - -http://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/WebIDL-Binder.html -''' - -from __future__ import print_function -import os, sys +"""WebIDL binder -sys.path.insert(1, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +https://emscripten.org/docs/porting/connecting_cpp_and_javascript/WebIDL-Binder.html +""" +import os +import sys from tempfiles import try_delete -print(os.path.join('/', 'emsdk', 'upstream', 'emscripten')) def path_from_root(*pathelems): return os.path.join(os.path.join('/', 'emsdk', 'upstream', 'emscripten'), *pathelems) - sys.path.append(path_from_root('third_party', 'ply')) import WebIDL @@ -32,23 +31,26 @@ def path_from_root(*pathelems): # CHECKS='ALL' will do extensive argument type checking (~5x slower than default). # This will catch invalid numbers, invalid pointers, invalid strings, etc. # Anything else defaults to legacy mode for backward compatibility. -CHECKS = os.environ.get('IDL_CHECKS') or 'DEFAULT' +CHECKS = os.environ.get('IDL_CHECKS', 'DEFAULT') # DEBUG=1 will print debug info in render_function DEBUG = os.environ.get('IDL_VERBOSE') == '1' -if DEBUG: print("Debug print ON, CHECKS=%s" % CHECKS) +if DEBUG: + print("Debug print ON, CHECKS=%s" % CHECKS) # We need to avoid some closure errors on the constructors we define here. -CONSTRUCTOR_CLOSURE_SUPPRESSIONS = '/** @suppress {undefinedVars, duplicate} */' +CONSTRUCTOR_CLOSURE_SUPPRESSIONS = '/** @suppress {undefinedVars, duplicate} @this{Object} */' + -class Dummy(object): +class Dummy: def __init__(self, init): for k, v in init.items(): self.__dict__[k] = v - def getExtendedAttribute(self, name): + def getExtendedAttribute(self, name): # noqa: U100 return None + input_file = sys.argv[1] output_base = sys.argv[2] @@ -74,8 +76,8 @@ def getExtendedAttribute(self, name): elif isinstance(thing, WebIDL.IDLEnum): enums[thing.identifier.name] = thing -#print interfaces -#print implements +# print interfaces +# print implements pre_c = [] mid_c = [] @@ -89,6 +91,7 @@ def getExtendedAttribute(self, name): extern "C" { '''] + def build_constructor(name): implementing_name = implements[name][0] if implements.get(name) else 'WrapperObject' return [r'''{name}.prototype = Object.create({implementing}.prototype); @@ -110,13 +113,15 @@ def build_constructor(name): mid_js += build_constructor('WrapperObject') mid_js += [''' -/** @suppress {duplicate} (TODO: avoid emitting this multiple times, it is redundant) */ +/** @suppress {duplicate} (TODO: avoid emitting this multiple times, it is redundant) + @param {*=} __class__ */ function getCache(__class__) { return (__class__ || WrapperObject).__cache__; } Module['getCache'] = getCache; -/** @suppress {duplicate} (TODO: avoid emitting this multiple times, it is redundant) */ +/** @suppress {duplicate} (TODO: avoid emitting this multiple times, it is redundant) + @param {*=} __class__ */ function wrapPointer(ptr, __class__) { var cache = getCache(__class__); var ret = cache[ptr]; @@ -170,6 +175,7 @@ def build_constructor(name): size: 0, // the size of buffer pos: 0, // the next free offset in buffer temps: [], // extra allocations + owned: [], // Owned allocations needed: 0, // the total size we need next time prepare: function() { @@ -193,89 +199,111 @@ def build_constructor(name): } ensureCache.pos = 0; }, - alloc: function(array, view) { + alloc: function(array, view, owner) { assert(ensureCache.buffer); var bytes = view.BYTES_PER_ELEMENT; var len = array.length * bytes; len = (len + 7) & -8; // keep things aligned to 8 byte boundaries var ret; - if (ensureCache.pos + len >= ensureCache.size) { - // we failed to allocate in the buffer, ensureCache time around :( + if (owner) { assert(len > 0); // null terminator, at least ensureCache.needed += len; ret = Module['_malloc'](len); - ensureCache.temps.push(ret); + ensureCache.owned.push(ret); } else { - // we can allocate in the buffer - ret = ensureCache.buffer + ensureCache.pos; - ensureCache.pos += len; + if (ensureCache.pos + len >= ensureCache.size) { + // we failed to allocate in the buffer, ensureCache time around :( + assert(len > 0); // null terminator, at least + ensureCache.needed += len; + ret = Module['_malloc'](len); + ensureCache.temps.push(ret); + } else { + // we can allocate in the buffer + ret = ensureCache.buffer + ensureCache.pos; + ensureCache.pos += len; + } } return ret; }, copy: function(array, view, offset) { - var offsetShifted = offset; + offset >>>= 0; var bytes = view.BYTES_PER_ELEMENT; switch (bytes) { - case 2: offsetShifted >>= 1; break; - case 4: offsetShifted >>= 2; break; - case 8: offsetShifted >>= 3; break; + case 2: offset >>>= 1; break; + case 4: offset >>>= 2; break; + case 8: offset >>>= 3; break; } for (var i = 0; i < array.length; i++) { - view[offsetShifted + i] = array[i]; + view[offset + i] = array[i]; } }, + clear: function(clearOwned) { + for (var i = 0; i < ensureCache.temps.length; i++) { + Module['_free'](ensureCache.temps[i]); + } + if (clearOwned) { + for (var i = 0; i < ensureCache.owned.length; i++) { + Module['_free'](ensureCache.owned[i]); + } + } + ensureCache.temps.length = 0; + Module['_free'](ensureCache.buffer); + ensureCache.buffer = 0; + ensureCache.size = 0; + ensureCache.needed = 0; + } }; /** @suppress {duplicate} (TODO: avoid emitting this multiple times, it is redundant) */ -function ensureString(value) { +function ensureString(value, owner) { if (typeof value === 'string') { var intArray = intArrayFromString(value); - var offset = ensureCache.alloc(intArray, HEAP8); + var offset = ensureCache.alloc(intArray, HEAP8, owner); ensureCache.copy(intArray, HEAP8, offset); return offset; } return value; } /** @suppress {duplicate} (TODO: avoid emitting this multiple times, it is redundant) */ -function ensureInt8(value) { +function ensureInt8(value, owner) { if (typeof value === 'object') { - var offset = ensureCache.alloc(value, HEAP8); + var offset = ensureCache.alloc(value, HEAP8, owner); ensureCache.copy(value, HEAP8, offset); return offset; } return value; } /** @suppress {duplicate} (TODO: avoid emitting this multiple times, it is redundant) */ -function ensureInt16(value) { +function ensureInt16(value, owner) { if (typeof value === 'object') { - var offset = ensureCache.alloc(value, HEAP16); + var offset = ensureCache.alloc(value, HEAP16, owner); ensureCache.copy(value, HEAP16, offset); return offset; } return value; } /** @suppress {duplicate} (TODO: avoid emitting this multiple times, it is redundant) */ -function ensureInt32(value) { +function ensureInt32(value, owner) { if (typeof value === 'object') { - var offset = ensureCache.alloc(value, HEAP32); + var offset = ensureCache.alloc(value, HEAP32, owner); ensureCache.copy(value, HEAP32, offset); return offset; } return value; } /** @suppress {duplicate} (TODO: avoid emitting this multiple times, it is redundant) */ -function ensureFloat32(value) { +function ensureFloat32(value, owner) { if (typeof value === 'object') { - var offset = ensureCache.alloc(value, HEAPF32); + var offset = ensureCache.alloc(value, HEAPF32, owner); ensureCache.copy(value, HEAPF32, offset); return offset; } return value; } /** @suppress {duplicate} (TODO: avoid emitting this multiple times, it is redundant) */ -function ensureFloat64(value) { +function ensureFloat64(value, owner) { if (typeof value === 'object') { - var offset = ensureCache.alloc(value, HEAPF64); + var offset = ensureCache.alloc(value, HEAPF64, owner); ensureCache.copy(value, HEAPF64, offset); return offset; } @@ -286,22 +314,27 @@ def build_constructor(name): mid_c += [''' // Not using size_t for array indices as the values used by the javascript code are signed. + +EM_JS(void, array_bounds_check_error, (size_t idx, size_t size), { + throw 'Array index ' + idx + ' out of bounds: [0,' + size + ')'; +}); + void array_bounds_check(const int array_size, const int array_idx) { if (array_idx < 0 || array_idx >= array_size) { - EM_ASM({ - throw 'Array index ' + $0 + ' out of bounds: [0,' + $1 + ')'; - }, array_idx, array_size); + array_bounds_check_error(array_idx, array_size); } } '''] C_FLOATS = ['float', 'double'] + def full_typename(arg): return ('const ' if arg.getExtendedAttribute('Const') else '') + arg.type.name + ('[]' if arg.type.isArray() else '') + def type_to_c(t, non_pointing=False): - #print 'to c ', t + # print 'to c ', t def base_type_to_c(t): if t == 'Long': ret = 'int' @@ -353,31 +386,37 @@ def base_type_to_c(t): prefix = 'const ' return prefix + base_type_to_c(t) + suffix + def take_addr_if_nonpointer(m): if m.getExtendedAttribute('Ref') or m.getExtendedAttribute('Value'): return '&' return '' + def deref_if_nonpointer(m): if m.getExtendedAttribute('Ref') or m.getExtendedAttribute('Value'): return '*' return '' + def type_to_cdec(raw): - name = ret = type_to_c(raw.type.name, non_pointing=True) - if raw.getExtendedAttribute('Const'): ret = 'const ' + ret - if raw.type.name not in interfaces: return ret + ret = type_to_c(raw.type.name, non_pointing=True) + if raw.getExtendedAttribute('Const'): + ret = 'const ' + ret + if raw.type.name not in interfaces: + return ret if raw.getExtendedAttribute('Ref'): return ret + '&' if raw.getExtendedAttribute('Value'): return ret return ret + '*' -def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, operator, constructor, func_scope, call_content=None, const=False, array_attribute=False): - global mid_c, mid_js, js_impl_methods +def render_function(class_name, func_name, sigs, return_type, non_pointer, + copy, operator, constructor, func_scope, + call_content=None, const=False, owned=False, array_attribute=False): legacy_mode = CHECKS not in ['ALL', 'FAST'] - all_checks = CHECKS == 'ALL' + all_checks = CHECKS == 'ALL' bindings_name = class_name + '_' + func_name min_args = min(sigs.keys()) @@ -399,7 +438,8 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, cache = ('getCache(%s)[this.ptr] = this;' % class_name) if constructor else '' call_prefix = '' if not constructor else 'this.ptr = ' call_postfix = '' - if return_type != 'Void' and not constructor: call_prefix = 'return ' + if return_type != 'Void' and not constructor: + call_prefix = 'return ' if not constructor: if return_type in interfaces: call_prefix += 'wrapPointer(' @@ -449,23 +489,29 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, body += " /* %s <%s> [%s] */\n" % (js_arg, arg.type.name, inner) # Wrap asserts with existence check when argument is optional. - if all_checks and optional: body += "if(typeof {0} !== 'undefined' && {0} !== null) {{\n".format(js_arg) + if all_checks and optional: + body += "if(typeof {0} !== 'undefined' && {0} !== null) {{\n".format(js_arg) # Special case argument types. if arg.type.isNumeric(): if arg.type.isInteger(): - if all_checks: body += " assert(typeof {0} === 'number' && !isNaN({0}), '{1}Expecting ');\n".format(js_arg, check_msg) + if all_checks: + body += " assert(typeof {0} === 'number' && !isNaN({0}), '{1}Expecting ');\n".format(js_arg, check_msg) else: - if all_checks: body += " assert(typeof {0} === 'number', '{1}Expecting ');\n".format(js_arg, check_msg) + if all_checks: + body += " assert(typeof {0} === 'number', '{1}Expecting ');\n".format(js_arg, check_msg) # No transform needed for numbers elif arg.type.isBoolean(): - if all_checks: body += " assert(typeof {0} === 'boolean' || (typeof {0} === 'number' && !isNaN({0})), '{1}Expecting ');\n".format(js_arg, check_msg) + if all_checks: + body += " assert(typeof {0} === 'boolean' || (typeof {0} === 'number' && !isNaN({0})), '{1}Expecting ');\n".format(js_arg, check_msg) # No transform needed for booleans elif arg.type.isString(): # Strings can be DOM strings or pointers. - if all_checks: body += " assert(typeof {0} === 'string' || ({0} && typeof {0} === 'object' && typeof {0}.ptr === 'number'), '{1}Expecting ');\n".format(js_arg, check_msg) + if all_checks: + body += " assert(typeof {0} === 'string' || ({0} && typeof {0} === 'object' && typeof {0}.ptr === 'number'), '{1}Expecting ');\n".format(js_arg, check_msg) do_default = True # legacy path is fast enough for strings. elif arg.type.isInterface(): - if all_checks: body += " assert(typeof {0} === 'object' && typeof {0}.ptr === 'number', '{1}Expecting ');\n".format(js_arg, check_msg) + if all_checks: + body += " assert(typeof {0} === 'object' && typeof {0}.ptr === 'number', '{1}Expecting ');\n".format(js_arg, check_msg) if optional: body += " if(typeof {0} !== 'undefined' && {0} !== null) {{ {0} = {0}.ptr }};\n".format(js_arg) else: @@ -474,7 +520,8 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, else: do_default = True - if all_checks and optional: body += "}\n" + if all_checks and optional: + body += "}\n" else: do_default = True @@ -482,20 +529,20 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, if not (arg.type.isArray() and not array_attribute): body += " if ({0} && typeof {0} === 'object') {0} = {0}.ptr;\n".format(js_arg) if arg.type.isString(): - body += " else {0} = ensureString({0});\n".format(js_arg) + body += " else {0} = ensureString({0}, {1});\n".format(js_arg, "true" if (owned) else "false") else: # an array can be received here arg_type = arg.type.name if arg_type in ['Byte', 'Octet']: - body += " if (typeof {0} == 'object') {{ {0} = ensureInt8({0}); }}\n".format(js_arg) + body += " if (typeof {0} == 'object') {{ {0} = ensureInt8({0}, {1}); }}\n".format(js_arg, "true" if (owned) else "false") elif arg_type in ['Short', 'UnsignedShort']: - body += " if (typeof {0} == 'object') {{ {0} = ensureInt16({0}); }}\n".format(js_arg) + body += " if (typeof {0} == 'object') {{ {0} = ensureInt16({0}, {1}); }}\n".format(js_arg, "true" if (owned) else "false") elif arg_type in ['Long', 'UnsignedLong']: - body += " if (typeof {0} == 'object') {{ {0} = ensureInt32({0}); }}\n".format(js_arg) + body += " if (typeof {0} == 'object') {{ {0} = ensureInt32({0}, {1}); }}\n".format(js_arg, "true" if (owned) else "false") elif arg_type == 'Float': - body += " if (typeof {0} == 'object') {{ {0} = ensureFloat32({0}); }}\n".format(js_arg) + body += " if (typeof {0} == 'object') {{ {0} = ensureFloat32({0}, {1}); }}\n".format(js_arg, "true" if (owned) else "false") elif arg_type == 'Double': - body += " if (typeof {0} == 'object') {{ {0} = ensureFloat64({0}); }}\n".format(js_arg) + body += " if (typeof {0} == 'object') {{ {0} = ensureFloat64({0}, {1}); }}\n".format(js_arg, "true" if (owned) else "false") c_names = {} for i in range(min_args, max_args): @@ -505,15 +552,16 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, body += ' %s%s(%s)%s;\n' % (call_prefix, '_' + c_names[max_args], ', '.join(pre_arg + args), call_postfix) if cache: body += ' ' + cache + '\n' - mid_js += [r'''%sfunction%s(%s) { + mid_js.append(r'''%sfunction%s(%s) { %s -};''' % (CONSTRUCTOR_CLOSURE_SUPPRESSIONS, (' ' + func_name) if constructor else '', ', '.join(args), body[:-1])] +};''' % (CONSTRUCTOR_CLOSURE_SUPPRESSIONS, (' ' + func_name) if constructor else '', ', '.join(args), body[:-1])) # C - for i in range(min_args, max_args+1): + for i in range(min_args, max_args + 1): raw = sigs.get(i) - if raw is None: continue + if raw is None: + continue sig = list(map(full_typename, raw)) if array_attribute: sig = [x.replace('[]', '') for x in sig] # for arrays, ignore that this is an array - our get/set methods operate on the elements @@ -554,7 +602,7 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, return_prefix = basic_return return_postfix = '' if non_pointer: - return_prefix += '&'; + return_prefix += '&' if copy: pre += ' static %s temp;\n' % type_to_c(return_type, non_pointing=True) return_prefix += '(temp = ' @@ -562,23 +610,23 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, c_return_type = type_to_c(return_type) maybe_const = 'const ' if const else '' - mid_c += [r''' + mid_c.append(r''' %s%s EMSCRIPTEN_KEEPALIVE %s(%s) { %s %s%s%s; } -''' % (maybe_const, type_to_c(class_name) if constructor else c_return_type, c_names[i], full_args, pre, return_prefix, call, return_postfix)] +''' % (maybe_const, type_to_c(class_name) if constructor else c_return_type, c_names[i], full_args, pre, return_prefix, call, return_postfix)) if not constructor: if i == max_args: dec_args = ', '.join([type_to_cdec(raw[j]) + ' ' + args[j] for j in range(i)]) - js_call_args = ', '.join(['%s%s' % (('(int)' if sig[j] in interfaces else '') + take_addr_if_nonpointer(raw[j]), args[j]) for j in range(i)]) + js_call_args = ', '.join(['%s%s' % (('(ptrdiff_t)' if sig[j] in interfaces else '') + take_addr_if_nonpointer(raw[j]), args[j]) for j in range(i)]) - js_impl_methods += [r''' %s %s(%s) %s { + js_impl_methods.append(r''' %s %s(%s) %s { %sEM_ASM_%s({ var self = Module['getCache'](Module['%s'])[$0]; if (!self.hasOwnProperty('%s')) throw 'a JSImplementation must implement all functions, you forgot %s::%s.'; %sself['%s'](%s)%s; - }, (int)this%s); + }, (ptrdiff_t)this%s); }''' % (c_return_type, func_name, dec_args, maybe_const, basic_return, 'INT' if c_return_type not in C_FLOATS else 'DOUBLE', class_name, @@ -587,12 +635,13 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, func_name, ','.join(['$%d' % i for i in range(1, max_args + 1)]), return_postfix, - (', ' if js_call_args else '') + js_call_args)] + (', ' if js_call_args else '') + js_call_args)) for name, interface in interfaces.items(): js_impl = interface.getExtendedAttribute('JSImplementation') - if not js_impl: continue + if not js_impl: + continue implements[name] = [js_impl[0]] # Compute the height in the inheritance tree of each node. Note that the order of interation @@ -623,11 +672,11 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, mid_js += ['\n// ' + name + '\n'] mid_c += ['\n// ' + name + '\n'] - global js_impl_methods js_impl_methods = [] cons = interface.getExtendedAttribute('Constructor') - if type(cons) == list: raise Exception('do not use "Constructor", instead create methods with the name of the interface') + if type(cons) == list: + raise Exception('do not use "Constructor", instead create methods with the name of the interface') js_impl = interface.getExtendedAttribute('JSImplementation') if js_impl: @@ -641,7 +690,8 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, mid_js += build_constructor(name) for m in interface.members: - if not m.isMethod(): continue + if not m.isMethod(): + continue constructor = m.identifier.name == name if not constructor: parent_constructor = False @@ -662,7 +712,7 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, return_type = ret.name else: assert return_type == ret.name, 'overloads must have the same return type' - for i in range(len(args)+1): + for i in range(len(args) + 1): if i == len(args) or args[i].optional: assert i not in sigs, 'overloading must differentiate by # of arguments (cannot have two signatures that differ by types but not by length)' sigs[i] = args[:i] @@ -679,13 +729,14 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, mid_js += build_constructor(name) for m in interface.members: - if not m.isAttr(): continue + if not m.isAttr(): + continue attr = m.identifier.name if m.type.isArray(): - get_sigs = { 1: [Dummy({ 'type': WebIDL.BuiltinTypes[WebIDL.IDLBuiltinType.Types.long] })] } - set_sigs = { 2: [Dummy({ 'type': WebIDL.BuiltinTypes[WebIDL.IDLBuiltinType.Types.long] }), - Dummy({ 'type': m.type })] } + get_sigs = {1: [Dummy({'type': WebIDL.BuiltinTypes[WebIDL.IDLBuiltinType.Types.long]})]} + set_sigs = {2: [Dummy({'type': WebIDL.BuiltinTypes[WebIDL.IDLBuiltinType.Types.long]}), + Dummy({'type': m.type})]} get_call_content = take_addr_if_nonpointer(m) + 'self->' + attr + '[arg0]' set_call_content = 'self->' + attr + '[arg0] = ' + deref_if_nonpointer(m) + 'arg1' if m.getExtendedAttribute('BoundsChecked'): @@ -693,8 +744,8 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, get_call_content = "(%s, %s)" % (bounds_check, get_call_content) set_call_content = "(%s, %s)" % (bounds_check, set_call_content) else: - get_sigs = { 0: [] } - set_sigs = { 1: [Dummy({ 'type': m.type })] } + get_sigs = {0: []} + set_sigs = {1: [Dummy({'type': m.type})]} get_call_content = take_addr_if_nonpointer(m) + 'self->' + attr set_call_content = 'self->' + attr + ' = ' + deref_if_nonpointer(m) + 'arg0' @@ -710,6 +761,7 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, func_scope=interface, call_content=get_call_content, const=m.getExtendedAttribute('Const'), + owned=m.getExtendedAttribute('Owner'), array_attribute=m.type.isArray()) if m.readonly: @@ -728,16 +780,16 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, func_scope=interface, call_content=set_call_content, const=m.getExtendedAttribute('Const'), + owned=m.getExtendedAttribute('Owner'), array_attribute=m.type.isArray()) mid_js += [r''' Object.defineProperty(%s.prototype, '%s', { get: %s.prototype.%s, set: %s.prototype.%s });''' % (name, attr, name, get_name, name, set_name)] - if not interface.getExtendedAttribute('NoDelete'): mid_js += [r''' %s.prototype['__destroy__'] = %s.prototype.__destroy__ = ''' % (name, name)] render_function(name, - '__destroy__', { 0: [] }, 'Void', + '__destroy__', {0: []}, 'Void', None, None, None, @@ -775,8 +827,7 @@ class %s : public %s { [namespace, identifier] = symbols if namespace in interfaces: # namespace is a class - deferred_js += ["Module['%s']['%s'] = _%s();\n" % \ - (namespace, identifier, function_id)] + deferred_js += ["Module['%s']['%s'] = _%s();\n" % (namespace, identifier, function_id)] else: # namespace is a namespace, so the enums get collapsed into the top level namespace. deferred_js += ["Module['%s'] = _%s();\n" % (identifier, function_id)] @@ -784,22 +835,25 @@ class %s : public %s { raise Exception("Illegal enum value %s" % value) mid_c += ['\n}\n\n'] -mid_js += [''' +if len(deferred_js): + mid_js += [''' (function() { function setupEnums() { %s } if (runtimeInitialized) setupEnums(); - else addOnPreMain(setupEnums); + else addOnInit(setupEnums); })(); ''' % '\n '.join(deferred_js)] # Write with open(output_base + '.cpp', 'w') as c: - for x in pre_c: c.write(x) - for x in mid_c: c.write(x) + for x in pre_c: + c.write(x) + for x in mid_c: + c.write(x) with open(output_base + '.js', 'w') as js: - for x in mid_js: js.write(x) - + for x in mid_js: + js.write(x) diff --git a/src/SubtitleOctopus.idl b/src/SubtitleOctopus.idl index 0ef5b511..5b7f033b 100644 --- a/src/SubtitleOctopus.idl +++ b/src/SubtitleOctopus.idl @@ -12,8 +12,8 @@ interface ASS_Image { [NoDelete] interface ASS_Style { - attribute DOMString Name; - attribute DOMString FontName; + [Owner] attribute DOMString Name; + [Owner] attribute DOMString FontName; attribute double FontSize; attribute unsigned long PrimaryColour; attribute unsigned long SecondaryColour; @@ -47,12 +47,12 @@ interface ASS_Event { attribute long ReadOrder; attribute long Layer; attribute long Style; - attribute DOMString Name; + [Owner] attribute DOMString Name; attribute long MarginL; attribute long MarginR; attribute long MarginV; - attribute DOMString Effect; - attribute DOMString Text; + [Owner] attribute DOMString Effect; + [Owner] attribute DOMString Text; }; [NoDelete] @@ -63,17 +63,17 @@ interface ASS_Track { attribute long max_events; [Value] attribute ASS_Style[] styles; [Value] attribute ASS_Event[] events; - attribute DOMString style_format; - attribute DOMString event_format; + [Owner] attribute DOMString style_format; + [Owner] attribute DOMString event_format; attribute long PlayResX; attribute long PlayResY; attribute double Timer; attribute long WrapStyle; attribute long ScaledBorderAndShadow; attribute long Kerning; - attribute DOMString Language; + [Owner] attribute DOMString Language; attribute long default_style; - attribute DOMString name; + [Owner] attribute DOMString name; }; enum ASS_Hinting { diff --git a/src/post-worker.js b/src/post-worker.js index 13675a89..6c01f39a 100644 --- a/src/post-worker.js +++ b/src/post-worker.js @@ -594,6 +594,7 @@ function onMessageFromMainEmscriptenThread(message) { for (var i = 0; i < self.octObj.getEventCount(); i++) { var evnt_ptr = self.octObj.track.get_events(i); var event = { + _index: i, Start: evnt_ptr.get_Start(), Duration: evnt_ptr.get_Duration(), ReadOrder: evnt_ptr.get_ReadOrder(), @@ -636,6 +637,7 @@ function onMessageFromMainEmscriptenThread(message) { for (var i = 0; i < self.octObj.getStyleCount(); i++) { var styl_ptr = self.octObj.track.get_styles(i); var style = { + _index: i, Name: styl_ptr.get_Name(), FontName: styl_ptr.get_FontName(), FontSize: styl_ptr.get_FontSize(), diff --git a/src/subtitles-octopus.js b/src/subtitles-octopus.js index 64981e04..a1d9ca4c 100644 --- a/src/subtitles-octopus.js +++ b/src/subtitles-octopus.js @@ -91,8 +91,8 @@ var SubtitlesOctopus = function (options) { // Worker if (!self.worker) { self.worker = new Worker(self.workerUrl); - self.worker.onmessage = self.onWorkerMessage; - self.worker.onerror = self.workerError; + self.worker.addEventListener('message', self.onWorkerMessage); + self.worker.addEventListener('error', self.workerError); } self.workerActive = false; self.createCanvas(); @@ -390,13 +390,9 @@ var SubtitlesOctopus = function (options) { break; } case 'get-events': { - console.log(data.target); - console.log(data.events); break; } case 'get-styles': { - console.log(data.target); - console.log(data.styles); break; } case 'ready': { @@ -558,6 +554,39 @@ var SubtitlesOctopus = function (options) { } }; + self.fetchFromWorker = function (workerOptions, onSuccess, onError) { + try { + var target = workerOptions['target'] + + var timeout = setTimeout(function() { + reject(Error('Error: Timeout while try to fetch ' + target)) + }, 5000) + + var resolve = function (event) { + if (event.data.target == target) { + onSuccess(event.data) + self.worker.removeEventListener('message', resolve) + self.worker.removeEventListener('error', reject) + clearTimeout(timeout) + } + } + + var reject = function (event) { + onError(event) + self.worker.removeEventListener('message', resolve) + self.worker.removeEventListener('error', reject) + clearTimeout(timeout) + } + + self.worker.addEventListener('message', resolve) + self.worker.addEventListener('error', reject) + + self.worker.postMessage(workerOptions) + } catch (error) { + onError(error) + } + } + self.createEvent = function (event) { self.worker.postMessage({ target: 'create-event', @@ -565,10 +594,12 @@ var SubtitlesOctopus = function (options) { }); }; - self.getEvents = function () { - self.worker.postMessage({ + self.getEvents = function (onSuccess, onError) { + self.fetchFromWorker({ target: 'get-events' - }); + }, function(data) { + onSuccess(data.events) + }, onError); }; self.setEvent = function (event, index) { @@ -592,11 +623,13 @@ var SubtitlesOctopus = function (options) { style: style }); }; - - self.getStyles = function () { - self.worker.postMessage({ + + self.getStyles = function (onSuccess, onError) { + self.fetchFromWorker({ target: 'get-styles' - }); + }, function(data) { + onSuccess(data.styles) + }, onError); }; self.setStyle = function (style, index) {