From ed8d9a5336660fe2e5929ab0dbbce282b13df973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thiago=20Fran=C3=A7a=20da=20Silva?= Date: Mon, 14 Feb 2022 22:17:44 -0300 Subject: [PATCH] WebIDL: Update webidl_binder from upstream Use as base this files: https://github.com/emscripten-core/emscripten/blob/f36f9fcaf83db93e6a6d0f0cdc47ab6379ade139/third_party/WebIDL.py https://github.com/emscripten-core/emscripten/blob/c834ef7d69ccb4100239eeba0b0f6573fed063bc/tools/tempfiles.py https://github.com/emscripten-core/emscripten/blob/f36f9fcaf83db93e6a6d0f0cdc47ab6379ade139/tools/webidl_binder.py --- build/WebIDL.py | 14 +++- build/tempfiles.py | 41 +++++----- build/webidl_binder.py | 182 ++++++++++++++++++++++++----------------- 3 files changed, 135 insertions(+), 102 deletions(-) diff --git a/build/WebIDL.py b/build/WebIDL.py index 62f3c5a3..d140f8fb 100644 --- a/build/WebIDL.py +++ b/build/WebIDL.py @@ -1,4 +1,9 @@ -# from http://mxr.mozilla.org/mozilla-central/source/dom/bindings/parser/WebIDL.py +## 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 # rev 501baeb3a034 # This Source Code Form is subject to the terms of the Mozilla Public @@ -4923,11 +4928,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/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..faedf10c 100644 --- a/build/webidl_binder.py +++ b/build/webidl_binder.py @@ -1,28 +1,26 @@ +## 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. # 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 -''' +"""WebIDL binder -from __future__ import print_function -import os, sys - -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 +30,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 +75,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 +90,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 +112,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]; @@ -213,15 +217,15 @@ def build_constructor(name): 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]; } }, }; @@ -286,22 +290,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 +362,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, 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 +414,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 +465,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 +496,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 @@ -505,15 +528,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 +578,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 +586,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 +611,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 +648,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 +666,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 +688,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 +705,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 +720,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' @@ -732,12 +759,11 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, 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 +801,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 +809,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)