diff options
77 files changed, 18863 insertions, 642 deletions
@@ -1,4 +1,5 @@ *.diff *.pyc *~ +*.bc @@ -13,5 +13,6 @@ under the licensing terms detailed in LICENSE. * David Yip <yipdw@member.fsf.org> * Julien Hamaide <julien.hamaide@gmail.com> * Ehsan Akhgari <ehsan.akhgari@gmail.com> (copyright owned by Mozilla Foundation) - - +* Adrian Taylor <adrian@macrobug.com> +* Richard Assar <richard.assar@gmail.com> +* Nathan Hammond <emscripten@nathanhammond.com> @@ -74,10 +74,19 @@ emcc can be influenced by a few environment variables: EMMAKEN_COMPILER - The compiler to be used, if you don't want the default clang. ''' -import os, sys, shutil, tempfile -from subprocess import Popen, PIPE, STDOUT +import os, sys, shutil, tempfile, subprocess +from subprocess import PIPE, STDOUT from tools import shared +def execute(cmd, *args, **kw): + try: + return subprocess.Popen(cmd, *args, **kw).communicate() # let compiler frontend print directly, so colors are saved (PIPE kills that) + except: + if not isinstance(cmd, str): + cmd = ' '.join(cmd) + print >> sys.stderr, 'Invoking Process failed: <<< ' + cmd + ' >>>' + raise + # Mapping of emcc opt levels to llvm opt levels. We use llvm opt level 3 in emcc opt # levels 2 and 3 (emcc 3 is unsafe opts, so unsuitable for the only level to get # llvm opt level 3, and speed-wise emcc level 2 is already the slowest/most optimizing @@ -99,8 +108,11 @@ LEAVE_INPUTS_RAW = os.environ.get('EMCC_LEAVE_INPUTS_RAW') # Do not compile .ll AUTODEBUG = os.environ.get('EMCC_AUTODEBUG') # If set to 1, we will run the autodebugger (the automatic debugging tool, see tools/autodebugger). # Note that this will disable inclusion of libraries. This is useful because including # dlmalloc makes it hard to compare native and js builds +EMCC_CFLAGS = os.environ.get('EMCC_CFLAGS') # Additional compiler flags that we treat as if they were passed to us on the commandline + +if DEBUG: print >> sys.stderr, '\nemcc invocation: ', ' '.join(sys.argv), (' + ' + EMCC_CFLAGS if EMCC_CFLAGS else '') +if EMCC_CFLAGS: sys.argv.append(EMCC_CFLAGS) -if DEBUG: print >> sys.stderr, 'emcc: ', ' '.join(sys.argv) if DEBUG and LEAVE_INPUTS_RAW: print >> sys.stderr, 'emcc: leaving inputs raw' stdout = PIPE if not DEBUG else None # suppress output of child processes @@ -149,7 +161,8 @@ Options that are modified or new in %s include: -O2 and then adding dangerous optimizations one by one. -s OPTION=VALUE JavaScript code generation option passed - into the emscripten compiler + into the emscripten compiler. For the + available options, see src/settings.js --typed-arrays <mode> 0: No typed arrays 1: Parallel typed arrays 2: Shared (C-like) typed arrays (default) @@ -173,6 +186,10 @@ Options that are modified or new in %s include: list of arguments, for example, <cmd> of "python processor.py" will cause a python script to be run. + --pre-js <file> A file whose contents are added before the + generated code + --post-js <file> A file whose contents are added after the + generated code --compress <on> 0: Do not compress the generated JavaScript's whitespace (default if closure compiler will not be run) @@ -181,6 +198,19 @@ Options that are modified or new in %s include: will be run). Note that this by itself will not minify the code (closure does that) + --embed-file <filename> A file to embed inside the generated + JavaScript. The compiled code will be able + to access the file in the current directory + with the same basename as given here (that is, + just the filename, without a path to it). + --ignore-dynamic-linking Normally emcc will treat dynamic linking like + static linking, by linking in the code from + the dynamic library. This fails if the same + dynamic library is linked more than once. + With this option, dynamic linking is ignored, + which allows the build system to proceed without + errors. However, you will need to manually + link to the shared libraries later on yourself. --shell-file <path> The path name to a skeleton HTML file used when generating HTML output. The shell file used needs to have this token inside it: @@ -195,7 +225,7 @@ be generated: <name>.js JavaScript (default) <name>.html HTML with embedded JavaScript <name>.bc LLVM bitcode - <name>.o LLVM bitcode + <name>.o LLVM bitcode (same as .bc) The -c option (which tells gcc not to run the linker) will cause LLVM bitcode to be generated, as %s only generates @@ -205,6 +235,9 @@ The input file(s) can be either source code files that Clang can handle (C or C++), LLVM bitcode in binary form, or LLVM assembly files in human-readable form. +emcc is affected by several environment variables. For details, view +the source of emcc (search for 'os.environ'). + ''' % (this, this, this) exit(0) @@ -240,8 +273,13 @@ if EMMAKEN_CFLAGS: CC_ADDITIONAL_ARGS += EMMAKEN_CFLAGS.split(' ') SOURCE_SUFFIXES = ('.c', '.cpp', '.cxx', '.cc') BITCODE_SUFFIXES = ('.bc', '.o') -SHAREDLIB_SUFFIXES = ('.dylib', '.so', '.dll') +DYNAMICLIB_SUFFIXES = ('.dylib', '.so', '.dll') +STATICLIB_SUFFIXES = ('.a',) ASSEMBLY_SUFFIXES = ('.ll',) +LIB_PREFIXES = ('', 'lib') + +def suffix(name): + return name.split('.')[:-1] def unsuffixed(name): return '.'.join(name.split('.')[:-1]) @@ -305,7 +343,11 @@ try: llvm_opts = None closure = None js_transform = None + pre_js = None + post_js = None compress_whitespace = None + embed_files = [] + ignore_dynamic_linking = False shell_path = shared.path_from_root('src', 'shell.html') def check_bad_eq(arg): @@ -334,17 +376,35 @@ try: js_transform = newargs[i+1] newargs[i] = '' newargs[i+1] = '' + elif newargs[i].startswith('--pre-js'): + check_bad_eq(newargs[i]) + pre_js = open(newargs[i+1]).read() + newargs[i] = '' + newargs[i+1] = '' + elif newargs[i].startswith('--post-js'): + check_bad_eq(newargs[i]) + post_js = open(newargs[i+1]).read() + newargs[i] = '' + newargs[i+1] = '' elif newargs[i].startswith('--compress'): check_bad_eq(newargs[i]) compress_whitespace = int(newargs[i+1]) newargs[i] = '' newargs[i+1] = '' + elif newargs[i].startswith('--embed-file'): + check_bad_eq(newargs[i]) + embed_files.append(newargs[i+1]) + newargs[i] = '' + newargs[i+1] = '' elif newargs[i] == '-MF': # clang cannot handle this, so we fake it f = open(newargs[i+1], 'w') f.write('\n') f.close() newargs[i] = '' newargs[i+1] = '' + elif newargs[i] == '--ignore-dynamic-linking': + ignore_dynamic_linking = True + newargs[i] = '' elif newargs[i].startswith('--shell-file'): check_bad_eq(newargs[i]) shell_path = newargs[i+1] @@ -373,12 +433,21 @@ try: newargs[i+1] = '' newargs = [ arg for arg in newargs if arg is not '' ] + # Find input files + input_files = [] has_source_inputs = False + lib_dirs = [] + libs = [] for i in range(len(newargs)): # find input files XXX this a simple heuristic. we should really analyze based on a full understanding of gcc params, # right now we just assume that what is left contains no more |-x OPT| things arg = newargs[i] - if arg.endswith(SOURCE_SUFFIXES + BITCODE_SUFFIXES + SHAREDLIB_SUFFIXES + ASSEMBLY_SUFFIXES) or shared.Building.is_ar(arg): # we already removed -o <target>, so all these should be inputs + + if i > 0: + prev = newargs[i-1] + if prev == '-MT': continue # ignore this gcc-style argument + + if arg.endswith(SOURCE_SUFFIXES + BITCODE_SUFFIXES + DYNAMICLIB_SUFFIXES + ASSEMBLY_SUFFIXES) or shared.Building.is_ar(arg): # we already removed -o <target>, so all these should be inputs newargs[i] = '' if os.path.exists(arg): if arg.endswith(SOURCE_SUFFIXES): @@ -392,9 +461,38 @@ try: print >> sys.stderr, 'emcc: %s: warning: Not valid LLVM bitcode' % arg else: print >> sys.stderr, 'emcc: %s: warning: No such file or directory' % arg + elif arg.startswith('-L'): + lib_dirs.append(arg[2:]) + newargs[i] = '' + elif arg.startswith('-l'): + libs.append(arg[2:]) + newargs[i] = '' newargs = [ arg for arg in newargs if arg is not '' ] - assert len(input_files) > 0, 'emcc: no input files' + # Find library files + for lib in libs: + if DEBUG: print >> sys.stderr, 'emcc: looking for library "%s"' % lib + found = False + for prefix in LIB_PREFIXES: + for suff in STATICLIB_SUFFIXES + DYNAMICLIB_SUFFIXES: + name = prefix + lib + suff + for lib_dir in lib_dirs: + path = os.path.join(lib_dir, name) + if os.path.exists(path): + if DEBUG: print >> sys.stderr, 'emcc: found library "%s" at %s' % (lib, path) + input_files.append(path) + found = True + break + if found: break + if found: break + + if ignore_dynamic_linking: + input_files = filter(lambda input_file: not input_file.endswith(DYNAMICLIB_SUFFIXES), input_files) + + if len(input_files) == 0: + print >> sys.stderr, 'emcc: no input files' + print >> sys.stderr, 'note that input files without a known suffix are ignored, make sure your input files end with one of: ' + str(SOURCE_SUFFIXES + BITCODE_SUFFIXES + DYNAMICLIB_SUFFIXES + STATICLIB_SUFFIXES + ASSEMBLY_SUFFIXES) + exit(0) newargs += CC_ADDITIONAL_ARGS @@ -436,7 +534,7 @@ try: temp_files.append(output_file) args = newargs + ['-emit-llvm', '-c', input_file, '-o', output_file] if DEBUG: print >> sys.stderr, "emcc running:", call, ' '.join(args) - Popen([call] + args).communicate() # let compiler frontend print directly, so colors are saved (PIPE kills that) + execute([call] + args) # let compiler frontend print directly, so colors are saved (PIPE kills that) if not os.path.exists(output_file): print >> sys.stderr, 'emcc: compiler frontend failed to generate LLVM bitcode, halting' sys.exit(1) @@ -446,7 +544,7 @@ try: temp_file = in_temp(unsuffixed_basename(input_file) + '.o') shutil.copyfile(input_file, temp_file) temp_files.append(temp_file) - elif input_file.endswith(SHAREDLIB_SUFFIXES) or shared.Building.is_ar(input_file): + elif input_file.endswith(DYNAMICLIB_SUFFIXES) or shared.Building.is_ar(input_file): if DEBUG: print >> sys.stderr, 'emcc: copying library file: ', input_file temp_file = in_temp(os.path.basename(input_file)) shutil.copyfile(input_file, temp_file) @@ -460,6 +558,8 @@ try: shared.Building.llvm_as(input_file, temp_file) temp_files.append(temp_file) + if not LEAVE_INPUTS_RAW: assert len(temp_files) == len(input_files) + # If we were just asked to generate bitcode, stop there if final_suffix not in ['js', 'html']: if llvm_opts > 0: @@ -477,7 +577,7 @@ try: ld_args = temp_files + ['-b', specified_target] #[arg.split('-Wl,')[1] for arg in filter(lambda arg: arg.startswith('-Wl,'), sys.argv)] if DEBUG: print >> sys.stderr, 'emcc: link: ' + str(ld_args) - Popen([shared.LLVM_LD, '-disable-opt'] + ld_args).communicate() + execute([shared.LLVM_LD, '-disable-opt'] + ld_args) exit(0) ## Continue on to create JavaScript @@ -495,9 +595,9 @@ try: # dlmalloc def create_dlmalloc(): if DEBUG: print >> sys.stderr, 'emcc: building dlmalloc for cache' - Popen([shared.EMCC, shared.path_from_root('system', 'lib', 'dlmalloc.c'), '-g', '-o', in_temp('dlmalloc.o')], stdout=stdout, stderr=stderr).communicate() + execute([shared.EMCC, shared.path_from_root('system', 'lib', 'dlmalloc.c'), '-g', '-o', in_temp('dlmalloc.o')], stdout=stdout, stderr=stderr) # we include the libc++ new stuff here, so that the common case of using just new/delete is quick to link - Popen([shared.EMXX, shared.path_from_root('system', 'lib', 'libcxx', 'new.cpp'), '-g', '-o', in_temp('new.o')], stdout=stdout, stderr=stderr).communicate() + execute([shared.EMXX, shared.path_from_root('system', 'lib', 'libcxx', 'new.cpp'), '-g', '-o', in_temp('new.o')], stdout=stdout, stderr=stderr) shared.Building.link([in_temp('dlmalloc.o'), in_temp('new.o')], in_temp('dlmalloc_full.o')) return in_temp('dlmalloc_full.o') def fix_dlmalloc(): @@ -528,14 +628,28 @@ try: libcxx_symbols = filter(lambda symbol: symbol not in dlmalloc_symbols, libcxx_symbols) libcxx_symbols = set(libcxx_symbols) + # libcxxabi - just for dynamic_cast for now + def create_libcxxabi(): + if DEBUG: print >> sys.stderr, 'emcc: building libcxxabi for cache' + shared.Building.build_library('libcxxabi', shared.EMSCRIPTEN_TEMP_DIR, shared.EMSCRIPTEN_TEMP_DIR, ['libcxxabi.bc'], configure=None, copy_project=True, source_dir=shared.path_from_root('system', 'lib', 'libcxxabi')) + return os.path.join(shared.EMSCRIPTEN_TEMP_DIR, 'libcxxabi', 'libcxxabi.bc') + def fix_libcxxabi(): + assert shared.Settings.QUANTUM_SIZE == 4, 'We do not support libc++abi with QUANTUM_SIZE == 1' + print >> sys.stderr, 'emcc: warning: using libcxxabi, this may need CORRECT_* options' + #shared.Settings.CORRECT_SIGNS = shared.Settings.CORRECT_OVERFLOWS = shared.Settings.CORRECT_ROUNDINGS = 1 + libcxxabi_symbols = map(lambda line: line.strip().split(' ')[1], open(shared.path_from_root('system', 'lib', 'libcxxabi', 'symbols')).readlines()) + libcxxabi_symbols = filter(lambda symbol: symbol not in dlmalloc_symbols, libcxxabi_symbols) + libcxxabi_symbols = set(libcxxabi_symbols) + force = False # If we have libcxx, we must force inclusion of dlmalloc, since libcxx uses new internally. Note: this is kind of hacky - for name, create, fix, library_symbols in [('libcxx', create_libcxx, fix_libcxx, libcxx_symbols), - ('dlmalloc', create_dlmalloc, fix_dlmalloc, dlmalloc_symbols)]: + for name, create, fix, library_symbols in [('libcxx', create_libcxx, fix_libcxx, libcxx_symbols), + ('libcxxabi', create_libcxxabi, fix_libcxxabi, libcxxabi_symbols), + ('dlmalloc', create_dlmalloc, fix_dlmalloc, dlmalloc_symbols)]: need = [] has = [] - for input_file in input_files: - symbols = shared.Building.llvm_nm(in_temp(unsuffixed_basename(input_file) + '.o')) + for temp_file in temp_files: + symbols = shared.Building.llvm_nm(temp_file) for library_symbol in library_symbols: if library_symbol in symbols.undefs: need.append(library_symbol) @@ -550,16 +664,17 @@ try: if fix: fix() - # First, combine the bitcode files if there are several - if len(input_files) + len(extra_files_to_link) > 1: - linker_inputs = map(lambda input_file: in_temp(unsuffixed_basename(input_file) + '.o'), input_files) + extra_files_to_link + # First, combine the bitcode files if there are several. We must also link if we have a singleton .a + if len(input_files) + len(extra_files_to_link) > 1 or \ + (not LEAVE_INPUTS_RAW and not (suffix(temp_files[0]) in BITCODE_SUFFIXES or suffix(temp_files[0]) in DYNAMICLIB_SUFFIXES) and shared.Building.is_ar(temp_files[0])): + linker_inputs = temp_files + extra_files_to_link if DEBUG: print >> sys.stderr, 'emcc: linking: ', linker_inputs shared.Building.link(linker_inputs, in_temp(target_basename + '.bc')) final = in_temp(target_basename + '.bc') else: if not LEAVE_INPUTS_RAW: - shutil.move(in_temp(unsuffixed_basename(input_files[0]) + '.o'), in_temp(target_basename + '.bc')) + shutil.move(temp_files[0], in_temp(target_basename + '.bc')) final = in_temp(target_basename + '.bc') else: final = input_files[0] @@ -604,7 +719,7 @@ try: if AUTODEBUG: if DEBUG: print >> sys.stderr, 'emcc: autodebug' - Popen(['python', shared.AUTODEBUGGER, final, final + '.ad.ll']).communicate()[0] + execute(['python', shared.AUTODEBUGGER, final, final + '.ad.ll']) final += '.ad.ll' if DEBUG: save_intermediate('autodebug', 'll') @@ -613,12 +728,31 @@ try: final = shared.Building.emscripten(final, append_ext=False) if DEBUG: save_intermediate('original') + # Embed files + if len(embed_files) > 0: + if DEBUG: print >> sys.stderr, 'emcc: embedding files' + src = open(final).read().replace( + '// {{PRE_RUN_ADDITIONS}}', + '\n'.join(map(lambda embed_file: "FS.createDataFile('/', '%s', %s, true, false);" % (os.path.basename(embed_file), str(map(ord, open(embed_file, 'rb').read()))), embed_files)) + ) + final += '.ef.js' + open(final, 'w').write(src) + if DEBUG: save_intermediate('embedded_files') + + # Apply pre and postjs files + if pre_js or post_js: + if DEBUG: print >> sys.stderr, 'emcc: applying pre/postjses' + src = open(final).read() + final += '.pp.js' + open(final, 'w').write((pre_js or '') + src + (post_js or '')) + if DEBUG: save_intermediate('pre-post') + # Apply a source code transformation, if requested if js_transform: shutil.copyfile(final, final + '.tr.js') final += '.tr.js' if DEBUG: print >> sys.stderr, 'emcc: applying transform: %s' % js_transform - Popen(js_transform.split(' ') + [os.path.abspath(final)]).communicate() + execute(js_transform.split(' ') + [os.path.abspath(final)]) if DEBUG: save_intermediate('transformed') # It is useful to run several js optimizer passes together, to save on unneeded unparsing/reparsing @@ -654,10 +788,12 @@ try: if DEBUG: save_intermediate('eliminator') # js optimizer pre-pass - js_optimizer_queue += ['simplifyExpressionsPre', 'optimizeShiftsConservative'] - ###js_optimizer_queue += ['optimizeShiftsAggressive'] - ###final = shared.Building.eliminator(final) # aggressive shifts optimization introduces some new variables, remove ones that we can - ###if DEBUG: save_intermediate('eliminator') + js_optimizer_queue += ['simplifyExpressionsPre'] + if shared.Settings.RELOOP: + js_optimizer_queue += ['optimizeShiftsAggressive'] # aggressive shifts optimization requires loops, it breaks on switches + flush_js_optimizer_queue() + final = shared.Building.eliminator(final) # aggressive shifts optimization introduces some new variables, remove ones that we can + if DEBUG: save_intermediate('eliminator') if closure: flush_js_optimizer_queue() diff --git a/settings.py b/settings.py index e4c541f3..48eaa9ab 100644 --- a/settings.py +++ b/settings.py @@ -10,8 +10,6 @@ NODE_JS = 'node' SPIDERMONKEY_ENGINE = [os.path.expanduser('~/Dev/mozilla-central/js/src/js'), '-m', '-n'] V8_ENGINE = os.path.expanduser('~/Dev/v8/d8') -CLOSURE_COMPILER = os.path.expanduser('~/Dev/closure-compiler/compiler.jar') # optional (needed for the benchmarks) - TEMP_DIR = '/tmp' diff --git a/src/analyzer.js b/src/analyzer.js index 1c643303..8ded86f1 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -11,11 +11,8 @@ var VAR_EMULATED = 'emulated'; var ENTRY_IDENT = toNiceIdent('%0'); var ENTRY_IDENTS = set(toNiceIdent('%0'), toNiceIdent('%1')); -function cleanFunc(func) { - func.lines = func.lines.filter(function(line) { return line.intertype !== null }); - func.labels.forEach(function(label) { - label.lines = label.lines.filter(function(line) { return line.intertype !== null }); - }); +function recomputeLines(func) { + func.lines = func.labels.map(function(label) { return label.lines }).reduce(concatenator, []); } // Handy sets @@ -71,6 +68,7 @@ function analyzer(data, sidePass) { subItem.endLineNum = null; subItem.lines = []; // We will fill in the function lines after the legalizer, since it can modify them subItem.labels = []; + subItem.forceEmulated = false; // no explicit 'entry' label in clang on LLVM 2.8 - most of the time, but not all the time! - so we add one if necessary if (item.items[i+1].intertype !== 'label') { @@ -122,10 +120,6 @@ function analyzer(data, sidePass) { processItem: function(data) { // Legalization if (USE_TYPED_ARRAYS == 2) { - function isIllegalType(type) { - var bits = getBits(type); - return bits > 0 && (bits >= 64 || !isPowerOfTwo(bits)); - } function getLegalVars(base, bits) { assert(!isNumber(base)); var ret = new Array(Math.ceil(bits/32)); @@ -142,7 +136,7 @@ function analyzer(data, sidePass) { var ret = new Array(Math.ceil(bits/32)); var i = 0; while (bits > 0) { - ret[i] = { ident: parsed[i].toString(), bits: Math.min(32, bits) }; + ret[i] = { ident: (parsed[i]|0).toString(), bits: Math.min(32, bits) }; // resign all values bits -= 32; i++; } @@ -184,6 +178,13 @@ function analyzer(data, sidePass) { i++; } } + function fixUnfolded(item) { + // Unfolded items may need some correction to work properly in the global scope + if (item.intertype in MATHOPS) { + item.op = item.intertype; + item.intertype = 'mathop'; + } + } data.functions.forEach(function(func) { // Legalize function params legalizeFunctionParameters(func.params); @@ -220,10 +221,29 @@ function analyzer(data, sidePass) { // accessible through ident$x, and not constants we need to parse then and there) if (subItem != item && (!(subItem.intertype in UNUNFOLDABLE) || (subItem.intertype == 'value' && isNumber(subItem.ident) && isIllegalType(subItem.type)))) { - var tempIdent = '$$emscripten$temp$' + (tempId++); - subItem.assignTo = tempIdent; - unfolded.unshift(subItem); - return { intertype: 'value', ident: tempIdent, type: subItem.type }; + if (item.intertype == 'phi') { + assert(subItem.intertype == 'value', 'We can only unfold illegal constants in phis'); + // we must handle this in the phi itself, if we unfold normally it will not be pushed back with the phi + } else { + var tempIdent = '$$emscripten$temp$' + (tempId++); + subItem.assignTo = tempIdent; + unfolded.unshift(subItem); + fixUnfolded(subItem); + return { intertype: 'value', ident: tempIdent, type: subItem.type }; + } + } else if (subItem.intertype == 'switch' && isIllegalType(subItem.type)) { + subItem.switchLabels.forEach(function(switchLabel) { + if (switchLabel.value[0] != '$') { + var tempIdent = '$$emscripten$temp$' + (tempId++); + unfolded.unshift({ + assignTo: tempIdent, + intertype: 'value', + ident: switchLabel.value, + type: subItem.type + }); + switchLabel.value = tempIdent; + } + }); } }); if (unfolded.length > 0) { @@ -270,7 +290,7 @@ function analyzer(data, sidePass) { continue; } // call, return: Return value is in an unlegalized array literal. Not fully optimal. - case 'call': case 'invoke': { + case 'call': { bits = getBits(value.type); var elements = getLegalVars(item.assignTo, bits); var toAdd = [value]; @@ -298,6 +318,12 @@ function analyzer(data, sidePass) { i++; continue; } + case 'invoke': { + legalizeFunctionParameters(value.params); + // We can't add lines after this, since invoke already modifies control flow. So we handle the return in invoke + i++; + continue; + } case 'value': { bits = getBits(value.type); var elements = getLegalVars(item.assignTo, bits); @@ -355,6 +381,12 @@ function analyzer(data, sidePass) { var toAdd = []; var elements = getLegalVars(item.assignTo, bits); var j = 0; + var literalValues = {}; // special handling of literals - we cannot unfold them normally + value.params.map(function(param) { + if (isNumber(param.value.ident)) { + literalValues[param.value.ident] = getLegalLiterals(param.value.ident, bits); + } + }); elements.forEach(function(element) { toAdd.push({ intertype: 'phi', @@ -366,7 +398,7 @@ function analyzer(data, sidePass) { label: param.label, value: { intertype: 'value', - ident: param.value.ident + '$' + j, + ident: (param.value.ident in literalValues) ? literalValues[param.value.ident][j].ident : (param.value.ident + '$' + j), type: 'i' + element.bits, } }; @@ -377,6 +409,10 @@ function analyzer(data, sidePass) { i += removeAndAdd(label.lines, i, toAdd); continue; } + case 'switch': { + i++; + continue; // special case, handled in makeComparison + } case 'bitcast': { var inType = item.type2; var outType = item.type; @@ -387,16 +423,18 @@ function analyzer(data, sidePass) { } // fall through } - case 'inttoptr': case 'ptrtoint': { + case 'inttoptr': case 'ptrtoint': case 'zext': case 'sext': case 'trunc': case 'ashr': case 'lshr': case 'shl': case 'or': case 'and': case 'xor': { value = { op: item.intertype, - param1: item.params[0] + variant: item.variant, + type: item.type, + params: item.params }; // fall through } case 'mathop': { var toAdd = []; - var sourceBits = getBits(value.param1.type); + var sourceBits = getBits(value.params[0].type); // All mathops can be parametrized by how many shifts we do, and how big the source is var shifts = 0; var targetBits = sourceBits; @@ -408,11 +446,11 @@ function analyzer(data, sidePass) { // fall through } case 'lshr': { - shifts = parseInt(value.param2.ident); + shifts = parseInt(value.params[1].ident); break; } case 'shl': { - shifts = -parseInt(value.param2.ident); + shifts = -parseInt(value.params[1].ident); break; } case 'sext': { @@ -420,7 +458,7 @@ function analyzer(data, sidePass) { // fall through } case 'trunc': case 'zext': case 'ptrtoint': { - targetBits = getBits(value.param2.ident); + targetBits = getBits(value.params[1] ? value.params[1].ident : value.type); break; } case 'inttoptr': { @@ -431,31 +469,35 @@ function analyzer(data, sidePass) { break; } case 'select': { - sourceBits = targetBits = getBits(value.param2.type); - var otherElementsA = getLegalVars(value.param2.ident, sourceBits); - var otherElementsB = getLegalVars(value.param3.ident, sourceBits); + sourceBits = targetBits = getBits(value.params[1].type); + var otherElementsA = getLegalVars(value.params[1].ident, sourceBits); + var otherElementsB = getLegalVars(value.params[2].ident, sourceBits); processor = function(result, j) { return { intertype: 'mathop', op: 'select', type: 'i' + otherElementsA[j].bits, - param1: value.param1, - param2: { intertype: 'value', ident: otherElementsA[j].ident, type: 'i' + otherElementsA[j].bits }, - param3: { intertype: 'value', ident: otherElementsB[j].ident, type: 'i' + otherElementsB[j].bits } + params: [ + value.params[0], + { intertype: 'value', ident: otherElementsA[j].ident, type: 'i' + otherElementsA[j].bits }, + { intertype: 'value', ident: otherElementsB[j].ident, type: 'i' + otherElementsB[j].bits } + ] }; }; break; } case 'or': case 'and': case 'xor': { - var otherElements = getLegalVars(value.param2.ident, sourceBits); + var otherElements = getLegalVars(value.params[1].ident, sourceBits); processor = function(result, j) { return { intertype: 'mathop', op: value.op, type: 'i' + otherElements[j].bits, - param1: result, - param2: { intertype: 'value', ident: otherElements[j].ident, type: 'i' + otherElements[j].bits } - }; + params: [ + result, + { intertype: 'value', ident: otherElements[j].ident, type: 'i' + otherElements[j].bits } + ] + }; }; break; } @@ -471,16 +513,16 @@ function analyzer(data, sidePass) { var sourceElements; if (sourceBits <= 32) { // The input is a legal type - sourceElements = [{ ident: value.param1.ident, bits: sourceBits }]; + sourceElements = [{ ident: value.params[0].ident, bits: sourceBits }]; } else { - sourceElements = getLegalVars(value.param1.ident, sourceBits); + sourceElements = getLegalVars(value.params[0].ident, sourceBits); } if (!isNumber(shifts)) { // We can't statically legalize this, do the operation at runtime TODO: optimize assert(sourceBits == 64, 'TODO: handle nonconstant shifts on != 64 bits'); value.intertype = 'value'; value.ident = 'Runtime.bitshift64(' + sourceElements[0].ident + ', ' + - sourceElements[1].ident + ',"' + value.op + '",' + value.param2.ident + '$0);' + + sourceElements[1].ident + ',"' + value.op + '",' + value.params[1].ident + '$0);' + 'var ' + value.assignTo + '$0 = ' + value.assignTo + '[0], ' + value.assignTo + '$1 = ' + value.assignTo + '[1];'; i++; continue; @@ -499,37 +541,43 @@ function analyzer(data, sidePass) { var result = { intertype: 'value', ident: (j + whole >= 0 && j + whole < sourceElements.length) ? sourceElements[j + whole].ident : (signed ? signedFill : '0'), - param1: (signed && j + whole > sourceElements.length) ? signedKeepAlive : null, + params: [(signed && j + whole > sourceElements.length) ? signedKeepAlive : null], type: 'i32', }; if (fraction != 0) { var other = { intertype: 'value', ident: (j + sign + whole >= 0 && j + sign + whole < sourceElements.length) ? sourceElements[j + sign + whole].ident : (signed ? signedFill : '0'), - param1: (signed && j + sign + whole > sourceElements.length) ? signedKeepAlive : null, + params: [(signed && j + sign + whole > sourceElements.length) ? signedKeepAlive : null], type: 'i32', }; other = { intertype: 'mathop', op: shiftOp, type: 'i32', - param1: other, - param2: { intertype: 'value', ident: (32 - fraction).toString(), type: 'i32' } + params: [ + other, + { intertype: 'value', ident: (32 - fraction).toString(), type: 'i32' } + ] }; result = { intertype: 'mathop', // shifting in 1s from the top is a special case op: (signed && shifts >= 0 && j + sign + whole >= sourceElements.length) ? 'ashr' : shiftOpReverse, type: 'i32', - param1: result, - param2: { intertype: 'value', ident: fraction.toString(), type: 'i32' } + params: [ + result, + { intertype: 'value', ident: fraction.toString(), type: 'i32' } + ] }; result = { intertype: 'mathop', op: 'or', type: 'i32', - param1: result, - param2: other + params: [ + result, + other + ] } } if (targetElements[j].bits < 32 && shifts < 0) { @@ -538,8 +586,10 @@ function analyzer(data, sidePass) { intertype: 'mathop', op: 'and', type: 'i32', - param1: result, - param2: { intertype: 'value', ident: (Math.pow(2, targetElements[j].bits)-1).toString(), type: 'i32' } + params: [ + result, + { intertype: 'value', ident: (Math.pow(2, targetElements[j].bits)-1).toString(), type: 'i32' } + ] } } if (processor) { @@ -550,8 +600,11 @@ function analyzer(data, sidePass) { } if (targetBits <= 32) { // We are generating a normal legal type here - legalValue = { intertype: 'value', ident: targetElements[0].ident, type: 'rawJS' }; - // truncation to smaller than 32 bits has already been done, if necessary + legalValue = { + intertype: 'value', + ident: targetElements[0].ident + (targetBits < 32 ? '&' + (Math.pow(2, targetBits)-1) : ''), + type: 'rawJS' + }; legalValue.assignTo = item.assignTo; toAdd.push(legalValue); } @@ -857,7 +910,7 @@ function analyzer(data, sidePass) { variable.impl = VAR_EMULATED; } else if (variable.origin == 'funcparam') { variable.impl = VAR_EMULATED; - } else if (variable.type == 'i64*' && I64_MODE == 1) { + } else if (variable.type == 'i64*' && USE_TYPED_ARRAYS == 2) { variable.impl = VAR_EMULATED; } else if (MICRO_OPTS && variable.pointingLevels === 0) { // A simple int value, can be implemented as a native variable @@ -1098,23 +1151,65 @@ function analyzer(data, sidePass) { } }); + function operateOnLabels(line, func) { + function process(item, id) { + ['label', 'labelTrue', 'labelFalse', 'toLabel', 'unwindLabel', 'defaultLabel'].forEach(function(id) { + if (item[id]) { + func(item, id); + } + }); + } + if (line.intertype in BRANCH_INVOKE) { + process(line); + } else if (line.intertype == 'switch') { + process(line); + line.switchLabels.forEach(process); + } + } + // Label analyzer substrate.addActor('LabelAnalyzer', { processItem: function(item) { item.functions.forEach(function(func) { func.labelsDict = {}; func.labelIds = {}; - func.labelIdCounter = 0; + func.labelIdsInverse = {}; + func.labelIds[toNiceIdent('%0')] = 0; + func.labelIdsInverse[0] = toNiceIdent('%0'); + func.labelIdCounter = 1; func.labels.forEach(function(label) { - func.labelsDict[label.ident] = label; func.labelIds[label.ident] = func.labelIdCounter++; + func.labelIdsInverse[func.labelIdCounter-1] = label.ident; + }); + + // Minify label ids to numeric ids. + func.labels.forEach(function(label) { + label.ident = func.labelIds[label.ident]; + label.lines.forEach(function(line) { + operateOnLabels(line, function(item, id) { + item[id] = func.labelIds[item[id]].toString(); // strings, because we will append as we process + }); + }); + }); + + func.labels.forEach(function(label) { + func.labelsDict[label.ident] = label; + }); + + // Correct phis + func.labels.forEach(function(label) { + label.lines.forEach(function(phi) { + if (phi.intertype == 'phi') { + for (var i = 0; i < phi.params.length; i++) { + phi.params[i].label = func.labelIds[phi.params[i].label]; + } + } + }); }); - func.labelIds[toNiceIdent('%0')] = -1; // entry is always -1 - func.hasIndirectBr = false; func.lines.forEach(function(line) { if (line.intertype == 'indirectbr') { - func.hasIndirectBr = true; + func.forceEmulated = true; } }); @@ -1129,6 +1224,52 @@ function analyzer(data, sidePass) { return null; } + // Basic longjmp support, see library.js setjmp/longjmp + var setjmp = toNiceIdent('@setjmp'); + func.setjmpTable = null; + for (var i = 0; i < func.labels.length; i++) { + var label = func.labels[i]; + for (var j = 0; j < label.lines.length; j++) { + var line = label.lines[j]; + if (line.intertype == 'call' && line.ident == setjmp) { + // Add a new label + var oldIdent = label.ident; + var newIdent = func.labelIdCounter++; + if (!func.setjmpTable) func.setjmpTable = []; + func.setjmpTable.push([oldIdent, newIdent, line.assignTo]); + func.labels.splice(i+1, 0, { + intertype: 'label', + ident: newIdent, + lineNum: label.lineNum + 0.5, + lines: label.lines.slice(j+1) + }); + label.lines = label.lines.slice(0, j+1); + label.lines.push({ + intertype: 'branch', + label: toNiceIdent(newIdent), + lineNum: line.lineNum + 0.01, // XXX legalizing might confuse this + }); + // Correct phis + func.labels.forEach(function(label) { + label.lines.forEach(function(phi) { + if (phi.intertype == 'phi') { + for (var i = 0; i < phi.params.length; i++) { + var sourceLabelId = getActualLabelId(phi.params[i].label); + if (sourceLabelId == oldIdent) { + phi.params[i].label = newIdent; + } + } + } + }); + }); + } + } + } + if (func.setjmpTable) { + func.forceEmulated = true; + recomputeLines(func); + } + if (!MICRO_OPTS) { // 'Emulate' phis, by doing an if where the phi appears in the .ll. For this // we need __lastLabel__. @@ -1223,8 +1364,7 @@ function analyzer(data, sidePass) { var lines = func.labels[0].lines; for (var i = 0; i < lines.length; i++) { var item = lines[i]; - if (!item.assignTo || item.intertype != 'alloca') break; - assert(isNumber(item.allocatedNum)); + if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum)) break; item.allocatedSize = func.variables[item.assignTo].impl === VAR_EMULATED ? calcAllocatedSize(item.allocatedType)*item.allocatedNum: 0; if (USE_TYPED_ARRAYS === 2) { @@ -1235,7 +1375,7 @@ function analyzer(data, sidePass) { var index = 0; for (var i = 0; i < lines.length; i++) { var item = lines[i]; - if (!item.assignTo || item.intertype != 'alloca') break; + if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum)) break; item.allocatedIndex = index; index += item.allocatedSize; delete item.allocatedSize; @@ -1260,7 +1400,7 @@ function analyzer(data, sidePass) { var finishedInitial = false; for (var i = 0; i < lines.length; i++) { var item = lines[i]; - if (!item.assignTo || item.intertype != 'alloca') { + if (!item.assignTo || item.intertype != 'alloca' || !isNumber(item.allocatedNum)) { finishedInitial = true; continue; } @@ -1288,22 +1428,6 @@ function analyzer(data, sidePass) { } }); - function operateOnLabels(line, func) { - function process(item, id) { - ['label', 'labelTrue', 'labelFalse', 'toLabel', 'unwindLabel', 'defaultLabel'].forEach(function(id) { - if (item[id]) { - func(item, id); - } - }); - } - if (line.intertype in BRANCH_INVOKE) { - process(line); - } else if (line.intertype == 'switch') { - process(line); - line.switchLabels.forEach(process); - } - } - //! @param toLabelId If false, just a dry run - useful to search for labels function replaceLabels(line, labelIds, toLabelId) { var ret = []; @@ -1394,7 +1518,7 @@ function analyzer(data, sidePass) { label.allOutLabels = []; }); - // First, find allInLabels + // First, find allInLabels. TODO: use typed arrays here to optimize this for memory and speed var more = true, nextModified, modified = set(getLabelIds(labels)); while (more) { more = false; @@ -1445,7 +1569,7 @@ function analyzer(data, sidePass) { var idCounter = 0; function makeBlockId(entries) { idCounter++; - return entries.join('$') + '$' + idCounter; + return '$_$' + idCounter; } // There are X main kinds of blocks: @@ -1745,7 +1869,7 @@ function analyzer(data, sidePass) { // TODO: each of these can be run in parallel item.functions.forEach(function(func) { dprint('relooping', "// relooping function: " + func.ident); - func.block = makeBlock(func.labels, [toNiceIdent(func.labels[0].ident)], func.labelsDict, func.hasIndirectBr); + func.block = makeBlock(func.labels, [toNiceIdent(func.labels[0].ident)], func.labelsDict, func.forceEmulated); }); return finish(); diff --git a/src/compiler.js b/src/compiler.js index d37bc68b..2e95552d 100644 --- a/src/compiler.js +++ b/src/compiler.js @@ -19,6 +19,7 @@ var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIR if (ENVIRONMENT_IS_NODE) { // Expose functionality in the same simple way that the shells work + // Note that we pollute the global namespace here, otherwise we break in node print = function(x) { process['stdout'].write(x + '\n'); }; @@ -37,26 +38,30 @@ if (ENVIRONMENT_IS_NODE) { return ret; }; + load = function(f) { + globalEval(read(f)); + }; + arguments_ = process['argv'].slice(2); } else if (ENVIRONMENT_IS_SHELL) { // Polyfill over SpiderMonkey/V8 differences if (!this['read']) { - read = function(f) { snarf(f) }; + this['read'] = function(f) { snarf(f) }; } - if (!this['arguments']) { + if (typeof scriptArgs != 'undefined') { arguments_ = scriptArgs; - } else { + } else if (typeof arguments != 'undefined') { arguments_ = arguments; } } else if (ENVIRONMENT_IS_WEB) { - print = printErr = function(x) { + this['print'] = printErr = function(x) { console.log(x); }; - read = function(url) { + this['read'] = function(url) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); xhr.send(null); @@ -69,7 +74,7 @@ if (ENVIRONMENT_IS_NODE) { } else if (ENVIRONMENT_IS_WORKER) { // We can do very little here... - load = importScripts; + this['load'] = importScripts; } else { throw 'Unknown runtime environment. Where are we?'; @@ -80,17 +85,17 @@ function globalEval(x) { } if (typeof load == 'undefined' && typeof read != 'undefined') { - load = function(f) { + this['load'] = function(f) { globalEval(read(f)); }; } if (typeof printErr === 'undefined') { - printErr = function(){}; + this['printErr'] = function(){}; } if (typeof print === 'undefined') { - print = printErr; + this['print'] = printErr; } // *** Environment setup code *** @@ -138,7 +143,6 @@ EXPORTED_GLOBALS = set(EXPORTED_GLOBALS); // Settings sanity checks assert(!(USE_TYPED_ARRAYS === 2 && QUANTUM_SIZE !== 4), 'For USE_TYPED_ARRAYS == 2, must have normal QUANTUM_SIZE of 4'); -assert(!(USE_TYPED_ARRAYS !== 2 && I64_MODE === 1), 'i64 mode 1 is only supported with typed arrays mode 2'); // Output some info and warnings based on settings @@ -149,7 +153,7 @@ if (!MICRO_OPTS || !RELOOP || ASSERTIONS || CHECK_SIGNS || CHECK_OVERFLOWS || IN print('// Note: For maximum-speed code, see "Optimizing Code" on the Emscripten wiki, https://github.com/kripken/emscripten/wiki/Optimizing-Code'); } -if (DOUBLE_MODE || I64_MODE || CORRECT_SIGNS || CORRECT_OVERFLOWS || CORRECT_ROUNDINGS) { +if (DOUBLE_MODE || CORRECT_SIGNS || CORRECT_OVERFLOWS || CORRECT_ROUNDINGS) { print('// Note: Some Emscripten settings may limit the speed of the generated code.'); } diff --git a/src/intertyper.js b/src/intertyper.js index 91ad15eb..c5a9583b 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -334,6 +334,8 @@ function intertyper(data, sidePass, baseLineNums) { return 'Phi'; if (tokensLength >= 3 && token0Text == 'landingpad') return 'Landingpad'; + if (token0Text == 'fence') + return '/dev/null'; } else if (item.indent === 0) { if ((tokensLength >= 1 && token0Text.substr(-1) == ':') || (tokensLength >= 3 && token1Text == '<label>')) @@ -728,11 +730,22 @@ function intertyper(data, sidePass, baseLineNums) { this.forwardItem(item, 'Reintegrator'); } }); - // 'landingpad' - just a stub implementation + // 'landingpad' substrate.addActor('Landingpad', { processItem: function(item) { item.intertype = 'landingpad'; item.type = item.tokens[1].text; + item.catchables = []; + var catchIdx = findTokenText(item, "catch"); + if (catchIdx != -1) { + do { + var nextCatchIdx = findTokenTextAfter(item, "catch", catchIdx+1); + if (nextCatchIdx == -1) + nextCatchIdx = item.tokens.length; + item.catchables.push(parseLLVMSegment(item.tokens.slice(catchIdx+2, nextCatchIdx))); + catchIdx = nextCatchIdx; + } while (catchIdx != item.tokens.length); + } Types.needAnalysis[item.type] = 0; this.forwardItem(item, 'Reintegrator'); } @@ -789,38 +802,39 @@ function intertyper(data, sidePass, baseLineNums) { } if (item.tokens[1].text == 'exact') item.tokens.splice(1, 1); // TODO: Implement trap values var segments = splitTokenList(item.tokens.slice(1)); + item.params = []; for (var i = 1; i <= 4; i++) { if (segments[i-1]) { if (i > 1 && segments[i-1].length == 1 && segments[0].length > 1 && !isType(segments[i-1][0].text)) { segments[i-1].unshift(segments[0][0]); // Add the type from the first segment, they are all alike } - item['param'+i] = parseLLVMSegment(segments[i-1]); + item.params[i-1] = parseLLVMSegment(segments[i-1]); } } if (item.op === 'select') { - assert(item.param2.type === item.param3.type); - item.type = item.param2.type; + assert(item.params[1].type === item.params[2].type); + item.type = item.params[1].type; } else if (item.op === 'inttoptr' || item.op === 'ptrtoint') { - item.type = item.param2.type; + item.type = item.params[1].type; } else { - item.type = item.param1.type; + item.type = item.params[0].type; } if (item.op != 'ptrtoint') { - for (var i = 1; i <= 4; i++) { - if (item['param'+i]) item['param'+i].type = item.type; // All params have the same type, normally + for (var i = 0; i < 4; i++) { + if (item.params[i]) item.params[i].type = item.type; // All params have the same type, normally } } if (item.op in LLVM.EXTENDS) { - item.type = item.param2.ident; - item.param1.type = item.param2.type; + item.type = item.params[1].ident; + item.params[0].type = item.params[1].type; // TODO: also remove 2nd param? } - if (I64_MODE == 1) { + if (USE_TYPED_ARRAYS == 2) { // Some specific corrections, since 'i64' is special if (item.op in LLVM.SHIFTS) { - item.param2.type = 'i32'; + item.params[1].type = 'i32'; } else if (item.op == 'select') { - item.param1.type = 'i1'; + item.params[0].type = 'i1'; } } Types.needAnalysis[item.type] = 0; diff --git a/src/jsifier.js b/src/jsifier.js index b830fc7c..5ad1573b 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -66,7 +66,7 @@ function JSify(data, functionsOnly, givenFunctions) { assert(!BUILD_AS_SHARED_LIB, 'Cannot have both INCLUDE_FULL_LIBRARY and BUILD_AS_SHARED_LIB set.') libFuncsToInclude = []; for (var key in LibraryManager.library) { - if (!key.match(/__(deps|postset)$/)) { + if (!key.match(/__(deps|postset|inline)$/)) { libFuncsToInclude.push(key); } } @@ -167,7 +167,7 @@ function JSify(data, functionsOnly, givenFunctions) { } // Add current value(s) var currValue = flatten(values[i]); - if (I64_MODE == 1 && typeData.fields[i] == 'i64') { + if (USE_TYPED_ARRAYS == 2 && typeData.fields[i] == 'i64') { // 'flatten' out the 64-bit value into two 32-bit halves ret[index++] = currValue>>>0; ret[index++] = 0; @@ -229,7 +229,7 @@ function JSify(data, functionsOnly, givenFunctions) { return makeEmptyStruct(type); } else if (value.intertype === 'string') { return JSON.stringify(parseLLVMString(value.text)) + - ' /* ' + value.text.substr(0, 20).replace(/\*/g, '_') + ' */'; // make string safe for inclusion in comment + ' /* ' + value.text.substr(0, 20).replace(/[*<>]/g, '_') + ' */'; // make string safe for inclusion in comment } else { return alignStruct(handleSegments(value.contents), type); } @@ -574,14 +574,30 @@ function JSify(data, functionsOnly, givenFunctions) { if (block.type == 'emulated') { if (block.labels.length > 1) { if (block.entries.length == 1) { - ret += indent + '__label__ = ' + getLabelId(block.entries[0]) + '; ' + (SHOW_LABELS ? '/* ' + block.entries[0] + ' */' : '') + '\n'; + ret += indent + '__label__ = ' + getLabelId(block.entries[0]) + '; ' + (SHOW_LABELS ? '/* ' + getOriginalLabelId(block.entries[0]) + ' */' : '') + '\n'; } // otherwise, should have been set before! - ret += indent + 'while(1) switch(__label__) {\n'; + if (func.setjmpTable) { + var setjmpTable = {}; + ret += indent + 'var setjmpTable = {'; + func.setjmpTable.forEach(function(triple) { // original label, label we created for right after the setjmp, variable setjmp result goes into + ret += '"' + getLabelId(triple[0]) + '": ' + 'function(value) { __label__ = ' + getLabelId(triple[1]) + '; ' + triple[2] + ' = value },'; + }); + ret += 'dummy: 0'; + ret += '};\n'; + } + ret += indent + 'while(1) '; + if (func.setjmpTable) { + ret += 'try { '; + } + ret += 'switch(__label__) {\n'; ret += block.labels.map(function(label) { - return indent + ' case ' + getLabelId(label.ident) + ': // ' + label.ident + '\n' + return indent + ' case ' + getLabelId(label.ident) + ': ' + (SHOW_LABELS ? '// ' + getOriginalLabelId(label.ident) : '') + '\n' + getLabelLines(label, indent + ' '); }).join('\n'); ret += '\n' + indent + ' default: assert(0, "bad label: " + __label__);\n' + indent + '}'; + if (func.setjmpTable) { + ret += ' } catch(e) { if (!e.longjmp) throw(e); setjmpTable[e.label](e.value) }'; + } } else { ret += (SHOW_LABELS ? indent + '/* ' + block.entries[0] + ' */' : '') + '\n' + getLabelLines(block.labels[0], indent); } @@ -754,11 +770,14 @@ function JSify(data, functionsOnly, givenFunctions) { makeFuncLineActor('deleted', function(item) { return ';' }); - function getLabelId(label) { + function getOriginalLabelId(label) { var funcData = Framework.currItem.funcData; - var labelIds = funcData.labelIds; - if (labelIds[label] !== undefined) return labelIds[label]; - return labelIds[label] = funcData.labelIdCounter++; + var labelIdsInverse = funcData.labelIdsInverse; + return labelIdsInverse[label]; + } + + function getLabelId(label) { + return label; } function makeBranch(label, lastLabel, labelIsVariable) { @@ -772,7 +791,7 @@ function JSify(data, functionsOnly, givenFunctions) { var trueLabel = parts[1] || ''; var oldLabel = parts[2] || ''; var labelSetting = oldLabel ? '__label__ = ' + getLabelId(oldLabel) + ';' + - (SHOW_LABELS ? ' /* to: ' + cleanLabel(oldLabel) + ' */' : '') : ''; // TODO: optimize away the setting + (SHOW_LABELS ? ' /* to: ' + getOriginalLabelId(cleanLabel(oldLabel)) + ' */' : '') : ''; // TODO: optimize away the setting if (label[1] == 'R') { if (label[2] == 'N') { // BRNOL: break, no label setting labelSetting = ''; @@ -792,7 +811,7 @@ function JSify(data, functionsOnly, givenFunctions) { } } else { if (!labelIsVariable) label = getLabelId(label); - return pre + '__label__ = ' + label + ';' + (SHOW_LABELS ? ' /* to: ' + cleanLabel(label) + ' */' : '') + ' break;'; + return pre + '__label__ = ' + label + ';' + (SHOW_LABELS ? ' /* to: ' + getOriginalLabelId(cleanLabel(label)) + ' */' : '') + ' break;'; } } @@ -902,13 +921,16 @@ function JSify(data, functionsOnly, givenFunctions) { }); var ret = ''; var first = true; + var signedIdent = makeSignOp(item.ident, item.type, 're'); // we need to standardize for purpose of comparison for (var targetLabel in targetLabels) { if (!first) { ret += 'else '; } else { first = false; } - ret += 'if (' + targetLabels[targetLabel].map(function(value) { return item.ident + ' == ' + value }).join(' || ') + ') {\n'; + ret += 'if (' + targetLabels[targetLabel].map(function(value) { + return makeComparison(signedIdent, makeSignOp(value, item.type, 're'), item.type) + }).join(' || ') + ') {\n'; ret += ' ' + getPhiSetsForLabel(phiSets, targetLabel) + makeBranch(targetLabel, item.currLabelId || null) + '\n'; ret += '}\n'; } @@ -953,7 +975,16 @@ function JSify(data, functionsOnly, givenFunctions) { + 'if (typeof e != "number") throw e; ' + 'if (ABORT) throw e; __THREW__ = true; ' + (EXCEPTION_DEBUG ? 'print("Exception: " + e + ", currently at: " + (new Error().stack)); ' : '') - + 'return null } })(); if (!__THREW__) { ' + getPhiSetsForLabel(phiSets, item.toLabel) + makeBranch(item.toLabel, item.currLabelId) + + 'return null } })();'; + if (item.assignTo) { + ret = 'var ' + item.assignTo + ' = ' + ret; + if (isIllegalType(item.type)) { + assert(item.type == 'i64', 'Can only handle i64 invoke among illegal invokes'); + ret += 'var ' + item.assignTo + '$0 = ' + item.assignTo + '[0], ' + item.assignTo + '$1 = ' + item.assignTo + '[1];'; + } + item.assignTo = null; + } + ret += 'if (!__THREW__) { ' + getPhiSetsForLabel(phiSets, item.toLabel) + makeBranch(item.toLabel, item.currLabelId) + ' } else { ' + getPhiSetsForLabel(phiSets, item.unwindLabel) + makeBranch(item.unwindLabel, item.currLabelId) + ' }'; return ret; }); @@ -963,6 +994,7 @@ function JSify(data, functionsOnly, givenFunctions) { var param2 = finalizeLLVMParameter(item.params[1]); switch (item.op) { case 'add': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue+' + param2, type) + ',tempValue)'; + case 'sub': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, 'tempValue-' + param2, type) + ',tempValue)'; case 'xchg': return '(tempValue=' + makeGetValue(param1, 0, type) + ',' + makeSetValue(param1, 0, param2, type) + ',tempValue)'; case 'cmpxchg': { var param3 = finalizeLLVMParameter(item.params[2]); @@ -972,9 +1004,8 @@ function JSify(data, functionsOnly, givenFunctions) { } }); makeFuncLineActor('landingpad', function(item) { - // Just a stub - return '{ f0: ' + makeGetValue('_llvm_eh_exception.buf', '0', 'void*') + - ', f1:' + makeGetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'void*') + ' }'; + var catchTypeArray = item.catchables.map(finalizeLLVMParameter).join(','); + return '___cxa_find_matching_catch('+ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') +',' + makeGetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'void*') + ',[' + catchTypeArray +'])'; }); makeFuncLineActor('load', function(item) { var value = finalizeLLVMParameter(item.pointer); @@ -1030,10 +1061,14 @@ function JSify(data, functionsOnly, givenFunctions) { makeFuncLineActor('mathop', processMathop); makeFuncLineActor('bitcast', function(item) { - return processMathop({ + var temp = { op: 'bitcast', variant: null, type: item.type, - param1: item.params[0] - }); + assignTo: item.assignTo, + params: [item.params[0]] // XXX + }; + var ret = processMathop(temp); + if (!temp.assignTo) item.assignTo = null; // If the assign was stolen, propagate that + return ret; }); function makeFunctionCall(ident, params, funcData, type) { diff --git a/src/library.js b/src/library.js index aee4a416..7cda8bbb 100644 --- a/src/library.js +++ b/src/library.js @@ -219,8 +219,8 @@ LibraryManager.library = { // Creates a file record from existing data. createDataFile: function(parent, name, data, canRead, canWrite) { if (typeof data === 'string') { - var dataArray = []; - for (var i = 0; i < data.length; i++) dataArray.push(data.charCodeAt(i)); + var dataArray = new Array(data.length); + for (var i = 0, len = data.length; i < len; ++i) dataArray[i] = data.charCodeAt(i); data = dataArray; } var properties = {isDevice: false, contents: data}; @@ -309,34 +309,52 @@ LibraryManager.library = { FS.ensureRoot(); + // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here + input = input || Module['stdin']; + output = output || Module['stdout']; + error = error || Module['stderr']; + // Default handlers. - if (!input) input = function() { - if (!input.cache || !input.cache.length) { - var result; - if (typeof window != 'undefined' && - typeof window.prompt == 'function') { - // Browser. - result = window.prompt('Input: '); - } else if (typeof readline == 'function') { - // Command line. - result = readline(); + var stdinOverridden = true, stdoutOverridden = true, stderrOverridden = true; + if (!input) { + stdinOverridden = false; + input = function() { + if (!input.cache || !input.cache.length) { + var result; + if (typeof window != 'undefined' && + typeof window.prompt == 'function') { + // Browser. + result = window.prompt('Input: '); + } else if (typeof readline == 'function') { + // Command line. + result = readline(); + } + if (!result) result = ''; + input.cache = intArrayFromString(result + '\n', true); } - if (!result) result = ''; - input.cache = intArrayFromString(result + '\n', true); - } - return input.cache.shift(); - }; - if (!output) output = function(val) { + return input.cache.shift(); + }; + } + function simpleOutput(val) { if (val === null || val === '\n'.charCodeAt(0)) { output.printer(output.buffer.join('')); output.buffer = []; } else { output.buffer.push(String.fromCharCode(val)); } - }; + } + if (!output) { + stdoutOverridden = false; + output = simpleOutput; + } if (!output.printer) output.printer = print; if (!output.buffer) output.buffer = []; - if (!error) error = output; + if (!error) { + stderrOverridden = false; + error = simpleOutput; + } + if (!error.printer) error.printer = print; + if (!error.buffer) error.buffer = []; // Create the temporary folder. FS.createFolder('/', 'tmp', true, true); @@ -356,6 +374,7 @@ LibraryManager.library = { isRead: true, isWrite: false, isAppend: false, + isTerminal: !stdinOverridden, error: false, eof: false, ungotten: [] @@ -367,6 +386,7 @@ LibraryManager.library = { isRead: false, isWrite: true, isAppend: false, + isTerminal: !stdoutOverridden, error: false, eof: false, ungotten: [] @@ -378,6 +398,7 @@ LibraryManager.library = { isRead: false, isWrite: true, isAppend: false, + isTerminal: !stderrOverridden, error: false, eof: false, ungotten: [] @@ -400,9 +421,9 @@ LibraryManager.library = { quit: function() { if (!FS.init.initialized) return; - // Flush any partially-printed lines in stdout and stderr - if (FS.streams[2].object.output.buffer.length > 0) FS.streams[2].object.output('\n'.charCodeAt(0)); - if (FS.streams[3].object.output.buffer.length > 0) FS.streams[3].object.output('\n'.charCodeAt(0)); + // Flush any partially-printed lines in stdout and stderr. Careful, they may have been closed + if (FS.streams[2] && FS.streams[2].object.output.buffer.length > 0) FS.streams[2].object.output('\n'.charCodeAt(0)); + if (FS.streams[3] && FS.streams[3].object.output.buffer.length > 0) FS.streams[3].object.output('\n'.charCodeAt(0)); } }, @@ -1386,9 +1407,13 @@ LibraryManager.library = { isatty: function(fildes) { // int isatty(int fildes); // http://pubs.opengroup.org/onlinepubs/000095399/functions/isatty.html - // For now it's easier to pretend we have no terminals. - ___setErrNo(FS.streams[fildes] ? ERRNO_CODES.ENOTTY : ERRNO_CODES.EBADF); - return -1; + if (!FS.streams[fildes]) { + ___setErrNo(ERRNO_CODES.EBADF); + return 0; + } + if (FS.streams[fildes].isTerminal) return 1; + ___setErrNo(ERRNO_CODES.ENOTTY); + return 0; }, lchown__deps: ['chown'], lchown: function(path, owner, group) { @@ -2298,7 +2323,7 @@ LibraryManager.library = { var ret; if (type === 'double') { ret = {{{ makeGetValue('varargs', 'argIndex', 'double', undefined, undefined, true) }}}; -#if I64_MODE == 1 +#if USE_TYPED_ARRAYS == 2 } else if (type == 'i64') { ret = [{{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}}, {{{ makeGetValue('varargs', 'argIndex+4', 'i32', undefined, undefined, true) }}}]; @@ -2433,7 +2458,7 @@ LibraryManager.library = { var signed = next == 'd'.charCodeAt(0) || next == 'i'.charCodeAt(0); argSize = argSize || 4; var currArg = getNextArg('i' + (argSize * 8)); -#if I64_MODE == 1 +#if USE_TYPED_ARRAYS == 2 // Flatten i64-1 [low, high] into a (slightly rounded) double if (argSize == 8) { currArg = Runtime.makeBigInt(currArg[0], currArg[1], next == 'u'.charCodeAt(0)); @@ -2711,7 +2736,9 @@ LibraryManager.library = { var flush = function(filedes) { // Right now we write all data directly, except for output devices. if (filedes in FS.streams && FS.streams[filedes].object.output) { - FS.streams[filedes].object.output(null); + if (!FS.streams[filedes].isTerminal) { // don't flush terminals, it would cause a \n to also appear + FS.streams[filedes].object.output(null); + } } }; try { @@ -3296,7 +3323,7 @@ LibraryManager.library = { }, atexit: function(func, arg) { - __ATEXIT__.push({ func: func, arg: arg }); + __ATEXIT__.unshift({ func: func, arg: arg }); }, __cxa_atexit: 'atexit', @@ -3485,12 +3512,6 @@ LibraryManager.library = { if (bits == 64) { ret = [{{{ splitI64('ret') }}}]; } -#else -#if I64_MODE == 1 - if (bits == 64) { - ret = {{{ splitI64('ret') }}}; - } -#endif #endif return ret; @@ -4374,27 +4395,10 @@ LibraryManager.library = { __cxa_guard_release: function() {}, __cxa_guard_abort: function() {}, + _ZTVN10__cxxabiv119__pointer_type_infoE: [0], // is a pointer _ZTVN10__cxxabiv117__class_type_infoE: [1], // no inherited classes _ZTVN10__cxxabiv120__si_class_type_infoE: [2], // yes inherited classes - __dynamic_cast: function(ptr, knownTI, attemptedTI, idunno) { - var ptrTV = {{{ makeGetValue('ptr', '0', '*') }}}; - var count = {{{ makeGetValue('ptrTV', '0', '*') }}}; - ptrTV -= {{{ Runtime.QUANTUM_SIZE }}}; - var TI = {{{ makeGetValue('ptrTV', '0', '*') }}}; - do { - if (TI == attemptedTI) return ptr; - // Go to parent class - var type_infoAddr = {{{ makeGetValue('TI', '0', '*') }}} - {{{ Runtime.QUANTUM_SIZE*2 }}}; - var type_info = {{{ makeGetValue('type_infoAddr', '0', '*') }}}; - if (type_info == 1) return 0; // no parent class - var TIAddr = TI + {{{ Runtime.QUANTUM_SIZE*2 }}}; - var TI = {{{ makeGetValue('TIAddr', '0', '*') }}}; - } while (1); - - return 0; - }, - // Exceptions __cxa_allocate_exception: function(size) { return _malloc(size); @@ -4402,8 +4406,20 @@ LibraryManager.library = { __cxa_free_exception: function(ptr) { return _free(ptr); }, - __cxa_throw__deps: ['llvm_eh_exception', '_ZSt18uncaught_exceptionv'], + __cxa_throw__deps: ['llvm_eh_exception', '_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'], __cxa_throw: function(ptr, type, destructor) { + if (!___cxa_throw.initialized) { + try { + {{{ makeSetValue('__ZTVN10__cxxabiv119__pointer_type_infoE', '0', '0', 'i32') }}}; // Workaround for libcxxabi integration bug + } catch(e){} + try { + {{{ makeSetValue('__ZTVN10__cxxabiv117__class_type_infoE', '0', '1', 'i32') }}}; // Workaround for libcxxabi integration bug + } catch(e){} + try { + {{{ makeSetValue('__ZTVN10__cxxabiv120__si_class_type_infoE', '0', '2', 'i32') }}}; // Workaround for libcxxabi integration bug + } catch(e){} + ___cxa_throw.initialized = true; + } #if EXCEPTION_DEBUG print('Compiled code throwing an exception, ' + [ptr,type,destructor] + ', at ' + new Error().stack); #endif @@ -4437,9 +4453,6 @@ LibraryManager.library = { llvm_eh_typeid_for: function(type) { return type; }, - _Unwind_Resume_or_Rethrow: function(ptr) { - throw ptr; - }, __cxa_begin_catch__deps: ['_ZSt18uncaught_exceptionv'], __cxa_begin_catch: function(ptr) { __ZSt18uncaught_exceptionv.uncaught_exception--; @@ -4481,11 +4494,104 @@ LibraryManager.library = { throw exception; }, + _Unwind_Resume_or_Rethrow: function(ptr) { + throw ptr; + }, + _Unwind_RaiseException__deps: ['llvm_eh_exception', '__cxa_find_matching_catch'], + _Unwind_RaiseException: function(ptr) { + throw ptr; + }, + _Unwind_DeleteException: function(ptr) {}, + terminate: '__cxa_call_unexpected', __gxx_personality_v0: function() { }, + __cxa_is_number_type: function(type) { + var isNumber = false; + try { if (type == __ZTIi) isNumber = true } catch(e){} + try { if (type == __ZTIl) isNumber = true } catch(e){} + try { if (type == __ZTIx) isNumber = true } catch(e){} + try { if (type == __ZTIf) isNumber = true } catch(e){} + try { if (type == __ZTId) isNumber = true } catch(e){} + return isNumber; + }, + + // Finds a suitable catch clause for when an exception is thrown. + // In normal compilers, this functionality is handled by the C++ + // 'personality' routine. This is passed a fairly complex structure + // relating to the context of the exception and makes judgements + // about how to handle it. Some of it is about matching a suitable + // catch clause, and some of it is about unwinding. We already handle + // unwinding using 'if' blocks around each function, so the remaining + // functionality boils down to picking a suitable 'catch' block. + // We'll do that here, instead, to keep things simpler. + + __cxa_find_matching_catch__deps: ['__cxa_does_inherit', '__cxa_is_number_type'], + __cxa_find_matching_catch: function(thrown, throwntype, typeArray) { + // If throwntype is a pointer, this means a pointer has been + // thrown. When a pointer is thrown, actually what's thrown + // is a pointer to the pointer. We'll dereference it. + if (throwntype != 0 && !___cxa_is_number_type(throwntype)) { + var throwntypeInfoAddr= {{{ makeGetValue('throwntype', '0', '*') }}} - {{{ Runtime.QUANTUM_SIZE*2 }}}; + var throwntypeInfo= {{{ makeGetValue('throwntypeInfoAddr', '0', '*') }}}; + if (throwntypeInfo == 0) + thrown = {{{ makeGetValue('thrown', '0', '*') }}}; + } + // The different catch blocks are denoted by different types. + // Due to inheritance, those types may not precisely match the + // type of the thrown object. Find one which matches, and + // return the type of the catch block which should be called. + for (var i = 0; i < typeArray.length; i++) { + if (___cxa_does_inherit(typeArray[i], throwntype, thrown)) + return { 'f0':thrown, 'f1':typeArray[i]}; + } + // Shouldn't happen unless we have bogus data in typeArray + // or encounter a type for which emscripten doesn't have suitable + // typeinfo defined. Best-efforts match just in case. + return {'f0':thrown,'f1':throwntype}; + }, + + // Recursively walks up the base types of 'possibilityType' + // to see if any of them match 'definiteType'. + __cxa_does_inherit__deps: ['__cxa_is_number_type'], + __cxa_does_inherit: function(definiteType, possibilityType, possibility) { + if (possibility == 0) return false; + if (possibilityType == 0 || possibilityType == definiteType) + return true; + var possibility_type_info; + if (___cxa_is_number_type(possibilityType)) { + possibility_type_info = possibilityType; + } else { + var possibility_type_infoAddr = {{{ makeGetValue('possibilityType', '0', '*') }}} - {{{ Runtime.QUANTUM_SIZE*2 }}}; + possibility_type_info = {{{ makeGetValue('possibility_type_infoAddr', '0', '*') }}}; + } + switch (possibility_type_info) { + case 0: // possibility is a pointer + // See if definite type is a pointer + var definite_type_infoAddr = {{{ makeGetValue('definiteType', '0', '*') }}} - {{{ Runtime.QUANTUM_SIZE*2 }}}; + var definite_type_info = {{{ makeGetValue('definite_type_infoAddr', '0', '*') }}}; + if (definite_type_info == 0) { + // Also a pointer; compare base types of pointers + var defPointerBaseAddr = definiteType+{{{ Runtime.QUANTUM_SIZE*2 }}}; + var defPointerBaseType = {{{ makeGetValue('defPointerBaseAddr', '0', '*') }}}; + var possPointerBaseAddr = possibilityType+{{{ Runtime.QUANTUM_SIZE*2 }}}; + var possPointerBaseType = {{{ makeGetValue('possPointerBaseAddr', '0', '*') }}}; + return ___cxa_does_inherit(defPointerBaseType, possPointerBaseType, possibility); + } else + return false; // one pointer and one non-pointer + case 1: // class with no base class + return false; + case 2: // class with base class + var parentTypeAddr = possibilityType + {{{ Runtime.QUANTUM_SIZE*2 }}}; + var parentType = {{{ makeGetValue('parentTypeAddr', '0', '*') }}}; + return ___cxa_does_inherit(definiteType, parentType, possibility); + default: + return false; // some unencountered type + } + }, + // RTTI hacks for exception handling, defining type_infos for common types. // The values are dummies. We simply use the addresses of these statically // allocated variables as unique identifiers. @@ -4503,6 +4609,8 @@ LibraryManager.library = { _ZTIc: [0], // type_info for void. _ZTIv: [0], + // type_info for void*. + _ZTIPv: [0], llvm_uadd_with_overflow_i32: function(x, y) { return { @@ -5102,7 +5210,7 @@ LibraryManager.library = { var dst = Number(start.getTimezoneOffset() != date.getTimezoneOffset()); {{{ makeSetValue('tmPtr', 'offsets.tm_isdst', 'dst', 'i32') }}} - var timezone = date.toString().match(/\(([A-Z]+)\)/)[1]; + var timezone = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | date.toString().match(/\(([A-Z]+)\)/)[1]; if (!(timezone in ___tm_timezones)) { ___tm_timezones[timezone] = allocate(intArrayFromString(timezone), 'i8', ALLOC_NORMAL); } @@ -5164,8 +5272,8 @@ LibraryManager.library = { var summer = new Date(2000, 6, 1); {{{ makeSetValue('__daylight', '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}} - var winterName = winter.toString().match(/\(([A-Z]+)\)/)[1]; - var summerName = summer.toString().match(/\(([A-Z]+)\)/)[1]; + var winterName = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | winter.toString().match(/\(([A-Z]+)\)/)[1]; + var summerName = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | summer.toString().match(/\(([A-Z]+)\)/)[1]; var winterNamePtr = allocate(intArrayFromString(winterName), 'i8', ALLOC_NORMAL); var summerNamePtr = allocate(intArrayFromString(summerName), 'i8', ALLOC_NORMAL); __tzname = _malloc(2 * {{{ Runtime.QUANTUM_SIZE }}}); // glibc does not need the double __ @@ -5278,19 +5386,26 @@ LibraryManager.library = { // ========================================================================== // setjmp.h + // + // Basic support for setjmp/longjmp: enough to run the wikipedia example and + // hopefully handle most normal behavior. We do not support cases where + // longjmp behavior is undefined (for example, if the setjmp function returns + // before longjmp is called). + // + // Note that we need to emulate functions that use setjmp, and also to create + // a new label we can return to. Emulation make such functions slower, this + // can be alleviated by making a new function containing just the setjmp + // related functionality so the slowdown is more limited. // ========================================================================== - setjmp: function(env) { - // XXX print('WARNING: setjmp() not really implemented, will fail if longjmp() is actually called'); - return 0; + setjmp__inline: function(env) { + // Save the label + return '(' + makeSetValue(env, '0', '__label__', 'i32') + ', 0)'; }, - _setjmp: 'setjmp', - longjmp: function(env, val) { - // not really working... - assert(0); + longjmp: function(env, value) { + throw { longjmp: true, label: {{{ makeGetValue('env', '0', 'i32') }}}, value: value || 1 }; }, - _longjmp: 'longjmp', // ========================================================================== // signal.h @@ -5828,6 +5943,26 @@ LibraryManager.library = { return 0; }, + pthread_once: function(ptr, func) { + if (!_pthread_once.seen) _pthread_once.seen = {}; + if (ptr in _pthread_once.seen) return; + FUNCTION_TABLE[func](); + _pthread_once.seen[ptr] = 1; + }, + + pthread_key_create: function(key, destructor) { + if (!_pthread_key_create.keys) _pthread_key_create.keys = {}; + _pthread_key_create.keys[key] = null; + }, + + pthread_getspecific: function(key) { + return _pthread_key_create.keys[key]; + }, + + pthread_setspecific: function(key, value) { + _pthread_key_create.keys[key] = value; + }, + // ========================================================================== // malloc.h // ========================================================================== diff --git a/src/modules.js b/src/modules.js index 2896d632..fd22b9fe 100644 --- a/src/modules.js +++ b/src/modules.js @@ -15,7 +15,7 @@ var LLVM = { SHIFTS: set('ashr', 'lshr', 'shl'), PHI_REACHERS: set('branch', 'switch', 'invoke'), EXTENDS: set('sext', 'zext'), - INTRINSICS_32: set('_llvm_memcpy_p0i8_p0i8_i64', '_llvm_memmove_p0i8_p0i8_i64', '_llvm_memset_p0i8_i64'), // intrinsics that need args converted to i32 in I64_MODE 1 + INTRINSICS_32: set('_llvm_memcpy_p0i8_p0i8_i64', '_llvm_memmove_p0i8_p0i8_i64', '_llvm_memset_p0i8_i64'), // intrinsics that need args converted to i32 in USE_TYPED_ARRAYS == 2 }; LLVM.GLOBAL_MODIFIERS = set(keys(LLVM.LINKAGES).concat(['constant', 'global', 'hidden'])); diff --git a/src/parseTools.js b/src/parseTools.js index 071ed090..520d278e 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -22,6 +22,9 @@ function preprocess(text) { var showStack = []; for (var i = 0; i < lines.length; i++) { var line = lines[i]; + if (line[line.length-1] == '\r') { + line = line.substr(0, line.length-1); // Windows will have '\r' left over from splitting over '\r\n' + } if (!line[0] || line[0] != '#') { if (showStack.indexOf(false) == -1) { ret += line + '\n'; @@ -137,6 +140,11 @@ function getBits(type) { return parseInt(left); } +function isIllegalType(type) { + var bits = getBits(type); + return bits > 0 && (bits >= 64 || !isPowerOfTwo(bits)); +} + function isVoidType(type) { return type == 'void'; } @@ -227,7 +235,11 @@ function getTokenIndexByText(tokens, text) { } function findTokenText(item, text) { - for (var i = 0; i < item.tokens.length; i++) { + return findTokenTextAfter(item, text, 0); +} + +function findTokenTextAfter(item, text, startAt) { + for (var i = startAt; i < item.tokens.length; i++) { if (item.tokens[i].text == text) return i; } return -1; @@ -312,7 +324,7 @@ function parseParamTokens(params) { ret.push({ intertype: 'value', type: segment[0].text, - ident: toNiceIdent(parseNumerical(segment[1].text)) + ident: toNiceIdent(parseNumerical(segment[1].text, segment[0].text)) }); Types.needAnalysis[removeAllPointing(ret[ret.length-1].type)] = 0; } @@ -338,7 +350,7 @@ function finalizeParam(param) { } else if (param.intertype === 'jsvalue') { return param.ident; } else { - if (param.type == 'i64' && I64_MODE == 1) { + if (param.type == 'i64' && USE_TYPED_ARRAYS == 2) { return parseI64Constant(param.ident); } var ret = toNiceIdent(param.ident); @@ -525,7 +537,7 @@ function makeInlineCalculation(expression, value, tempVar) { // Makes a proper runtime value for a 64-bit value from low and high i32s. low and high are assumed to be unsigned. function makeI64(low, high) { high = high || '0'; - if (I64_MODE == 1) { + if (USE_TYPED_ARRAYS == 2) { return '[' + makeSignOp(low, 'i32', 'un', 1, 1) + ',' + makeSignOp(high, 'i32', 'un', 1, 1) + ']'; } else { if (high) return RuntimeGenerator.makeBigInt(low, high); @@ -535,36 +547,36 @@ function makeI64(low, high) { // XXX Make all i64 parts signed -// Splits a number (an integer in a double, possibly > 32 bits) into an I64_MODE 1 i64 value. +// Splits a number (an integer in a double, possibly > 32 bits) into an USE_TYPED_ARRAYS == 2 i64 value. // Will suffer from rounding. mergeI64 does the opposite. function splitI64(value) { // We need to min here, since our input might be a double, and large values are rounded, so they can // be slightly higher than expected. And if we get 4294967296, that will turn into a 0 if put into a // HEAP32 or |0'd, etc. if (legalizedI64s) { - return [value + '>>>0', 'Math.min(Math.floor(' + value + '/4294967296), 4294967295)']; + return [value + '>>>0', 'Math.min(Math.floor((' + value + ')/4294967296), 4294967295)']; } else { return makeInlineCalculation(makeI64('VALUE>>>0', 'Math.min(Math.floor(VALUE/4294967296), 4294967295)'), value, 'tempBigIntP'); } } -function mergeI64(value) { - assert(I64_MODE == 1); +function mergeI64(value, unsigned) { + assert(USE_TYPED_ARRAYS == 2); if (legalizedI64s) { - return RuntimeGenerator.makeBigInt(value + '$0', value + '$1'); + return RuntimeGenerator.makeBigInt(value + '$0', value + '$1', unsigned); } else { - return makeInlineCalculation(RuntimeGenerator.makeBigInt('VALUE[0]', 'VALUE[1]'), value, 'tempI64'); + return makeInlineCalculation(RuntimeGenerator.makeBigInt('VALUE[0]', 'VALUE[1]', unsigned), value, 'tempI64'); } } // Takes an i64 value and changes it into the [low, high] form used in i64 mode 1. In that // mode, this is a no-op function ensureI64_1(value) { - if (I64_MODE == 1) return value; + if (USE_TYPED_ARRAYS == 2) return value; return splitI64(value, 1); } function makeCopyI64(value) { - assert(I64_MODE == 1); + assert(USE_TYPED_ARRAYS == 2); return value + '.slice(0)'; } @@ -602,6 +614,22 @@ function parseArbitraryInt(str, bits) { } } + function mul2(v) { // v *= 2 + for (var i = v.length-1; i >= 0; i--) { + var d = v[i]*2; + r = d >= 10; + v[i] = d%10; + var j = i-1; + if (r) { + if (j < 0) { + v.unshift(1); + break; + } + v[j] += 0.5; // will be multiplied + } + } + } + function subtract(v, w) { // v -= w. we assume v >= w while (v.length > w.length) w.splice(0, 0, 0); for (var i = 0; i < v.length; i++) { @@ -631,9 +659,11 @@ function parseArbitraryInt(str, bits) { if (str[0] == '-') { // twos-complement is needed - assert(bits == 64, "we only support 64-bit two's complement so far"); str = str.substr(1); - v = str2vec('18446744073709551616'); // 2^64 + v = str2vec('1'); + for (var i = 0; i < bits; i++) { + mul2(v); + } subtract(v, str2vec(str)); } else { v = str2vec(str); @@ -654,7 +684,7 @@ function parseArbitraryInt(str, bits) { } function parseI64Constant(str) { - assert(I64_MODE == 1); + assert(USE_TYPED_ARRAYS == 2); if (!isNumber(str)) { // This is a variable. Copy it, so we do not modify the original @@ -671,8 +701,8 @@ function parseNumerical(value, type) { // Hexadecimal double value, as the llvm docs say, // "The one non-intuitive notation for constants is the hexadecimal form of floating point constants." value = IEEEUnHex(value); - } else if (type == 'i64' && I64_MODE == 1) { - value = parseI64Constant(value); + } else if (USE_TYPED_ARRAYS == 2 && isIllegalType(type)) { + return value; // do not parseFloat etc., that can lead to loss of precision } else if (value == 'null') { // NULL *is* 0, in C/C++. No JS null! (null == 0 is false, etc.) value = '0'; @@ -736,7 +766,7 @@ function calcAllocatedSize(type) { function generateStructTypes(type) { if (isArray(type)) return type; // already in the form of [type, type,...] if (Runtime.isNumberType(type) || isPointerType(type)) { - if (I64_MODE == 1 && type == 'i64') { + if (USE_TYPED_ARRAYS == 2 && type == 'i64') { return ['i64', 0, 0, 0, 'i32', 0, 0, 0]; } return [type].concat(zeros(Runtime.getNativeFieldSize(type))); @@ -753,7 +783,7 @@ function generateStructTypes(type) { var type = typeData.fields[i]; if (!SAFE_HEAP && isPointerType(type)) type = '*'; // do not include unneeded type names without safe heap if (Runtime.isNumberType(type) || isPointerType(type)) { - if (I64_MODE == 1 && type == 'i64') { + if (USE_TYPED_ARRAYS == 2 && type == 'i64') { ret[index++] = 'i64'; ret[index++] = 0; ret[index++] = 0; @@ -1084,7 +1114,7 @@ function makeCopyValues(dest, src, num, type, modifier, align, sep) { var oldDest = dest, oldSrc = src; dest = '$$dest'; src = '$$src'; - return 'for ($$src = ' + oldSrc + ', $$dest = ' + oldDest + ', $$stop = $$src + ' + num + '; $$src < $$stop; $$src++, $$dest++) {\n' + + return 'for (var $$src = ' + oldSrc + ', $$dest = ' + oldDest + ', $$stop = $$src + ' + num + '; $$src < $$stop; $$src++, $$dest++) {\n' + unroll(type, 1) + ' }'; } else { // USE_TYPED_ARRAYS == 2 // If we don't know how to handle this at compile-time, or handling it is best done in a large amount of code, call memset @@ -1312,19 +1342,15 @@ function finalizeLLVMFunctionCall(item, noIndexizeFunctions) { var temp = { op: item.intertype, variant: item.variant, - type: item.type + type: item.type, + params: item.params.slice(0) // XXX slice? }; - for (var i = 1; i <= 4; i++) { - if (item.params[i-1]) { - temp['param' + i] = item.params[i-1]; - } - } return processMathop(temp); } function getGetElementPtrIndexes(item) { var type = item.params[0].type; - if (I64_MODE == 1) { + if (USE_TYPED_ARRAYS == 2) { // GEP indexes are marked as i64s, but they are just numbers to us item.params.forEach(function(param) { param.type = 'i32' }); } @@ -1424,10 +1450,10 @@ function finalizeLLVMParameter(param, noIndexizeFunctions) { if (ret in Variables.globals && Variables.globals[ret].isString) { ret = "STRING_TABLE." + ret; } - if (param.type == 'i64' && I64_MODE == 1) { + if (param.type == 'i64' && USE_TYPED_ARRAYS == 2) { ret = parseI64Constant(ret); } - ret = parseNumerical(ret); + ret = parseNumerical(ret, param.type); } else if (param.intertype == 'structvalue') { ret = makeLLVMStruct(param.params.map(function(value) { return finalizeLLVMParameter(value, noIndexizeFunctions) })); } else if (param.intertype === 'blockaddress') { @@ -1444,8 +1470,17 @@ function finalizeLLVMParameter(param, noIndexizeFunctions) { return ret; } +function makeComparison(a, b, type) { + if (!isIllegalType(type)) { + return a + ' == ' + b; + } else { + assert(type == 'i64'); + return a + '$0 == ' + b + '$0 && ' + a + '$1 == ' + b + '$1'; + } +} + function makeSignOp(value, type, op, force, ignore) { - if (I64_MODE == 1 && type == 'i64') { + if (USE_TYPED_ARRAYS == 2 && type == 'i64') { return value; // these are always assumed to be two 32-bit unsigneds. } @@ -1532,48 +1567,45 @@ function processMathop(item) { var variant = item.variant; var type = item.type; var paramTypes = ['', '', '', '']; - for (var i = 1; i <= 3; i++) { - if (item['param'+i]) { - paramTypes[i-1] = item['param'+i].type || type; - item['ident'+i] = finalizeLLVMParameter(item['param'+i]); - if (!isNumber(item['ident'+i]) && !isNiceIdent(item['ident'+i])) { - item['ident'+i] = '(' + item['ident'+i] + ')'; // we may have nested expressions. So enforce the order of operations we want + var idents = []; + for (var i = 0; i < 3; i++) { + if (item.params[i]) { + paramTypes[i] = item.params[i].type || type; + idents[i] = finalizeLLVMParameter(item.params[i]); + if (!isNumber(idents[i]) && !isNiceIdent(idents[i])) { + idents[i] = '(' + idents[i] + ')'; // we may have nested expressions. So enforce the order of operations we want } } else { - item['ident'+i] = null; // just so it exists for purposes of reading ident2 etc. later on, and no exception is thrown + idents[i] = null; // just so it exists for purposes of reading idents[1] etc. later on, and no exception is thrown } } - var ident1 = item.ident1; - var ident2 = item.ident2; - var ident3 = item.ident3; - var originalIdent1 = ident1; - var originalIdent2 = ident2; + var originalIdents = idents.slice(0); if (isUnsignedOp(op, variant)) { - ident1 = makeSignOp(ident1, paramTypes[0], 'un'); - ident2 = makeSignOp(ident2, paramTypes[1], 'un'); + idents[0] = makeSignOp(idents[0], paramTypes[0], 'un'); + idents[1] = makeSignOp(idents[1], paramTypes[1], 'un'); } else if (isSignedOp(op, variant)) { - ident1 = makeSignOp(ident1, paramTypes[0], 're'); - ident2 = makeSignOp(ident2, paramTypes[1], 're'); + idents[0] = makeSignOp(idents[0], paramTypes[0], 're'); + idents[1] = makeSignOp(idents[1], paramTypes[1], 're'); } var bits = null; if (item.type[0] === 'i') { bits = parseInt(item.type.substr(1)); } - var bitsLeft = parseInt(((item.param2 && item.param2.ident) ? item.param2.ident : item.type).substr(1)); // remove i to leave the number of bits left after this operation + var bitsLeft = parseInt(((item.params[1] && item.params[1].ident) ? item.params[1].ident : item.type).substr(1)); // remove i to leave the number of bits left after this operation function integerizeBignum(value) { return makeInlineCalculation('VALUE-VALUE%1', value, 'tempBigIntI'); } - if ((type == 'i64' || paramTypes[0] == 'i64' || paramTypes[1] == 'i64' || ident2 == '(i64)') && I64_MODE == 1) { + if ((type == 'i64' || paramTypes[0] == 'i64' || paramTypes[1] == 'i64' || idents[1] == '(i64)') && USE_TYPED_ARRAYS == 2) { var warnI64_1 = function() { warnOnce('Arithmetic on 64-bit integers in mode 1 is rounded and flaky, like mode 0!'); }; // In ops that can be either legalized or not, we need to differentiate how we access low and high parts - var low1 = ident1 + (legalizedI64s ? '$0' : '[0]'); - var high1 = ident1 + (legalizedI64s ? '$1' : '[1]'); - var low2 = ident2 + (legalizedI64s ? '$0' : '[0]'); - var high2 = ident2 + (legalizedI64s ? '$1' : '[1]'); + var low1 = idents[0] + (legalizedI64s ? '$0' : '[0]'); + var high1 = idents[0] + (legalizedI64s ? '$1' : '[1]'); + var low2 = idents[1] + (legalizedI64s ? '$0' : '[0]'); + var high2 = idents[1] + (legalizedI64s ? '$1' : '[1]'); function finish(result) { // If this is in legalization mode, steal the assign and assign into two vars if (legalizedI64s) { @@ -1588,104 +1620,112 @@ function processMathop(item) { switch (op) { // basic integer ops case 'or': { - return '[' + ident1 + '[0] | ' + ident2 + '[0], ' + ident1 + '[1] | ' + ident2 + '[1]]'; + return '[' + idents[0] + '[0] | ' + idents[1] + '[0], ' + idents[0] + '[1] | ' + idents[1] + '[1]]'; } case 'and': { - return '[' + ident1 + '[0] & ' + ident2 + '[0], ' + ident1 + '[1] & ' + ident2 + '[1]]'; + return '[' + idents[0] + '[0] & ' + idents[1] + '[0], ' + idents[0] + '[1] & ' + idents[1] + '[1]]'; } case 'xor': { - return '[' + ident1 + '[0] ^ ' + ident2 + '[0], ' + ident1 + '[1] ^ ' + ident2 + '[1]]'; + return '[' + idents[0] + '[0] ^ ' + idents[1] + '[0], ' + idents[0] + '[1] ^ ' + idents[1] + '[1]]'; } case 'shl': case 'ashr': case 'lshr': { - if (!isNumber(ident2)) { - return 'Runtime.bitshift64(' + ident1 + '[0], ' + ident1 + '[1],"' + op + '",' + stripCorrections(ident2) + '[0]|0)'; + if (!isNumber(idents[1])) { + return 'Runtime.bitshift64(' + idents[0] + '[0], ' + idents[0] + '[1],"' + op + '",' + stripCorrections(idents[1]) + '[0]|0)'; } - bits = parseInt(ident2); + bits = parseInt(idents[1]); var ander = Math.pow(2, bits)-1; if (bits < 32) { switch (op) { case 'shl': - return '[' + ident1 + '[0] << ' + ident2 + ', ' + - '('+ident1 + '[1] << ' + ident2 + ') | ((' + ident1 + '[0]&(' + ander + '<<' + (32 - bits) + ')) >>> (32-' + ident2 + '))]'; + return '[' + idents[0] + '[0] << ' + idents[1] + ', ' + + '('+idents[0] + '[1] << ' + idents[1] + ') | ((' + idents[0] + '[0]&(' + ander + '<<' + (32 - bits) + ')) >>> (32-' + idents[1] + '))]'; case 'ashr': - return '[((('+ident1 + '[0] >>> ' + ident2 + ') | ((' + ident1 + '[1]&' + ander + ')<<' + (32 - bits) + ')) >> 0) >>> 0,' + - '(' + ident1 + '[1] >> ' + ident2 + ') >>> 0]'; + return '[((('+idents[0] + '[0] >>> ' + idents[1] + ') | ((' + idents[0] + '[1]&' + ander + ')<<' + (32 - bits) + ')) >> 0) >>> 0,' + + '(' + idents[0] + '[1] >> ' + idents[1] + ') >>> 0]'; case 'lshr': - return '[(('+ident1 + '[0] >>> ' + ident2 + ') | ((' + ident1 + '[1]&' + ander + ')<<' + (32 - bits) + ')) >>> 0,' + - ident1 + '[1] >>> ' + ident2 + ']'; + return '[(('+idents[0] + '[0] >>> ' + idents[1] + ') | ((' + idents[0] + '[1]&' + ander + ')<<' + (32 - bits) + ')) >>> 0,' + + idents[0] + '[1] >>> ' + idents[1] + ']'; } } else if (bits == 32) { switch (op) { case 'shl': - return '[0, ' + ident1 + '[0]]'; + return '[0, ' + idents[0] + '[0]]'; case 'ashr': - return '[' + ident1 + '[1], (' + ident1 + '[1]|0) < 0 ? ' + ander + ' : 0]'; + return '[' + idents[0] + '[1], (' + idents[0] + '[1]|0) < 0 ? ' + ander + ' : 0]'; case 'lshr': - return '[' + ident1 + '[1], 0]'; + return '[' + idents[0] + '[1], 0]'; } } else { // bits > 32 switch (op) { case 'shl': - return '[0, ' + ident1 + '[0] << ' + (bits - 32) + ']'; + return '[0, ' + idents[0] + '[0] << ' + (bits - 32) + ']'; case 'ashr': - return '[(' + ident1 + '[1] >> ' + (bits - 32) + ') >>> 0, (' + ident1 + '[1]|0) < 0 ? ' + ander + ' : 0]'; + return '[(' + idents[0] + '[1] >> ' + (bits - 32) + ') >>> 0, (' + idents[0] + '[1]|0) < 0 ? ' + ander + ' : 0]'; case 'lshr': - return '[' + ident1 + '[1] >>> ' + (bits - 32) + ', 0]'; + return '[' + idents[0] + '[1] >>> ' + (bits - 32) + ', 0]'; } } } case 'uitofp': case 'sitofp': return low1 + ' + ' + high1 + '*4294967296'; - case 'fptoui': case 'fptosi': return finish(splitI64(ident1)); + case 'fptoui': case 'fptosi': return finish(splitI64(idents[0])); case 'icmp': { switch (variant) { - case 'uge': return high1 + ' >= ' + high2 + ' && (' + high1 + ' > ' + high2 + ' || ' + - low1 + ' >= ' + low2 + ')'; + case 'uge': return '(' + high1 + '>>>0) >= (' + high2 + '>>>0) && ((' + high1 + '>>>0) > (' + high2 + '>>>0) || ' + + '(' + low1 + '>>>0) >= (' + low2 + '>>>0))'; case 'sge': return '(' + high1 + '|0) >= (' + high2 + '|0) && ((' + high1 + '|0) > (' + high2 + '|0) || ' + - '(' + low1 + '|0) >= (' + low2 + '|0))'; - case 'ule': return high1 + ' <= ' + high2 + ' && (' + high1 + ' < ' + high2 + ' || ' + - low1 + ' <= ' + low2 + ')'; + '(' + low1 + '>>>0) >= (' + low2 + '>>>0))'; + case 'ule': return '(' + high1 + '>>>0) <= (' + high2 + '>>>0) && ((' + high1 + '>>>0) < (' + high2 + '>>>0) || ' + + '(' + low1 + '>>>0) <= (' + low2 + '>>>0))'; case 'sle': return '(' + high1 + '|0) <= (' + high2 + '|0) && ((' + high1 + '|0) < (' + high2 + '|0) || ' + - '(' + low1 + '|0) <= (' + low2 + '|0))'; - case 'ugt': return high1 + ' > ' + high2 + ' || (' + high1 + ' == ' + high2 + ' && ' + - low1 + ' > ' + low2 + ')'; + '(' + low1 + '>>>0) <= (' + low2 + '>>>0))'; + case 'ugt': return '(' + high1 + '>>>0) > (' + high2 + '>>>0) || ((' + high1 + '>>>0) == (' + high2 + '>>>0) && ' + + '(' + low1 + '>>>0) > (' + low2 + '>>>0))'; case 'sgt': return '(' + high1 + '|0) > (' + high2 + '|0) || ((' + high1 + '|0) == (' + high2 + '|0) && ' + - '(' + low1 + '|0) > (' + low2 + '|0))'; - case 'ult': return high1 + ' < ' + high2 + ' || (' + high1 + ' == ' + high2 + ' && ' + - low1 + ' < ' + low2 + ')'; + '(' + low1 + '>>>0) > (' + low2 + '>>>0))'; + case 'ult': return '(' + high1 + '>>>0) < (' + high2 + '>>>0) || ((' + high1 + '>>>0) == (' + high2 + '>>>0) && ' + + '(' + low1 + '>>>0) < (' + low2 + '>>>0))'; case 'slt': return '(' + high1 + '|0) < (' + high2 + '|0) || ((' + high1 + '|0) == (' + high2 + '|0) && ' + - '(' + low1 + '|0) < (' + low2 + '|0))'; + '(' + low1 + '>>>0) < (' + low2 + '>>>0))'; case 'ne': return low1 + ' != ' + low2 + ' || ' + high1 + ' != ' + high2 + ''; case 'eq': return low1 + ' == ' + low2 + ' && ' + high1 + ' == ' + high2 + ''; default: throw 'Unknown icmp variant: ' + variant; } } - case 'zext': return makeI64(ident1, 0); - case 'sext': return makeInlineCalculation(makeI64('VALUE', 'VALUE<0 ? 4294967295 : 0'), ident1, 'tempBigIntD'); + case 'zext': return makeI64(idents[0], 0); + case 'sext': return makeInlineCalculation(makeI64('VALUE', 'VALUE<0 ? 4294967295 : 0'), idents[0], 'tempBigIntD'); case 'trunc': { - return '((' + ident1 + '[0]) & ' + (Math.pow(2, bitsLeft)-1) + ')'; + return '((' + idents[0] + '[0]) & ' + (Math.pow(2, bitsLeft)-1) + ')'; } - case 'select': return ident1 + ' ? ' + makeCopyI64(ident2) + ' : ' + makeCopyI64(ident3); - case 'ptrtoint': return makeI64(ident1, 0); - case 'inttoptr': return '(' + ident1 + '[0])'; // just directly truncate the i64 to a 'pointer', which is an i32 + case 'select': return idents[0] + ' ? ' + makeCopyI64(idents[1]) + ' : ' + makeCopyI64(idents[2]); + case 'ptrtoint': return makeI64(idents[0], 0); + case 'inttoptr': return '(' + idents[0] + '[0])'; // just directly truncate the i64 to a 'pointer', which is an i32 // Dangerous, rounded operations. TODO: Fully emulate - case 'add': warnI64_1(); return finish(splitI64(mergeI64(ident1) + '+' + mergeI64(ident2))); - case 'sub': warnI64_1(); return finish(splitI64(mergeI64(ident1) + '-' + mergeI64(ident2))); - case 'sdiv': case 'udiv': warnI64_1(); return finish(splitI64(makeRounding(mergeI64(ident1) + '/' + mergeI64(ident2), bits, op[0] === 's'))); - case 'mul': warnI64_1(); return finish(splitI64(mergeI64(ident1) + '*' + mergeI64(ident2))); - case 'urem': case 'srem': warnI64_1(); return finish(splitI64(mergeI64(ident1) + '%' + mergeI64(ident2))); + case 'add': warnI64_1(); return finish(splitI64(mergeI64(idents[0]) + '+' + mergeI64(idents[1]))); + case 'sub': warnI64_1(); return finish(splitI64(mergeI64(idents[0]) + '-' + mergeI64(idents[1]))); + case 'sdiv': case 'udiv': warnI64_1(); return finish(splitI64(makeRounding(mergeI64(idents[0], op[0] === 'u') + '/' + mergeI64(idents[1], op[0] === 'u'), bits, op[0] === 's'))); + case 'mul': warnI64_1(); return finish(splitI64(mergeI64(idents[0], op[0] === 'u') + '*' + mergeI64(idents[1], op[0] === 'u'))); + case 'urem': case 'srem': warnI64_1(); return finish(splitI64(mergeI64(idents[0], op[0] === 'u') + '%' + mergeI64(idents[1], op[0] === 'u'))); case 'bitcast': { // Pointers are not 64-bit, so there is really only one possible type of bitcast here, int to float or vice versa assert(USE_TYPED_ARRAYS == 2, 'Can only bitcast ints <-> floats with typed arrays mode 2'); - var inType = item.param1.type; + var inType = item.params[0].type; var outType = item.type; if (inType in Runtime.INT_TYPES && outType in Runtime.FLOAT_TYPES) { - return makeInlineCalculation('tempDoubleI32[0]=VALUE[0],tempDoubleI32[1]=VALUE[1],tempDoubleF64[0]', ident1, 'tempI64'); + if (legalizedI64s) { + return '(tempDoubleI32[0]=' + idents[0] + '$0, tempDoubleI32[1]=' + idents[0] + '$1, tempDoubleF64[0])'; + } else { + return makeInlineCalculation('tempDoubleI32[0]=VALUE[0],tempDoubleI32[1]=VALUE[1],tempDoubleF64[0]', idents[0], 'tempI64'); + } } else if (inType in Runtime.FLOAT_TYPES && outType in Runtime.INT_TYPES) { - return '(tempDoubleF64[0]=' + ident1 + ',[tempDoubleI32[0],tempDoubleI32[1]])'; + if (legalizedI64s) { + return 'tempDoubleF64[0]=' + idents[0] + '; ' + finish(['tempDoubleI32[0]','tempDoubleI32[1]']); + } else { + return '(tempDoubleF64[0]=' + idents[0] + ',[tempDoubleI32[0],tempDoubleI32[1]])'; + } } else { - throw 'Invalid I64_MODE1 bitcast: ' + dump(item) + ' : ' + item.param1.type; + throw 'Invalid USE_TYPED_ARRAYS == 2 bitcast: ' + dump(item) + ' : ' + item.params[0].type; } } default: throw 'Unsupported i64 mode 1 op: ' + item.op + ' : ' + dump(item); @@ -1694,73 +1734,73 @@ function processMathop(item) { switch (op) { // basic integer ops - case 'add': return handleOverflow(getFastValue(ident1, '+', ident2, item.type), bits); - case 'sub': return handleOverflow(getFastValue(ident1, '-', ident2, item.type), bits); - case 'sdiv': case 'udiv': return makeRounding(getFastValue(ident1, '/', ident2, item.type), bits, op[0] === 's'); - case 'mul': return handleOverflow(getFastValue(ident1, '*', ident2, item.type), bits); - case 'urem': case 'srem': return getFastValue(ident1, '%', ident2, item.type); + case 'add': return handleOverflow(getFastValue(idents[0], '+', idents[1], item.type), bits); + case 'sub': return handleOverflow(getFastValue(idents[0], '-', idents[1], item.type), bits); + case 'sdiv': case 'udiv': return makeRounding(getFastValue(idents[0], '/', idents[1], item.type), bits, op[0] === 's'); + case 'mul': return handleOverflow(getFastValue(idents[0], '*', idents[1], item.type), bits); + case 'urem': case 'srem': return getFastValue(idents[0], '%', idents[1], item.type); case 'or': { if (bits > 32) { assert(bits === 64, 'Too many bits for or: ' + bits); dprint('Warning: 64 bit OR - precision limit may be hit on llvm line ' + item.lineNum); - return 'Runtime.or64(' + ident1 + ', ' + ident2 + ')'; + return 'Runtime.or64(' + idents[0] + ', ' + idents[1] + ')'; } - return ident1 + ' | ' + ident2; + return idents[0] + ' | ' + idents[1]; } case 'and': { if (bits > 32) { assert(bits === 64, 'Too many bits for and: ' + bits); dprint('Warning: 64 bit AND - precision limit may be hit on llvm line ' + item.lineNum); - return 'Runtime.and64(' + ident1 + ', ' + ident2 + ')'; + return 'Runtime.and64(' + idents[0] + ', ' + idents[1] + ')'; } - return ident1 + ' & ' + ident2; + return idents[0] + ' & ' + idents[1]; } case 'xor': { if (bits > 32) { assert(bits === 64, 'Too many bits for xor: ' + bits); dprint('Warning: 64 bit XOR - precision limit may be hit on llvm line ' + item.lineNum); - return 'Runtime.xor64(' + ident1 + ', ' + ident2 + ')'; + return 'Runtime.xor64(' + idents[0] + ', ' + idents[1] + ')'; } - return ident1 + ' ^ ' + ident2; + return idents[0] + ' ^ ' + idents[1]; } case 'shl': { - if (bits > 32) return ident1 + '*' + getFastValue(2, 'pow', ident2); - return ident1 + ' << ' + ident2; + if (bits > 32) return idents[0] + '*' + getFastValue(2, 'pow', idents[1]); + return idents[0] + ' << ' + idents[1]; } case 'ashr': { - if (bits > 32) return integerizeBignum(ident1 + '/' + getFastValue(2, 'pow', ident2)); - if (bits === 32) return originalIdent1 + ' >> ' + ident2; // No need to reSign in this case - return ident1 + ' >> ' + ident2; + if (bits > 32) return integerizeBignum(idents[0] + '/' + getFastValue(2, 'pow', idents[1])); + if (bits === 32) return originalIdents[0] + ' >> ' + idents[1]; // No need to reSign in this case + return idents[0] + ' >> ' + idents[1]; } case 'lshr': { - if (bits > 32) return integerizeBignum(ident1 + '/' + getFastValue(2, 'pow', ident2)); - if (bits === 32) return originalIdent1 + ' >>> ' + ident2; // No need to unSign in this case - return ident1 + ' >>> ' + ident2; + if (bits > 32) return integerizeBignum(idents[0] + '/' + getFastValue(2, 'pow', idents[1])); + if (bits === 32) return originalIdents[0] + ' >>> ' + idents[1]; // No need to unSign in this case + return idents[0] + ' >>> ' + idents[1]; } // basic float ops - case 'fadd': return getFastValue(ident1, '+', ident2, item.type); - case 'fsub': return getFastValue(ident1, '-', ident2, item.type); - case 'fdiv': return getFastValue(ident1, '/', ident2, item.type); - case 'fmul': return getFastValue(ident1, '*', ident2, item.type); - case 'uitofp': case 'sitofp': return ident1; - case 'fptoui': case 'fptosi': return makeRounding(ident1, bitsLeft, op === 'fptosi', true); + case 'fadd': return getFastValue(idents[0], '+', idents[1], item.type); + case 'fsub': return getFastValue(idents[0], '-', idents[1], item.type); + case 'fdiv': return getFastValue(idents[0], '/', idents[1], item.type); + case 'fmul': return getFastValue(idents[0], '*', idents[1], item.type); + case 'uitofp': case 'sitofp': return idents[0]; + case 'fptoui': case 'fptosi': return makeRounding(idents[0], bitsLeft, op === 'fptosi', true); // TODO: We sometimes generate false instead of 0, etc., in the *cmps. It seemed slightly faster before, but worth rechecking // Note that with typed arrays, these become 0 when written. So that is a potential difference with non-typed array runs. case 'icmp': { switch (variant) { - case 'uge': case 'sge': return ident1 + ' >= ' + ident2; - case 'ule': case 'sle': return ident1 + ' <= ' + ident2; - case 'ugt': case 'sgt': return ident1 + ' > ' + ident2; - case 'ult': case 'slt': return ident1 + ' < ' + ident2; + case 'uge': case 'sge': return idents[0] + ' >= ' + idents[1]; + case 'ule': case 'sle': return idents[0] + ' <= ' + idents[1]; + case 'ugt': case 'sgt': return idents[0] + ' > ' + idents[1]; + case 'ult': case 'slt': return idents[0] + ' < ' + idents[1]; // We use loose comparisons, which allows false == 0 to be true, etc. Ditto in fcmp case 'ne': case 'eq': { // We must sign them, so we do not compare -1 to 255 (could have unsigned them both too) // since LLVM tells us if <=, >= etc. comparisons are signed, but not == and !=. assert(paramTypes[0] == paramTypes[1]); - ident1 = makeSignOp(ident1, paramTypes[0], 're'); - ident2 = makeSignOp(ident2, paramTypes[1], 're'); - return ident1 + (variant === 'eq' ? '==' : '!=') + ident2; + idents[0] = makeSignOp(idents[0], paramTypes[0], 're'); + idents[1] = makeSignOp(idents[1], paramTypes[1], 're'); + return idents[0] + (variant === 'eq' ? '==' : '!=') + idents[1]; } default: throw 'Unknown icmp variant: ' + variant; } @@ -1769,23 +1809,23 @@ function processMathop(item) { switch (variant) { // TODO 'o' ones should be 'ordered (no NaN) and', // 'u' ones should be 'unordered or'. - case 'uge': case 'oge': return ident1 + ' >= ' + ident2; - case 'ule': case 'ole': return ident1 + ' <= ' + ident2; - case 'ugt': case 'ogt': return ident1 + ' > ' + ident2; - case 'ult': case 'olt': return ident1 + ' < ' + ident2; - case 'une': case 'one': return ident1 + ' != ' + ident2; - case 'ueq': case 'oeq': return ident1 + ' == ' + ident2; - case 'ord': return '!isNaN(' + ident1 + ') && !isNaN(' + ident2 + ')'; - case 'uno': return 'isNaN(' + ident1 + ') || isNaN(' + ident2 + ')'; + case 'uge': case 'oge': return idents[0] + ' >= ' + idents[1]; + case 'ule': case 'ole': return idents[0] + ' <= ' + idents[1]; + case 'ugt': case 'ogt': return idents[0] + ' > ' + idents[1]; + case 'ult': case 'olt': return idents[0] + ' < ' + idents[1]; + case 'une': case 'one': return idents[0] + ' != ' + idents[1]; + case 'ueq': case 'oeq': return idents[0] + ' == ' + idents[1]; + case 'ord': return '!isNaN(' + idents[0] + ') && !isNaN(' + idents[1] + ')'; + case 'uno': return 'isNaN(' + idents[0] + ') || isNaN(' + idents[1] + ')'; case 'true': return '1'; default: throw 'Unknown fcmp variant: ' + variant; } } // Note that zext has sign checking, see above. We must guard against -33 in i8 turning into -33 in i32 // then unsigning that i32... which would give something huge. - case 'zext': case 'fpext': case 'sext': return ident1; - case 'fptrunc': return ident1; - case 'select': return ident1 + ' ? ' + ident2 + ' : ' + ident3; + case 'zext': case 'fpext': case 'sext': return idents[0]; + case 'fptrunc': return idents[0]; + case 'select': return idents[0] + ' ? ' + idents[1] + ' : ' + idents[2]; case 'ptrtoint': case 'inttoptr': { var ret = ''; if (QUANTUM_SIZE == 1) { @@ -1793,9 +1833,9 @@ function processMathop(item) { 'The safest thing is to investigate every appearance, and modify the source code to avoid this. ' + 'Emscripten will print a list of the .ll lines, and also annotate the .js.'); dprint(' ' + op + ' on .ll line ' + item.lineNum); - ident1 += ' /* Warning: ' + op + ', .ll line ' + item.lineNum + ' */'; + idents[0] += ' /* Warning: ' + op + ', .ll line ' + item.lineNum + ' */'; } - if (op == 'inttoptr' || bitsLeft >= 32) return ident1; + if (op == 'inttoptr' || bitsLeft >= 32) return idents[0]; // For ptrtoint and <32 bits, fall through into trunc since we need to truncate here } case 'trunc': { @@ -1803,23 +1843,22 @@ function processMathop(item) { // truncating can change the number, e.g. by truncating to an i1 // in order to get the first bit assert(bitsLeft <= 32, 'Cannot truncate to more than 32 bits, since we use a native & op'); - return '((' + ident1 + ') & ' + (Math.pow(2, bitsLeft)-1) + ')'; + return '((' + idents[0] + ') & ' + (Math.pow(2, bitsLeft)-1) + ')'; } case 'bitcast': { // Most bitcasts are no-ops for us. However, the exception is int to float and float to int - var inType = item.param1.type; + var inType = item.params[0].type; var outType = item.type; if ((inType in Runtime.INT_TYPES && outType in Runtime.FLOAT_TYPES) || (inType in Runtime.FLOAT_TYPES && outType in Runtime.INT_TYPES)) { assert(USE_TYPED_ARRAYS == 2, 'Can only bitcast ints <-> floats with typed arrays mode 2'); - assert(inType == 'i32' || inType == 'float', 'Can only bitcast ints <-> floats with 32 bits (try I64_MODE=1)'); if (inType in Runtime.INT_TYPES) { - return '(tempDoubleI32[0] = ' + ident1 + ',tempDoubleF32[0])'; + return '(tempDoubleI32[0] = ' + idents[0] + ',tempDoubleF32[0])'; } else { - return '(tempDoubleF32[0] = ' + ident1 + ',tempDoubleI32[0])'; + return '(tempDoubleF32[0] = ' + idents[0] + ',tempDoubleI32[0])'; } } - return ident1; + return idents[0]; } default: throw 'Unknown mathcmp op: ' + item.op; } @@ -1837,9 +1876,6 @@ function walkInterdata(item, pre, post, obj) { // TODO if (item.pointer && walkInterdata(item.pointer, pre, post, obj)) return true; if (item.dependent && walkInterdata(item.dependent, pre, post, obj)) return true; var i; - for (i = 1; i <= 4; i++) { - if (item['param'+i] && walkInterdata(item['param'+i], pre, post, obj)) return true; - } if (item.params) { for (i = 0; i <= item.params.length; i++) { if (walkInterdata(item.params[i], pre, post, obj)) return true; @@ -1866,12 +1902,8 @@ function walkAndModifyInterdata(item, pre) { if (item.value && (repl = walkAndModifyInterdata(item.value, pre))) item.value = repl; if (item.pointer && (repl = walkAndModifyInterdata(item.pointer, pre))) item.pointer = repl; if (item.dependent && (repl = walkAndModifyInterdata(item.dependent, pre))) item.dependent = repl; - var i; - for (i = 1; i <= 4; i++) { - if (item['param'+i] && (repl = walkAndModifyInterdata(item['param'+i], pre))) item['param'+i] = repl; - } if (item.params) { - for (i = 0; i <= item.params.length; i++) { + for (var i = 0; i <= item.params.length; i++) { if (repl = walkAndModifyInterdata(item.params[i], pre)) item.params[i] = repl; } } diff --git a/src/postamble.js b/src/postamble.js index 390f9f27..56f0aee1 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -37,7 +37,9 @@ function run(args) { var ret = null; if (Module['_main']) { ret = Module.callMain(args); - exitRuntime(); + if (!Module['noExitRuntime']) { + exitRuntime(); + } } return ret; } diff --git a/src/preamble.js b/src/preamble.js index e957c212..afae5ea7 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -70,17 +70,6 @@ function SAFE_HEAP_ACCESS(dest, type, store, ignore) { } } } -#if USE_TYPED_ARRAYS == 2 -var warned64 = false; -function warn64() { - if (!warned64) { - __ATEXIT__.push({ func: function() { - print('Warning: using a 64-bit type with USE_TYPED_ARRAYS == 2. Depending on I64_MODE this may be problematic.'); - } }); - warned64 = true; - } -} -#endif function SAFE_HEAP_STORE(dest, value, type, ignore) { #if SAFE_HEAP_LOG @@ -106,7 +95,7 @@ function SAFE_HEAP_STORE(dest, value, type, ignore) { #if DOUBLE_MODE == 1 case 'double': assert(dest % 4 == 0); break; #else - case 'double': assert(dest % 4 == 0); warn64(); break; + case 'double': assert(dest % 4 == 0); break; #endif } #endif @@ -131,7 +120,7 @@ function SAFE_HEAP_LOAD(dest, type, unsigned, ignore) { #if DOUBLE_MODE == 1 case 'double': assert(dest % 4 == 0); break; #else - case 'double': assert(dest % 4 == 0); warn64(); break; + case 'double': assert(dest % 4 == 0); break; #endif } #endif @@ -341,7 +330,7 @@ var undef = 0; // tempInt is used for 32-bit signed values or smaller. tempBigInt is used // for 32-bit unsigned values or more than 32 bits. TODO: audit all uses of tempInt var tempValue, tempInt, tempBigInt, tempInt2, tempBigInt2, tempPair, tempBigIntI, tempBigIntR, tempBigIntS, tempBigIntP, tempBigIntD; -#if I64_MODE == 1 +#if USE_TYPED_ARRAYS == 2 var tempI64, tempI64b; #endif @@ -357,13 +346,85 @@ function assert(condition, text) { } } +var globalScope = this; + +// C calling interface. A convenient way to call C functions (in C files, or +// defined with extern "C"). +// +// Note: LLVM optimizations can inline and remove functions, after which you will not be +// able to call them. Adding +// +// __attribute__((used)) +// +// to the function definition will prevent that. +// +// Note: Closure optimizations will minify function names, making +// functions no longer callable. If you run closure (on by default +// in -O2 and above), you should export the functions you will call +// by calling emcc with something like +// +// -s EXPORTED_FUNCTIONS='["_func1","_func2"]' +// +// @param ident The name of the C function (note that C++ functions will be name-mangled - use extern "C") +// @param returnType The return type of the function, one of the JS types 'number' or 'string' (use 'number' for any C pointer). +// @param argTypes An array of the types of arguments for the function (if there are no arguments, this can be ommitted). Types are as in returnType. +// @param args An array of the arguments to the function, as native JS values (as in returnType) +// Note that string arguments will be stored on the stack (the JS string will become a C string on the stack). +// @return The return value, as a native JS value (as in returnType) +function ccall(ident, returnType, argTypes, args) { + function toC(value, type) { + if (type == 'string') { + var ret = STACKTOP; + Runtime.stackAlloc(value.length+1); + writeStringToMemory(value, ret); + return ret; + } + return value; + } + function fromC(value, type) { + if (type == 'string') { + return Pointer_stringify(value); + } + return value; + } + try { + var func = eval('_' + ident); + } catch(e) { + try { + func = globalScope['Module']['_' + ident]; // closure exported function + } catch(e) {} + } + assert(func, 'Cannot call unknown function ' + ident + ' (perhaps LLVM optimizations or closure removed it?)'); + var i = 0; + var cArgs = args ? args.map(function(arg) { + return toC(arg, argTypes[i++]); + }) : []; + return fromC(func.apply(null, cArgs), returnType); +} +Module["ccall"] = ccall; + +// Returns a native JS wrapper for a C function. This is similar to ccall, but +// returns a function you can call repeatedly in a normal way. For example: +// +// var my_function = cwrap('my_c_function', 'number', ['number', 'number']); +// alert(my_function(5, 22)); +// alert(my_function(99, 12)); +// +function cwrap(ident, returnType, argTypes) { + // TODO: optimize this, eval the whole function once instead of going through ccall each time + return function() { + return ccall(ident, returnType, argTypes, Array.prototype.slice.call(arguments)); + } +} + // Sets a value in memory in a dynamic way at run-time. Uses the // type data. This is the same as makeSetValue, except that // makeSetValue is done at compile-time and generates the needed // code then, whereas this function picks the right code at // run-time. // Note that setValue and getValue only do *aligned* writes and reads! - +// Note that ccall uses JS types as for defining types, while setValue and +// getValue need LLVM types ('i8', 'i32') - this is a lower-level operation function setValue(ptr, value, type, noSafe) { type = type || 'i8'; if (type[type.length-1] === '*') type = 'i32'; // pointers are 32-bit @@ -398,7 +459,6 @@ function setValue(ptr, value, type, noSafe) { Module['setValue'] = setValue; // Parallel to setValue. - function getValue(ptr, type, noSafe) { type = type || 'i8'; if (type[type.length-1] === '*') type = 'i32'; // pointers are 32-bit @@ -478,7 +538,7 @@ function allocate(slab, types, allocator) { assert(type, 'Must know what type to store in allocate!'); #endif -#if I64_MODE == 1 +#if USE_TYPED_ARRAYS == 2 if (type == 'i64') type = 'i32'; // special case: we have one i32 here, and one i32 later #endif @@ -523,7 +583,7 @@ var FUNCTION_TABLE; // XXX: In theory the indexes here can be equal to pointers var PAGE_SIZE = 4096; function alignMemoryPage(x) { - return Math.ceil(x/PAGE_SIZE)*PAGE_SIZE; + return ((x+4095)>>12)<<12; } var HEAP; @@ -534,7 +594,7 @@ var FHEAP; #endif #endif #if USE_TYPED_ARRAYS == 2 -var HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32; +var HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64; #endif var STACK_ROOT, STACKTOP, STACK_MAX; @@ -571,6 +631,7 @@ function enlargeMemory() { HEAPU16 = new Uint16Array(buffer); HEAPU32 = new Uint32Array(buffer); HEAPF32 = new Float32Array(buffer); + HEAPF64 = new Float64Array(buffer); HEAP8.set(oldHEAP8); #endif } @@ -602,6 +663,7 @@ var FAST_MEMORY = Module['FAST_MEMORY'] || {{{ FAST_MEMORY }}}; HEAPU16 = new Uint16Array(buffer); HEAPU32 = new Uint32Array(buffer); HEAPF32 = new Float32Array(buffer); + HEAPF64 = new Float64Array(buffer); // Endianness check (note: assumes compiler arch was little-endian) HEAP32[0] = 255; @@ -609,7 +671,7 @@ var FAST_MEMORY = Module['FAST_MEMORY'] || {{{ FAST_MEMORY }}}; #endif #else // Make sure that our HEAP is implemented as a flat array. - HEAP = new Array(TOTAL_MEMORY); + HEAP = []; // Hinting at the size with |new Array(TOTAL_MEMORY)| should help in theory but makes v8 much slower for (var i = 0; i < FAST_MEMORY; i++) { HEAP[i] = 0; // XXX We do *not* use {{| makeSetValue(0, 'i', 0, 'null') |}} here, since this is done just to optimize runtime speed } @@ -637,6 +699,7 @@ Module['HEAPU8'] = HEAPU8; Module['HEAPU16'] = HEAPU16; Module['HEAPU32'] = HEAPU32; Module['HEAPF32'] = HEAPF32; +Module['HEAPF64'] = HEAPF64; #endif STACK_ROOT = STACKTOP = Runtime.alignMemory(STATICTOP); @@ -647,7 +710,7 @@ var tempDoublePtr = Runtime.alignMemory(STACK_MAX, 8); var tempDoubleI8 = HEAP8.subarray(tempDoublePtr); var tempDoubleI32 = HEAP32.subarray(tempDoublePtr >> 2); var tempDoubleF32 = HEAPF32.subarray(tempDoublePtr >> 2); -var tempDoubleF64 = new Float64Array(HEAP8.buffer).subarray(tempDoublePtr >> 3); +var tempDoubleF64 = HEAPF64.subarray(tempDoublePtr >> 3); function copyTempFloat(ptr) { // functions, because inlining this code is increases code size too much tempDoubleI8[0] = HEAP8[ptr]; tempDoubleI8[1] = HEAP8[ptr+1]; @@ -803,6 +866,7 @@ function writeStringToMemory(string, buffer, dontAddNull) { {{{ makeSetValue('buffer', 'i', '0', 'i8') }}} } } +Module['writeStringToMemory'] = writeStringToMemory; var STRING_TABLE = []; diff --git a/src/settings.js b/src/settings.js index 6bca0174..b8dbe06e 100644 --- a/src/settings.js +++ b/src/settings.js @@ -55,12 +55,6 @@ var USE_TYPED_ARRAYS = 2; // Use typed arrays for the heap // TODO: require compiling with -malign-double, which does align doubles var USE_FHEAP = 1; // Relevant in USE_TYPED_ARRAYS == 1. If this is disabled, only IHEAP will be used, and FHEAP // not generated at all. This is useful if your code is 100% ints without floats or doubles -var I64_MODE = 1; // How to implement 64-bit integers: - // 0: As doubles. This will work up to about 53 bits. - // 1: As [low, high]. This will support all 64 bits for bit ops, etc. properly, but will still - // use doubles for addition etc., like mode 0. This mode is slower than - // mode 0, so its only benefit is proper support for 64 bit bitops. - // TODO: Full bignum support var DOUBLE_MODE = 1; // How to load and store 64-bit doubles. Without typed arrays or in typed array mode 1, // this doesn't matter - these values are just values like any other. In typed array mode 2, // a potentialy risk is that doubles may be only 32-bit aligned. Forcing 64-bit alignment @@ -154,8 +148,8 @@ var PGO = 0; // Profile-guided optimization. var PROFILE = 0; // Enables runtime profiling. See test_profiling for a usage example. -var EXPORTED_FUNCTIONS = ['_main']; // Functions that are explicitly exported, so they are guaranteed to - // be accessible outside of the generated code. +var EXPORTED_FUNCTIONS = ['_main', '_malloc', '_free']; // Functions that are explicitly exported, so they are guaranteed to + // be accessible outside of the generated code even after running closure compiler. var IGNORED_FUNCTIONS = []; // Functions that we should not generate, neither a stub nor a complete function. // This is useful if your project code includes a function, and you want to replace diff --git a/src/shell.html b/src/shell.html index 69217b18..2f34ace3 100644 --- a/src/shell.html +++ b/src/shell.html @@ -19,7 +19,11 @@ print: (function() { var element = document.getElementById('output'); return function(text) { - element.innerHTML += text.replace('\n', '<br>', 'g') + '<br>'; + text = text.replace(/&/g, "&"); + text = text.replace(/</g, "<"); + text = text.replace(/>/g, ">"); + text = text.replace('\n', '<br>', 'g'); + element.innerHTML += text + '<br>'; }; })(), canvas: document.getElementById('canvas') diff --git a/src/shell.js b/src/shell.js index d67cc45a..1648218a 100644 --- a/src/shell.js +++ b/src/shell.js @@ -11,6 +11,7 @@ var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIR if (ENVIRONMENT_IS_NODE) { // Expose functionality in the same simple way that the shells work + // Note that we pollute the global namespace here, otherwise we break in node print = function(x) { process['stdout'].write(x + '\n'); }; @@ -29,26 +30,30 @@ if (ENVIRONMENT_IS_NODE) { return ret; }; + load = function(f) { + globalEval(read(f)); + }; + arguments_ = process['argv'].slice(2); } else if (ENVIRONMENT_IS_SHELL) { // Polyfill over SpiderMonkey/V8 differences if (!this['read']) { - read = function(f) { snarf(f) }; + this['read'] = function(f) { snarf(f) }; } - if (!this['arguments']) { + if (typeof scriptArgs != 'undefined') { arguments_ = scriptArgs; - } else { + } else if (typeof arguments != 'undefined') { arguments_ = arguments; } } else if (ENVIRONMENT_IS_WEB) { - print = printErr = function(x) { + this['print'] = printErr = function(x) { console.log(x); }; - read = function(url) { + this['read'] = function(url) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); xhr.send(null); @@ -61,7 +66,7 @@ if (ENVIRONMENT_IS_NODE) { } else if (ENVIRONMENT_IS_WORKER) { // We can do very little here... - load = importScripts; + this['load'] = importScripts; } else { throw 'Unknown runtime environment. Where are we?'; @@ -72,17 +77,17 @@ function globalEval(x) { } if (typeof load == 'undefined' && typeof read != 'undefined') { - load = function(f) { + this['load'] = function(f) { globalEval(read(f)); }; } if (typeof printErr === 'undefined') { - printErr = function(){}; + this['printErr'] = function(){}; } if (typeof print === 'undefined') { - print = printErr; + this['print'] = printErr; } // *** Environment setup code *** diff --git a/system/include/libc/sys/unistd.h b/system/include/libc/sys/unistd.h index 05b9286a..082c5e4d 100644 --- a/system/include/libc/sys/unistd.h +++ b/system/include/libc/sys/unistd.h @@ -260,9 +260,10 @@ int _EXFUN(unlinkat, (int, const char *, int)); #include <sys/features.h> -#define STDIN_FILENO 0 /* standard input file descriptor */ -#define STDOUT_FILENO 1 /* standard output file descriptor */ -#define STDERR_FILENO 2 /* standard error file descriptor */ +/* XXX Emscripten: start these at 1, we can use pointers as equals to file descriptors */ +#define STDIN_FILENO 1 /* standard input file descriptor */ +#define STDOUT_FILENO 2 /* standard output file descriptor */ +#define STDERR_FILENO 3 /* standard error file descriptor */ /* * sysconf values per IEEE Std 1003.1, 2008 Edition diff --git a/system/include/unwind.h b/system/include/unwind.h new file mode 100644 index 00000000..f8d43d0d --- /dev/null +++ b/system/include/unwind.h @@ -0,0 +1,154 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2003 Hewlett-Packard Co + Contributed by David Mosberger-Tang <davidm@hpl.hp.com> + +This file is part of libunwind. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +#ifndef _UNWIND_H +#define _UNWIND_H + +/* For uint64_t */ +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Minimal interface as per C++ ABI draft standard: + + http://www.codesourcery.com/cxx-abi/abi-eh.html */ + +typedef enum + { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8 + } +_Unwind_Reason_Code; + +typedef int _Unwind_Action; + +#define _UA_SEARCH_PHASE 1 +#define _UA_CLEANUP_PHASE 2 +#define _UA_HANDLER_FRAME 4 +#define _UA_FORCE_UNWIND 8 + +struct _Unwind_Context; /* opaque data-structure */ +struct _Unwind_Exception; /* forward-declaration */ + +typedef void (*_Unwind_Exception_Cleanup_Fn) (_Unwind_Reason_Code, + struct _Unwind_Exception *); + +typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) (int, _Unwind_Action, + uint64_t, + struct _Unwind_Exception *, + struct _Unwind_Context *, + void *); + +/* The C++ ABI requires exception_class, private_1, and private_2 to + be of type uint64 and the entire structure to be + double-word-aligned. Please note that exception_class stays 64-bit + even on 32-bit machines for gcc compatibility. */ +struct _Unwind_Exception + { + uint64_t exception_class; + _Unwind_Exception_Cleanup_Fn exception_cleanup; + unsigned long private_1; + unsigned long private_2; + } __attribute__((__aligned__)); + +extern _Unwind_Reason_Code _Unwind_RaiseException (struct _Unwind_Exception *); +extern _Unwind_Reason_Code _Unwind_ForcedUnwind (struct _Unwind_Exception *, + _Unwind_Stop_Fn, void *); +extern void _Unwind_Resume (struct _Unwind_Exception *); +extern void _Unwind_DeleteException (struct _Unwind_Exception *); +extern unsigned long _Unwind_GetGR (struct _Unwind_Context *, int); +extern void _Unwind_SetGR (struct _Unwind_Context *, int, unsigned long); +extern unsigned long _Unwind_GetIP (struct _Unwind_Context *); +extern unsigned long _Unwind_GetIPInfo (struct _Unwind_Context *, int *); +extern void _Unwind_SetIP (struct _Unwind_Context *, unsigned long); +extern unsigned long _Unwind_GetLanguageSpecificData (struct _Unwind_Context*); +extern unsigned long _Unwind_GetRegionStart (struct _Unwind_Context *); + +#ifdef _GNU_SOURCE + +/* Callback for _Unwind_Backtrace(). The backtrace stops immediately + if the callback returns any value other than _URC_NO_REASON. */ +typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn) (struct _Unwind_Context *, + void *); + +/* See http://gcc.gnu.org/ml/gcc-patches/2001-09/msg00082.html for why + _UA_END_OF_STACK exists. */ +# define _UA_END_OF_STACK 16 + +/* If the unwind was initiated due to a forced unwind, resume that + operation, else re-raise the exception. This is used by + __cxa_rethrow(). */ +extern _Unwind_Reason_Code + _Unwind_Resume_or_Rethrow (struct _Unwind_Exception *); + +/* See http://gcc.gnu.org/ml/gcc-patches/2003-09/msg00154.html for why + _Unwind_GetBSP() exists. */ +extern unsigned long _Unwind_GetBSP (struct _Unwind_Context *); + +/* Return the "canonical frame address" for the given context. + This is used by NPTL... */ +extern unsigned long _Unwind_GetCFA (struct _Unwind_Context *); + +/* Return the base-address for data references. */ +extern unsigned long _Unwind_GetDataRelBase (struct _Unwind_Context *); + +/* Return the base-address for text references. */ +extern unsigned long _Unwind_GetTextRelBase (struct _Unwind_Context *); + +/* Call _Unwind_Trace_Fn once for each stack-frame, without doing any + cleanup. The first frame for which the callback is invoked is the + one for the caller of _Unwind_Backtrace(). _Unwind_Backtrace() + returns _URC_END_OF_STACK when the backtrace stopped due to + reaching the end of the call-chain or _URC_FATAL_PHASE1_ERROR if it + stops for any other reason. */ +extern _Unwind_Reason_Code _Unwind_Backtrace (_Unwind_Trace_Fn, void *); + +/* Find the start-address of the procedure containing the specified IP + or NULL if it cannot be found (e.g., because the function has no + unwind info). Note: there is not necessarily a one-to-one + correspondence between source-level functions and procedures: some + functions don't have unwind-info and others are split into multiple + procedures. */ +extern void *_Unwind_FindEnclosingFunction (void *); + +/* See also Linux Standard Base Spec: + http://www.linuxbase.org/spec/refspecs/LSB_1.3.0/gLSB/gLSB/libgcc-s.html */ + +#endif /* _GNU_SOURCE */ + +#ifdef __cplusplus +}; +#endif + +#endif /* _UNWIND_H */ diff --git a/system/lib/debugging.cpp b/system/lib/debugging.cpp index ff9e0d68..5dfaf223 100644 --- a/system/lib/debugging.cpp +++ b/system/lib/debugging.cpp @@ -18,5 +18,7 @@ void __assert_func(const char *file, int line, const char *assertt, const char * abort(); } +//struct _reent *_impure_ptr; + } diff --git a/system/lib/libcxxabi/CREDITS.TXT b/system/lib/libcxxabi/CREDITS.TXT new file mode 100644 index 00000000..a99245f7 --- /dev/null +++ b/system/lib/libcxxabi/CREDITS.TXT @@ -0,0 +1,22 @@ +This file is a partial list of people who have contributed to the LLVM/libc++abi +project. If you have contributed a patch or made some other contribution to +LLVM/libc++abi, please submit a patch to this file to add yourself, and it will be +done! + +The list is sorted by surname and formatted to allow easy grepping and +beautification by scripts. The fields are: name (N), email (E), web-address +(W), PGP key ID and fingerprint (P), description (D), and snail-mail address +(S). + +N: Howard Hinnant +E: hhinnant@apple.com +D: Architect and primary coauthor of libc++abi + +N: Marshall Clow +E: marshall@idio.com +E: mclow.lists@gmail.com +E: mclow@qualcomm.com +D: Architect and primary coauthor of libc++abi + +N: Nick Kledzik +E: kledzik@apple.com diff --git a/system/lib/libcxxabi/LICENSE.TXT b/system/lib/libcxxabi/LICENSE.TXT new file mode 100644 index 00000000..40ce7c7d --- /dev/null +++ b/system/lib/libcxxabi/LICENSE.TXT @@ -0,0 +1,76 @@ +============================================================================== +libc++abi License +============================================================================== + +The libc++abi library is dual licensed under both the University of Illinois +"BSD-Like" license and the MIT license. As a user of this code you may choose +to use it under either license. As a contributor, you agree to allow your code +to be used under both. + +Full text of the relevant licenses is included below. + +============================================================================== + +University of Illinois/NCSA +Open Source License + +Copyright (c) 2009-2010 by the contributors listed in CREDITS.TXT + +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + +============================================================================== + +Copyright (c) 2009-2010 by the contributors listed in CREDITS.TXT + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/system/lib/libcxxabi/Makefile b/system/lib/libcxxabi/Makefile new file mode 100644 index 00000000..62654ef2 --- /dev/null +++ b/system/lib/libcxxabi/Makefile @@ -0,0 +1,28 @@ +OBJECTS = \ + src/private_typeinfo.bc \ + $(NULL) + #src/cxa_vector.bc \ + #src/cxa_virtual.bc \ + #src/temporary.bc \ + #src/cxa_guard.bc \ + #src/cxa_unexpected.bc \ + #src/cxa_exception.bc \ + #src/cxa_aux_runtime.bc \ + #src/exception.bc \ + #src/stdexcept.bc \ + #src/abort_message.bc \ + #src/cxa_personality.bc \ + #src/cxa_new_delete.bc \ + #src/cxa_handlers.bc \ + #src/cxa_exception_storage.bc \ + #src/typeinfo.bc \ + #src/cxa_demangle.bc + +all: libcxxabi.bc + +%.bc: %.cpp + $(CXX) -I./include $< -o $@ + +libcxxabi.bc: $(OBJECTS) + $(CXX) $(OBJECTS) -o libcxxabi.bc + diff --git a/system/lib/libcxxabi/include/cxa_demangle.h b/system/lib/libcxxabi/include/cxa_demangle.h new file mode 100644 index 00000000..46dc9821 --- /dev/null +++ b/system/lib/libcxxabi/include/cxa_demangle.h @@ -0,0 +1,167 @@ +//===-------------------------- cxa_demangle.h ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef _CXA_DEMANGLE_H +#define _CXA_DEMANGLE_H + +#include <cxxabi.h> + +#pragma GCC visibility push(hidden) + +namespace __cxxabiv1 +{ + +namespace __libcxxabi +{ + +class __demangle_tree; +class __node; + +char* +__demangle(__demangle_tree, char*, size_t*, int*); + +struct __demangle_tree_rv +{ + __demangle_tree* ptr_; + + explicit __demangle_tree_rv(__demangle_tree* ptr) + : ptr_(ptr) {} +}; + +class __demangle_tree +{ + const char* __mangled_name_begin_; + const char* __mangled_name_end_; + int __status_; + __node* __root_; + __node* __node_begin_; + __node* __node_end_; + __node* __node_cap_; + __node** __sub_begin_; + __node** __sub_end_; + __node** __sub_cap_; + __node** __t_begin_; + __node** __t_end_; + __node** __t_cap_; + bool __tag_templates_; + bool __fix_forward_references_; + bool __owns_buf_; + + __demangle_tree& operator=(const __demangle_tree&); +public: + __demangle_tree(const char*, char*, size_t); + ~__demangle_tree(); + + __demangle_tree(__demangle_tree&); + __demangle_tree(__demangle_tree_rv); + operator __demangle_tree_rv() {return __demangle_tree_rv(this);} + + int __status() const; + size_t size() const; + char* __get_demangled_name(char*) const; + + void __parse(); + +private: + const char* __parse_encoding(const char*, const char*); + const char* __parse_type(const char*, const char*, + bool = true, bool = false); + const char* __parse_special_name(const char*, const char*); + const char* __parse_name(const char*, const char*); + const char* __parse_bare_function_type(const char*, const char*); + const char* __parse_call_offset(const char*, const char*); + const char* __parse_number(const char*, const char*); + const char* __parse_cv_qualifiers(const char* first, const char* last, + unsigned& cv, bool = false); + const char* __parse_nested_name(const char*, const char*); + const char* __parse_discriminator(const char*, const char*); + const char* __parse_local_name(const char*, const char*); + const char* __parse_unscoped_template_name(const char*, const char*); + const char* __parse_unscoped_name(const char*, const char*); + const char* __parse_operator_name(const char*, const char*, int* = 0); + const char* __parse_unqualified_name(const char*, const char*); + const char* __parse_source_name(const char*, const char*); + const char* __parse_ctor_dtor_name(const char*, const char*); + const char* __parse_unnamed_type_name(const char*, const char*); + const char* __parse_template_args(const char*, const char*); + const char* __parse_template_arg(const char*, const char*); + const char* __parse_expression(const char*, const char*); + const char* __parse_expr_primary(const char*, const char*); + const char* __parse_substitution(const char*, const char*); + const char* __parse_builtin_type(const char*, const char*); + const char* __parse_function_type(const char*, const char*); + const char* __parse_class_enum_type(const char*, const char*); + const char* __parse_array_type(const char*, const char*); + const char* __parse_pointer_to_member_type(const char*, const char*); + const char* __parse_decltype(const char*, const char*); + const char* __parse_template_param(const char*, const char*); + const char* __parse_unresolved_name(const char*, const char*); + const char* __parse_unresolved_type(const char*, const char*); + const char* __parse_base_unresolved_name(const char*, const char*); + const char* __parse_simple_id(const char*, const char*); + const char* __parse_destructor_name(const char*, const char*); + const char* __parse_function_param(const char*, const char*); + const char* __parse_const_cast_expr(const char*, const char*); + const char* __parse_alignof_expr(const char*, const char*); + const char* __parse_call_expr(const char*, const char*); + const char* __parse_conversion_expr(const char*, const char*); + const char* __parse_delete_array_expr(const char*, const char*); + const char* __parse_delete_expr(const char*, const char*); + const char* __parse_dynamic_cast_expr(const char*, const char*); + const char* __parse_dot_star_expr(const char*, const char*); + const char* __parse_dot_expr(const char*, const char*); + const char* __parse_decrement_expr(const char*, const char*); + const char* __parse_new_expr(const char*, const char*); + const char* __parse_increment_expr(const char*, const char*); + const char* __parse_arrow_expr(const char*, const char*); + const char* __parse_reinterpret_cast_expr(const char*, const char*); + const char* __parse_static_cast_expr(const char*, const char*); + const char* __parse_sizeof_type_expr(const char*, const char*); + const char* __parse_sizeof_param_pack_expr(const char*, const char*); + const char* __parse_typeid_expr(const char*, const char*); + const char* __parse_throw_expr(const char*, const char*); + const char* __parse_pack_expansion(const char*, const char*); + const char* __parse_sizeof_function_param_pack_expr(const char*, const char*); + const char* __parse_dot_suffix(const char*, const char*); + const char* __parse_unresolved_qualifier_level(const char*, const char*); + const char* __parse_vector_type(const char*, const char*); + const char* __parse_hex_number(const char*, const char*, unsigned long long&); + + template <class _Tp> bool __make(); + template <class _Tp, class _A0> bool __make(_A0 __a0); + template <class _Tp, class _A0, class _A1> bool __make(_A0 __a0, _A1 __a1); + template <class _Tp, class _A0, class _A1, class _A2> + bool __make(_A0 __a0, _A1 __a1, _A2 __a2); + template <class _Tp, class _A0, class _A1, class _A2, class _A3> + bool __make(_A0 __a0, _A1 __a1, _A2 __a2, _A3 __a3); + template <class _Tp, class _A0, class _A1, class _A2, class _A3, class _A4> + bool __make(_A0 __a0, _A1 __a1, _A2 __a2, _A3 __a3, _A4 __a4); + template <class _Tp, class _A0, class _A1, class _A2, class _A3, class _A4, + class _A5> + bool __make(_A0 __a0, _A1 __a1, _A2 __a2, _A3 __a3, _A4 __a4, _A5 __a5); + + friend + char* + __demangle(__demangle_tree, char*, size_t*, int*); + +}; + +__demangle_tree +__demangle(const char*); + +__demangle_tree +__demangle(const char*, char*, size_t); + +} // __libcxxabi +} // __cxxabiv1 + +#pragma GCC visibility pop + + +#endif // _CXA_DEMANGLE_H diff --git a/system/lib/libcxxabi/include/cxxabi.h b/system/lib/libcxxabi/include/cxxabi.h new file mode 100644 index 00000000..b08fba0e --- /dev/null +++ b/system/lib/libcxxabi/include/cxxabi.h @@ -0,0 +1,175 @@ +//===--------------------------- cxxabi.h ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __CXXABI_H +#define __CXXABI_H + +/* + * This header provides the interface to the C++ ABI as defined at: + * http://www.codesourcery.com/cxx-abi/ + */ + +#include <stddef.h> +#include <stdint.h> + +#define _LIBCPPABI_VERSION 1001 +#define LIBCXXABI_NORETURN __attribute__((noreturn)) + +#ifdef __cplusplus + +namespace std { + class type_info; // forward declaration +} + + +// runtime routines use C calling conventions, but are in __cxxabiv1 namespace +namespace __cxxabiv1 { + extern "C" { + +// 2.4.2 Allocating the Exception Object +extern void * __cxa_allocate_exception(size_t thrown_size) throw(); +extern void __cxa_free_exception(void * thrown_exception) throw(); + +// 2.4.3 Throwing the Exception Object +extern LIBCXXABI_NORETURN void __cxa_throw(void * thrown_exception, + std::type_info * tinfo, void (*dest)(void *)); + +// 2.5.3 Exception Handlers +extern void * __cxa_get_exception_ptr(void * exceptionObject) throw(); +extern void * __cxa_begin_catch(void * exceptionObject) throw(); +extern void __cxa_end_catch(); +extern std::type_info * __cxa_current_exception_type(); + +// 2.5.4 Rethrowing Exceptions +extern LIBCXXABI_NORETURN void __cxa_rethrow(); + + + +// 2.6 Auxiliary Runtime APIs +extern LIBCXXABI_NORETURN void __cxa_bad_cast(void); +extern LIBCXXABI_NORETURN void __cxa_bad_typeid(void); + + + +// 3.2.6 Pure Virtual Function API +extern LIBCXXABI_NORETURN void __cxa_pure_virtual(void); + +// 3.2.7 Deleted Virtual Function API +extern LIBCXXABI_NORETURN void __cxa_deleted_virtual(void); + +// 3.3.2 One-time Construction API +#ifdef LIBCXXABI_ARMEABI +extern int __cxa_guard_acquire(uint32_t*); +extern void __cxa_guard_release(uint32_t*); +extern void __cxa_guard_abort(uint32_t*); +#else +extern int __cxa_guard_acquire(uint64_t*); +extern void __cxa_guard_release(uint64_t*); +extern void __cxa_guard_abort(uint64_t*); +#endif + +// 3.3.3 Array Construction and Destruction API +extern void* __cxa_vec_new(size_t element_count, + size_t element_size, + size_t padding_size, + void (*constructor)(void*), + void (*destructor)(void*) ); + +extern void* __cxa_vec_new2(size_t element_count, + size_t element_size, + size_t padding_size, + void (*constructor)(void*), + void (*destructor)(void*), + void* (*alloc)(size_t), + void (*dealloc)(void*) ); + +extern void* __cxa_vec_new3(size_t element_count, + size_t element_size, + size_t padding_size, + void (*constructor)(void*), + void (*destructor)(void*), + void* (*alloc)(size_t), + void (*dealloc)(void*, size_t) ); + +extern void __cxa_vec_ctor(void* array_address, + size_t element_count, + size_t element_size, + void (*constructor)(void*), + void (*destructor)(void*) ); + + +extern void __cxa_vec_dtor(void* array_address, + size_t element_count, + size_t element_size, + void (*destructor)(void*) ); + + +extern void __cxa_vec_cleanup(void* array_address, + size_t element_count, + size_t element_size, + void (*destructor)(void*) ); + + +extern void __cxa_vec_delete(void* array_address, + size_t element_size, + size_t padding_size, + void (*destructor)(void*) ); + + +extern void __cxa_vec_delete2(void* array_address, + size_t element_size, + size_t padding_size, + void (*destructor)(void*), + void (*dealloc)(void*) ); + + +extern void __cxa_vec_delete3(void* __array_address, + size_t element_size, + size_t padding_size, + void (*destructor)(void*), + void (*dealloc) (void*, size_t)); + + +extern void __cxa_vec_cctor(void* dest_array, + void* src_array, + size_t element_count, + size_t element_size, + void (*constructor) (void*, void*), + void (*destructor)(void*) ); + + +// 3.3.5.3 Runtime API +extern int __cxa_atexit(void (*f)(void*), void* p, void* d); +extern int __cxa_finalize(void*); + + +// 3.4 Demangler API +extern char* __cxa_demangle(const char* mangled_name, + char* output_buffer, + size_t* length, + int* status); + +// Apple additions to support C++ 0x exception_ptr class +// These are primitives to wrap a smart pointer around an exception object +extern void * __cxa_current_primary_exception() throw(); +extern void __cxa_rethrow_primary_exception(void* primary_exception); +extern void __cxa_increment_exception_refcount(void* primary_exception) throw(); +extern void __cxa_decrement_exception_refcount(void* primary_exception) throw(); + +// Apple addition to support std::uncaught_exception() +extern bool __cxa_uncaught_exception() throw(); + + } // extern "C" +} // namespace __cxxabiv1 + +#endif // __cplusplus + +namespace abi = __cxxabiv1; + +#endif // __CXXABI_H diff --git a/system/lib/libcxxabi/lib/buildit b/system/lib/libcxxabi/lib/buildit new file mode 100755 index 00000000..d1d2acda --- /dev/null +++ b/system/lib/libcxxabi/lib/buildit @@ -0,0 +1,90 @@ +#! /bin/sh +# +# Set the $TRIPLE environment variable to your system's triple before +# running this script. If you set $CXX, that will be used to compile +# the library. Otherwise we'll use clang++. + +set -e + +if [ `basename $(pwd)` != "lib" ] +then + echo "current directory must be lib" + exit 1 +fi + +if [ -z "$CXX" ] +then + CXX=clang++ +fi + +if [ -z "$CC" ] +then + CC=clang +fi + +if [ -z $RC_ProjectSourceVersion ] +then + RC_ProjectSourceVersion=1 +fi + +EXTRA_FLAGS="-std=c++0x -stdlib=libc++ -fstrict-aliasing -Wstrict-aliasing=2 -Wnewline-eof" + +case $TRIPLE in + *-apple-*) + if [ -z $RC_XBS ] + then + RC_CFLAGS="-arch i386 -arch x86_64" + fi + SOEXT=dylib + if [ -n "$SDKROOT" ] + then + EXTRA_FLAGS+="-isysroot ${SDKROOT}" + CXX=`xcrun -sdk "${SDKROOT}" -find clang++` + CC=`xcrun -sdk "${SDKROOT}" -find clang` + fi + LDSHARED_FLAGS="-o libc++abi.dylib \ + -dynamiclib -nodefaultlibs \ + -current_version ${RC_ProjectSourceVersion} \ + -compatibility_version 1 \ + -install_name /usr/lib/libc++abi.dylib \ + -lSystem" + ;; + *-*-mingw*) + # FIXME: removing libgcc and libsupc++ dependencies means porting libcxxrt and LLVM/compiler-rt + SOEXT=dll + LDSHARED_FLAGS="-o libc++abi.dll \ + -shared -nodefaultlibs -Wl,--export-all-symbols -Wl,--allow-multiple-definition -Wl,--out-implib,libc++abi.dll.a \ + -lsupc++ -lpthread -lmingw32 -lgcc_s -lgcc -lmoldname -lmingwex -lmsvcr100 -ladvapi32 -lshell32 -luser32 -lkernel32 -lmingw32 -lgcc_s -lgcc -lmoldname -lmingwex -lmsvcrt" + ;; + *) + RC_CFLAGS="-fPIC" + SOEXT=so + LDSHARED_FLAGS="-o libc++abi.so.1.0 \ + -shared -nodefaultlibs -Wl,-soname,libc++abi.so.1 \ + -lpthread -lrt -lc -lstdc++" + ;; +esac + +if [ -z $RC_XBS ] +then + rm -f libc++abi.1.$SOEXT* +fi + +set -x + +for FILE in ../src/*.cpp; do + $CXX -c -g -O3 $RC_CFLAGS $EXTRA_FLAGS -I../include $FILE +done +case $TRIPLE in + *-*-mingw*) + for FILE in ../src/support/win32/*.cpp; do + $CXX -c -g -Os $RC_CFLAGS $EXTRA_FLAGS -I../include $FILE + done + ;; +esac +$CC *.o $RC_CFLAGS $LDSHARED_FLAGS $EXTRA_FLAGS + +if [ -z $RC_XBS ] +then + rm *.o +fi diff --git a/system/lib/libcxxabi/readme.txt b/system/lib/libcxxabi/readme.txt new file mode 100644 index 00000000..0be9a318 --- /dev/null +++ b/system/lib/libcxxabi/readme.txt @@ -0,0 +1 @@ +These files are from libcxxabi, svn revision 151132, Feb 22 2012 diff --git a/system/lib/libcxxabi/src/abort_message.cpp b/system/lib/libcxxabi/src/abort_message.cpp new file mode 100644 index 00000000..7beb86b5 --- /dev/null +++ b/system/lib/libcxxabi/src/abort_message.cpp @@ -0,0 +1,60 @@ +//===------------------------- abort_message.cpp --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include "abort_message.h" + +#pragma GCC visibility push(hidden) + +#if __APPLE__ +# if defined(__has_include) && __has_include(<CrashReporterClient.h>) +# define HAVE_CRASHREPORTERCLIENT_H 1 +# include <CrashReporterClient.h> + + // If any clients of llvm try to link to libCrashReporterClient.a themselves, + // only one crash info struct will be used. + extern "C" { + CRASH_REPORTER_CLIENT_HIDDEN + struct crashreporter_annotations_t gCRAnnotations + __attribute__((section("__DATA," CRASHREPORTER_ANNOTATIONS_SECTION))) + = { CRASHREPORTER_ANNOTATIONS_VERSION, 0, 0, 0, 0, 0, 0 }; + } + +# endif +#endif + +__attribute__((visibility("hidden"), noreturn)) +void abort_message(const char* format, ...) +{ + // write message to stderr +#if __APPLE__ + fprintf(stderr, "libc++abi.dylib: "); +#endif + va_list list; + va_start(list, format); + vfprintf(stderr, format, list); + va_end(list); + fprintf(stderr, "\n"); + +#if __APPLE__ && HAVE_CRASHREPORTERCLIENT_H + // record message in crash report + char* buffer; + va_list list2; + va_start(list2, format); + vasprintf(&buffer, format, list2); + va_end(list2); + CRSetCrashLogMessage(buffer); +#endif + + abort(); +} + +#pragma GCC visibility pop diff --git a/system/lib/libcxxabi/src/abort_message.h b/system/lib/libcxxabi/src/abort_message.h new file mode 100644 index 00000000..2c5cb204 --- /dev/null +++ b/system/lib/libcxxabi/src/abort_message.h @@ -0,0 +1,33 @@ +//===-------------------------- abort_message.h-----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __ABORT_MESSAGE_H_ +#define __ABORT_MESSAGE_H_ + +#include <stdio.h> + +#pragma GCC visibility push(hidden) + +#ifdef __cplusplus +extern "C" { +#endif + +__attribute__((visibility("hidden"), noreturn)) + void abort_message(const char* format, ...) + __attribute__((format(printf, 1, 2))); + + +#ifdef __cplusplus +} +#endif + +#pragma GCC visibility pop + +#endif + diff --git a/system/lib/libcxxabi/src/cxa_aux_runtime.cpp b/system/lib/libcxxabi/src/cxa_aux_runtime.cpp new file mode 100644 index 00000000..abd8091c --- /dev/null +++ b/system/lib/libcxxabi/src/cxa_aux_runtime.cpp @@ -0,0 +1,34 @@ +//===------------------------ cxa_aux_runtime.cpp -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +// +// This file implements the "Auxiliary Runtime APIs" +// http://www.codesourcery.com/public/cxx-abi/abi-eh.html#cxx-aux +//===----------------------------------------------------------------------===// + +#include "cxxabi.h" +#include <typeinfo> + +namespace __cxxabiv1 +{ + +extern "C" +{ + +LIBCXXABI_NORETURN +void __cxa_bad_cast (void) { + throw std::bad_cast(); +} + +LIBCXXABI_NORETURN +void __cxa_bad_typeid(void) { + throw std::bad_typeid(); +} + +} // extern "C" + +} // abi diff --git a/system/lib/libcxxabi/src/cxa_demangle.cpp b/system/lib/libcxxabi/src/cxa_demangle.cpp new file mode 100644 index 00000000..1135c99b --- /dev/null +++ b/system/lib/libcxxabi/src/cxa_demangle.cpp @@ -0,0 +1,10798 @@ +//===-------------------------- cxa_demangle.cpp --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "cxa_demangle.h" + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <stdio.h> +#include <new> +#include <algorithm> +#include <assert.h> + + +#ifdef DEBUGGING + +#include <string> +#include <typeinfo> + +#endif + +namespace __cxxabiv1 +{ + +namespace __libcxxabi +{ + +#pragma GCC visibility push(hidden) + +class __node +{ + __node(const __node&); + __node& operator=(const __node&); +public: + const char* __name_; + size_t __size_; + __node* __left_; + __node* __right_; + long double __value_; + long __cached_size_; +public: + __node() + : __name_(0), __size_(0), __left_(0), __right_(0), __cached_size_(-1) + {} + virtual ~__node() {}; + + void reset_cached_size() + { + __cached_size_ = -1; + if (__left_) + __left_->reset_cached_size(); + if (__right_) + __right_->reset_cached_size(); + } + + virtual size_t first_size() const {return 0;} + virtual size_t second_size() const {return 0;} + virtual size_t size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = first_size() + second_size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const {return buf;} + virtual char* second_demangled_name(char* buf) const {return buf;} + virtual char* get_demangled_name(char* buf) const + { + return second_demangled_name(first_demangled_name(buf)); + } + virtual size_t base_size() const {return size();} + virtual char* get_base_name(char* buf) const + { + return get_demangled_name(buf); + } + + virtual bool ends_with_template(bool parsing = false) const + { + return false; + } + virtual bool is_ctor_dtor_conv() const + { + return false; + } + virtual __node* base_name() const + { + return const_cast<__node*>(this); + } + virtual bool is_reference_or_pointer_to_function_or_array() const + { + return false; + } + virtual bool is_function() const + { + return false; + } + virtual bool is_cv_qualifer() const + { + return false; + } + virtual bool is_array() const + { + return false; + } + + virtual bool fix_forward_references(__node**, __node**) + { + return true; + } + virtual __node* extract_cv(__node*&) const + { + return 0; + } + virtual size_t list_len() const + { + return 0; + } + virtual bool is_sub() const + { + return false; + } +}; + +#ifdef DEBUGGING + +void display(__node* x, int indent = 0) +{ + if (x) + { + for (int i = 0; i < 2*indent; ++i) + printf(" "); + std::string buf(x->size(), '\0'); + x->get_demangled_name(&buf.front()); + printf("%s %s, %p\n", typeid(*x).name(), buf.c_str(), x); + display(x->__left_, indent+1); + display(x->__right_, indent+1); + } +} + +#endif + +class __vtable + : public __node +{ + static const ptrdiff_t n = sizeof("vtable for ") - 1; +public: + __vtable(__node* type) + { + __right_ = type; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = n + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "vtable for ", n); + return __right_->get_demangled_name(buf+n); + } + virtual __node* base_name() const + { + return __right_->base_name(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __VTT + : public __node +{ + static const ptrdiff_t n = sizeof("VTT for ") - 1; +public: + __VTT(__node* type) + { + __right_ = type; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = n + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "VTT for ", n); + return __right_->get_demangled_name(buf+n); + } + virtual __node* base_name() const + { + return __right_->base_name(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __construction_vtable + : public __node +{ + static const ptrdiff_t n = sizeof("construction vtable for ") - 1 + 4; +public: + __construction_vtable(__node* left, __node* right) + { + __left_ = left; + __right_ = right; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = n + __left_->size() + + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "construction vtable for ", n-4); + buf = __left_->get_demangled_name(buf+n-4); + *buf++ = '-'; + *buf++ = 'i'; + *buf++ = 'n'; + *buf++ = '-'; + return __right_->get_demangled_name(buf); + } + virtual __node* base_name() const + { + return __right_->base_name(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = __left_->fix_forward_references(t_begin, t_end); + return r && __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __typeinfo + : public __node +{ + static const ptrdiff_t n = sizeof("typeinfo for ") - 1; +public: + __typeinfo(__node* type) + { + __right_ = type; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = n + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "typeinfo for ", n); + return __right_->get_demangled_name(buf+n); + } + virtual __node* base_name() const + { + return __right_->base_name(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __typeinfo_name + : public __node +{ + static const ptrdiff_t n = sizeof("typeinfo name for ") - 1; +public: + __typeinfo_name(__node* type) + { + __right_ = type; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = n + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "typeinfo name for ", n); + return __right_->get_demangled_name(buf+n); + } + virtual __node* base_name() const + { + return __right_->base_name(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __covariant_return_thunk + : public __node +{ + static const ptrdiff_t n = sizeof("covariant return thunk to ") - 1; +public: + __covariant_return_thunk(__node* type) + { + __right_ = type; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = n + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "covariant return thunk to ", n); + return __right_->get_demangled_name(buf+n); + } + virtual __node* base_name() const + { + return __right_->base_name(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __virtual_thunk + : public __node +{ + static const size_t n = sizeof("virtual thunk to ") - 1; +public: + __virtual_thunk(__node* type) + { + __right_ = type; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = n + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "virtual thunk to ", n); + return __right_->get_demangled_name(buf+n); + } + virtual __node* base_name() const + { + return __right_->base_name(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __non_virtual_thunk + : public __node +{ + static const size_t n = sizeof("non-virtual thunk to ") - 1; +public: + __non_virtual_thunk(__node* type) + { + __right_ = type; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = n + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "non-virtual thunk to ", n); + return __right_->get_demangled_name(buf+n); + } + virtual __node* base_name() const + { + return __right_->base_name(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __guard_variable + : public __node +{ + static const size_t n = sizeof("guard variable for ") - 1; +public: + __guard_variable(__node* type) + { + __right_ = type; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = n + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "guard variable for ", n); + return __right_->get_demangled_name(buf+n); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __reference_temporary + : public __node +{ + static const size_t n = sizeof("reference temporary for ") - 1; +public: + __reference_temporary(__node* type) + { + __right_ = type; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = n + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "reference temporary for ", n); + return __right_->get_demangled_name(buf+n); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __source_name + : public __node +{ +public: + __source_name(const char* __name, unsigned __size) + { + __name_ = __name; + __size_ = __size; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__size_ >= 10 && strncmp(__name_, "_GLOBAL__N", 10) == 0) + const_cast<long&>(__cached_size_) = 21; + else + const_cast<long&>(__cached_size_) = __size_; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__size_ >= 10 && strncmp(__name_, "_GLOBAL__N", 10) == 0) + return strncpy(buf, "(anonymous namespace)", 21) + 21; + return strncpy(buf, __name_, __size_) + __size_; + } +}; + +class __operator_new + : public __node +{ +public: + + virtual size_t first_size() const {return sizeof("operator new") - 1;} + virtual char* first_demangled_name(char* buf) const + { + return strncpy(buf, "operator new", sizeof("operator new") - 1) + + sizeof("operator new") - 1; + } +}; + +class __operator_new_array + : public __node +{ +public: + + virtual size_t first_size() const {return sizeof("operator new[]") - 1;} + virtual char* first_demangled_name(char* buf) const + { + return strncpy(buf, "operator new[]", sizeof("operator new[]") - 1) + + sizeof("operator new[]") - 1; + } +}; + +class __operator_delete + : public __node +{ +public: + + virtual size_t first_size() const {return sizeof("operator delete") - 1;} + virtual char* first_demangled_name(char* buf) const + { + return strncpy(buf, "operator delete", sizeof("operator delete") - 1) + + sizeof("operator delete") - 1; + } +}; + +class __operator_delete_array + : public __node +{ +public: + + virtual size_t first_size() const {return sizeof("operator delete[]") - 1;} + virtual char* first_demangled_name(char* buf) const + { + return strncpy(buf, "operator delete[]", sizeof("operator delete[]") - 1) + + sizeof("operator delete[]") - 1; + } +}; + +class __operator_logical_and + : public __node +{ +public: + + __operator_logical_and() {} + __operator_logical_and(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 8 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator&&") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") && (", 6); + buf += 6; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator&&", sizeof("operator&&") - 1); + buf += sizeof("operator&&") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_addressof + : public __node +{ +public: + + __operator_addressof() {} + explicit __operator_addressof(__node* op) + { + __left_ = op; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = 3+__left_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator&") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '&'; + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator&", sizeof("operator&") - 1); + buf += sizeof("operator&") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + if (__left_) + return __left_->fix_forward_references(t_begin, t_end); + return true; + } +}; + +class __operator_bit_and + : public __node +{ +public: + + __operator_bit_and() {} + __operator_bit_and(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 7 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator&") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") & (", 5); + buf += 5; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator&", sizeof("operator&") - 1); + buf += sizeof("operator&") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_and_equal + : public __node +{ +public: + + __operator_and_equal() {} + __operator_and_equal(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 8 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator&=") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") &= (", 6); + buf += 6; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator&=", sizeof("operator&=") - 1); + buf += sizeof("operator&=") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_equal + : public __node +{ +public: + + __operator_equal() {} + __operator_equal(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 7 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator=") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") = (", 5); + buf += 5; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator=", sizeof("operator=") - 1); + buf += sizeof("operator=") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_alignof_type + : public __node +{ +public: + + __operator_alignof_type() {} + __operator_alignof_type(__node* op) + { + __right_ = op; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__right_) + const_cast<long&>(__cached_size_) = __right_->size() + 10; + else + const_cast<long&>(__cached_size_) = sizeof("operator alignof") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__right_) + { + strncpy(buf, "alignof (", 9); + buf += 9; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator alignof", sizeof("operator alignof") - 1); + buf += sizeof("operator alignof") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + if (__right_) + return __right_->fix_forward_references(t_begin, t_end); + return true; + } +}; + +class __operator_alignof_expression + : public __node +{ +public: + + __operator_alignof_expression() {} + __operator_alignof_expression(__node* op) + { + __right_ = op; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__right_) + const_cast<long&>(__cached_size_) = __right_->size() + 10; + else + const_cast<long&>(__cached_size_) = sizeof("operator alignof") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__right_) + { + strncpy(buf, "alignof (", 9); + buf += 9; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator alignof", sizeof("operator alignof") - 1); + buf += sizeof("operator alignof") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + if (__right_) + return __right_->fix_forward_references(t_begin, t_end); + return true; + } +}; + +class __operator_paren + : public __node +{ +public: + + virtual size_t first_size() const {return sizeof("operator()") - 1;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "operator()", sizeof("operator()") - 1); + return buf + sizeof("operator()") - 1; + } +}; + +class __operator_comma + : public __node +{ +public: + + __operator_comma() {} + __operator_comma(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 7 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator,") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") , (", 5); + buf += 5; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator,", sizeof("operator,") - 1); + buf += sizeof("operator,") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_tilda + : public __node +{ +public: + + __operator_tilda() {} + explicit __operator_tilda(__node* op) + { + __left_ = op; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = 3+__left_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator~") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '~'; + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator~", sizeof("operator~") - 1); + buf += sizeof("operator~") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + if (__left_) + return __left_->fix_forward_references(t_begin, t_end); + return true; + } +}; + +class __operator_cast + : public __node +{ + static const size_t n = sizeof("operator ") - 1; +public: + + explicit __operator_cast(__node* type) + { + __right_ = type; + } + __operator_cast(__node* type, __node* arg) + { + __size_ = 1; + __right_ = type; + __left_ = arg; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + size_t off; + if (__size_) + { + off = 4; + off += __right_->size(); + if (__left_) + off += __left_->size(); + } + else + off = n + __right_->size();; + const_cast<long&>(__cached_size_) = off; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__size_) + { + *buf++ = '('; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + *buf++ = '('; + if (__left_) + buf = __left_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator ", n); + buf = __right_->get_demangled_name(buf+n); + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } + virtual bool is_ctor_dtor_conv() const + { + return true; + } +}; + +class __cast_literal + : public __node +{ +public: + + __cast_literal(__node* type, const char* f, const char* l) + { + __left_ = type; + __name_ = f; + __size_ = l - f; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = 2 + __left_->size() + __size_; + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + *buf++ = ')'; + strncpy(buf, __name_, __size_); + return buf + __size_; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end); + } +}; + +class __operator_dereference + : public __node +{ +public: + + __operator_dereference() {} + explicit __operator_dereference(__node* op) + { + __left_ = op; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = 3+__left_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator*") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '*'; + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator*", sizeof("operator*") - 1); + buf += sizeof("operator*") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + if (__left_) + return __left_->fix_forward_references(t_begin, t_end); + return true; + } +}; + +class __operator_divide + : public __node +{ +public: + + __operator_divide() {} + __operator_divide(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 7 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator/") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") / (", 5); + buf += 5; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator/", sizeof("operator/") - 1); + buf += sizeof("operator/") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_divide_equal + : public __node +{ +public: + + __operator_divide_equal() {} + __operator_divide_equal(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 8 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator/=") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") /= (", 6); + buf += 6; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator/=", sizeof("operator/=") - 1); + buf += sizeof("operator/=") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_xor + : public __node +{ +public: + + __operator_xor() {} + __operator_xor(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 7 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator^") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") ^ (", 5); + buf += 5; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator^", sizeof("operator^") - 1); + buf += sizeof("operator^") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_xor_equal + : public __node +{ +public: + + __operator_xor_equal() {} + __operator_xor_equal(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 8 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator^=") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; // strncpy(buf, "(", 1); + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") ^= (", 6); + buf += 6; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator^=", sizeof("operator^=") - 1); + buf += sizeof("operator^=") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_equality + : public __node +{ +public: + + __operator_equality() {} + __operator_equality(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 8 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator==") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") == (", 6); + buf += 6; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator==", sizeof("operator==") - 1); + buf += sizeof("operator==") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_greater_equal + : public __node +{ +public: + + __operator_greater_equal() {} + __operator_greater_equal(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 8 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator>=") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") >= (", 6); + buf += 6; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator>=", sizeof("operator>=") - 1); + buf += sizeof("operator>=") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_greater + : public __node +{ +public: + + __operator_greater() {} + __operator_greater(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 9 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator>") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") > (", 5); + buf += 5; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + *buf++ = ')'; + } + else + { + strncpy(buf, "operator>", sizeof("operator>") - 1); + buf += sizeof("operator>") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_brackets + : public __node +{ +public: + + virtual size_t first_size() const {return sizeof("operator[]") - 1;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "operator[]", sizeof("operator[]") - 1); + return buf + sizeof("operator[]") - 1; + } +}; + +class __operator_less_equal + : public __node +{ +public: + + __operator_less_equal() {} + __operator_less_equal(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 8 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator<=") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") <= (", 6); + buf += 6; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator<=", sizeof("operator<=") - 1); + buf += sizeof("operator<=") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_less + : public __node +{ +public: + + __operator_less() {} + __operator_less(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 7 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator<") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") < (", 5); + buf += 5; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator<", sizeof("operator<") - 1); + buf += sizeof("operator<") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_left_shift + : public __node +{ +public: + + __operator_left_shift() {} + __operator_left_shift(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 8 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator<<") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") << (", 6); + buf += 6; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator<<", sizeof("operator<<") - 1); + buf += sizeof("operator<<") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_left_shift_equal + : public __node +{ +public: + + __operator_left_shift_equal() {} + __operator_left_shift_equal(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 9 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator<<=") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") <<= (", 7); + buf += 7; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator<<=", sizeof("operator<<=") - 1); + buf += sizeof("operator<<=") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_minus + : public __node +{ +public: + + __operator_minus() {} + __operator_minus(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 7 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator-") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") - (", 5); + buf += 5; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator-", sizeof("operator-") - 1); + buf += sizeof("operator-") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_minus_equal + : public __node +{ +public: + + __operator_minus_equal() {} + __operator_minus_equal(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 8 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator-=") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") -= (", 6); + buf += 6; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator-=", sizeof("operator-=") - 1); + buf += sizeof("operator-=") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_times + : public __node +{ +public: + + __operator_times() {} + __operator_times(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 7 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator*") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") * (", 5); + buf += 5; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator*", sizeof("operator*") - 1); + buf += sizeof("operator*") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_times_equal + : public __node +{ +public: + + __operator_times_equal() {} + __operator_times_equal(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 8 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator*=") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") *= (", 6); + buf += 6; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator*=", sizeof("operator*=") - 1); + buf += sizeof("operator*=") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_decrement + : public __node +{ +public: + + __operator_decrement() {} + explicit __operator_decrement(bool prefix, __node* op) + { + __size_ = prefix; + __left_ = op; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = 4+__left_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator--") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + if (__size_) + { + *buf++ = '-'; + *buf++ = '-'; + *buf++ = '('; + } + else + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + if (__size_) + *buf++ = ')'; + else + { + *buf++ = ')'; + *buf++ = '-'; + *buf++ = '-'; + } + } + else + { + strncpy(buf, "operator--", sizeof("operator--") - 1); + buf += sizeof("operator--") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + if (__left_) + return __left_->fix_forward_references(t_begin, t_end); + return true; + } +}; + +class __operator_not_equal + : public __node +{ +public: + + __operator_not_equal() {} + __operator_not_equal(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 8 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator!=") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") != (", 6); + buf += 6; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator!=", sizeof("operator!=") - 1); + buf += sizeof("operator!=") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_negate + : public __node +{ +public: + + __operator_negate() {} + explicit __operator_negate(__node* op) + { + __left_ = op; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = 3+__left_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator-") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '-'; + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator-", sizeof("operator-") - 1); + buf += sizeof("operator-") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + if (__left_) + return __left_->fix_forward_references(t_begin, t_end); + return true; + } +}; + +class __operator_logical_not + : public __node +{ +public: + + __operator_logical_not() {} + explicit __operator_logical_not(__node* op) + { + __left_ = op; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = 3+__left_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator!") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '!'; + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator!", sizeof("operator!") - 1); + buf += sizeof("operator!") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + if (__left_) + return __left_->fix_forward_references(t_begin, t_end); + return true; + } +}; + +class __operator_logical_or + : public __node +{ +public: + + __operator_logical_or() {} + __operator_logical_or(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 8 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator||") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") || (", 6); + buf += 6; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator||", sizeof("operator||") - 1); + buf += sizeof("operator||") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_bit_or + : public __node +{ +public: + + __operator_bit_or() {} + __operator_bit_or(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 7 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator|") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") | (", 5); + buf += 5; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator|", sizeof("operator|") - 1); + buf += sizeof("operator|") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_or_equal + : public __node +{ +public: + + __operator_or_equal() {} + __operator_or_equal(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 8 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator|=") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") |= (", 6); + buf += 6; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator|=", sizeof("operator|=") - 1); + buf += sizeof("operator|=") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_pointer_to_member + : public __node +{ +public: + + __operator_pointer_to_member() {} + __operator_pointer_to_member(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 9 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator->*") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") ->* (", 7); + buf += 7; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator->*", sizeof("operator->*") - 1); + buf += sizeof("operator->*") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_plus + : public __node +{ +public: + + __operator_plus() {} + __operator_plus(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 7 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator+") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") + (", 5); + buf += 5; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator+", sizeof("operator+") - 1); + buf += sizeof("operator+") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_plus_equal + : public __node +{ +public: + + __operator_plus_equal() {} + __operator_plus_equal(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 8 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator+=") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") += (", 6); + buf += 6; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator+=", sizeof("operator+=") - 1); + buf += sizeof("operator+=") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_increment + : public __node +{ +public: + + __operator_increment() {} + explicit __operator_increment(bool prefix, __node* op) + { + __size_ = prefix; + __left_ = op; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = 4+__left_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator++") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + if (__size_) + { + *buf++ = '+'; + *buf++ = '+'; + *buf++ = '('; + } + else + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + if (__size_) + *buf++ = ')'; + else + { + *buf++ = ')'; + *buf++ = '+'; + *buf++ = '+'; + } + } + else + { + strncpy(buf, "operator++", sizeof("operator++") - 1); + buf += sizeof("operator++") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + if (__left_) + return __left_->fix_forward_references(t_begin, t_end); + return true; + } +}; + +class __operator_unary_plus + : public __node +{ +public: + + __operator_unary_plus() {} + explicit __operator_unary_plus(__node* op) + { + __left_ = op; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = 3+__left_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator+") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '+'; + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator+", sizeof("operator+") - 1); + buf += sizeof("operator+") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + if (__left_) + return __left_->fix_forward_references(t_begin, t_end); + return true; + } +}; + +class __operator_arrow + : public __node +{ +public: + + __operator_arrow() {} + __operator_arrow(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 8 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator->") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") -> (", 6); + buf += 6; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator->", sizeof("operator->") - 1); + buf += sizeof("operator->") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_conditional + : public __node +{ +public: + + __operator_conditional() {} + __operator_conditional(__node* op1, __node* op2, __node* op3) + { + __name_ = (const char*)op1; + __left_ = op2; + __right_ = op3; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + { + __node* op1 = (__node*)__name_; + const_cast<long&>(__cached_size_) = op1->size() + __left_->size() + 12 + __right_->size(); + } + else + const_cast<long&>(__cached_size_) = sizeof("operator?") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + __node* op1 = (__node*)__name_; + *buf++ = '('; + buf = op1->get_demangled_name(buf); + strncpy(buf, ") ? (", 5); + buf += 5; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") : (", 5); + buf += 5; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator?", sizeof("operator?") - 1); + buf += sizeof("operator?") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__name_) + r = r && ((__node*)__name_)->fix_forward_references(t_begin, t_end); + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_mod + : public __node +{ +public: + + __operator_mod() {} + __operator_mod(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 7 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator%") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") % (", 5); + buf += 5; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator%", sizeof("operator%") - 1); + buf += sizeof("operator%") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_mod_equal + : public __node +{ +public: + + __operator_mod_equal() {} + __operator_mod_equal(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 8 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator%=") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") %= (", 6); + buf += 6; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator%=", sizeof("operator%=") - 1); + buf += sizeof("operator%=") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_right_shift + : public __node +{ +public: + + __operator_right_shift() {} + __operator_right_shift(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 8 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator>>") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") >> (", 6); + buf += 6; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator>>", sizeof("operator>>") - 1); + buf += sizeof("operator>>") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_right_shift_equal + : public __node +{ +public: + + __operator_right_shift_equal() {} + __operator_right_shift_equal(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_) + const_cast<long&>(__cached_size_) = __left_->size() + 9 + __right_->size(); + else + const_cast<long&>(__cached_size_) = sizeof("operator>>=") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + strncpy(buf, ") >>= (", 7); + buf += 7; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator>>=", sizeof("operator>>=") - 1); + buf += sizeof("operator>>=") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __operator_sizeof_type + : public __node +{ +public: + + __operator_sizeof_type() {} + __operator_sizeof_type(__node* op) + { + __right_ = op; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__right_) + const_cast<long&>(__cached_size_) = __right_->size() + 9; + else + const_cast<long&>(__cached_size_) = sizeof("operator sizeof") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__right_) + { + strncpy(buf, "sizeof (", 8); + buf += 8; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator sizeof", sizeof("operator sizeof") - 1); + buf += sizeof("operator sizeof") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + if (__right_) + return __right_->fix_forward_references(t_begin, t_end); + return true; + } +}; + +class __operator_sizeof_expression + : public __node +{ +public: + + __operator_sizeof_expression() {} + __operator_sizeof_expression(__node* op) + { + __right_ = op; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__right_) + const_cast<long&>(__cached_size_) = __right_->size() + 9; + else + const_cast<long&>(__cached_size_) = sizeof("operator sizeof") - 1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__right_) + { + strncpy(buf, "sizeof (", 8); + buf += 8; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + else + { + strncpy(buf, "operator sizeof", sizeof("operator sizeof") - 1); + buf += sizeof("operator sizeof") - 1; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + if (__right_) + return __right_->fix_forward_references(t_begin, t_end); + return true; + } +}; + +class __typeid + : public __node +{ +public: + + __typeid(__node* op) + { + __right_ = op; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = __right_->size() + 8; + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "typeid(", 7); + buf += 7; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __throw + : public __node +{ +public: + + __throw(__node* op) + { + __right_ = op; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = __right_->size() + 6; + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "throw ", 6); + return __right_->get_demangled_name(buf+6); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __rethrow + : public __node +{ + static const ptrdiff_t n = sizeof("throw") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "throw", n); + return buf+n; + } +}; + +class __operator_sizeof_param_pack + : public __node +{ +public: + + __operator_sizeof_param_pack(__node* op) + { + __right_ = op; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = __right_->size() + 11; + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "sizeof...(", 10); + buf += 10; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __const_cast + : public __node +{ +public: + + __const_cast(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = __left_->size() + 14 + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "const_cast<", 11); + buf += 11; + buf = __left_->get_demangled_name(buf); + *buf++ = '>'; + *buf++ = '('; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end) && + __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __dynamic_cast + : public __node +{ +public: + + __dynamic_cast(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = __left_->size() + 16 + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "dynamic_cast<", 13); + buf += 13; + buf = __left_->get_demangled_name(buf); + *buf++ = '>'; + *buf++ = '('; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end) && + __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __reinterpret_cast + : public __node +{ +public: + + __reinterpret_cast(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = __left_->size() + 20 + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "reinterpret_cast<", 17); + buf += 17; + buf = __left_->get_demangled_name(buf); + *buf++ = '>'; + *buf++ = '('; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end) && + __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __static_cast + : public __node +{ +public: + + __static_cast(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = __left_->size() + 15 + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "static_cast<", 12); + buf += 12; + buf = __left_->get_demangled_name(buf); + *buf++ = '>'; + *buf++ = '('; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end) && + __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __call_expr + : public __node +{ +public: + + __call_expr(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + size_t off = __left_->size() + 2; + if (__right_) + off += __right_->size(); + const_cast<long&>(__cached_size_) = off; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + buf = __left_->get_demangled_name(buf); + *buf++ = '('; + if (__right_) + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __delete_array_expr + : public __node +{ +public: + + __delete_array_expr(bool global, __node* op) + { + __size_ = global; + __right_ = op; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = (__size_ ? 2 : 0) + 9 + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__size_) + { + *buf++ = ':'; + *buf++ = ':'; + } + strncpy(buf, "delete[] ", 9); + return __right_->get_demangled_name(buf+9); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __delete_expr + : public __node +{ +public: + + __delete_expr(bool global, __node* op) + { + __size_ = global; + __right_ = op; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = (__size_ ? 2 : 0) + 7 + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__size_) + { + *buf++ = ':'; + *buf++ = ':'; + } + strncpy(buf, "delete ", 7); + return __right_->get_demangled_name(buf+7); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __new_expr + : public __node +{ +public: + + __new_expr(bool global, bool is_array, bool has_init, + __node* expr, __node* type, __node* init) + { + __size_ = (unsigned)global | + ((unsigned)is_array << 1) | + ((unsigned)has_init << 2); + __left_ = expr; + __name_ = (const char*)type; + __right_ = init; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + size_t off = 4; + if (__size_ & 1) + off += 2; + if (__size_ & 2) + off += 2; + if (__left_) + { + off += 2; + off += __left_->size(); + } + __node* type = (__node*)__name_; + off += type->size(); + if (__size_ & 4) + { + off += 2; + if (__right_) + off += __right_->size(); + } + const_cast<long&>(__cached_size_) = off; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__size_ & 1) + { + *buf++ = ':'; + *buf++ = ':'; + } + *buf++ = 'n'; + *buf++ = 'e'; + *buf++ = 'w'; + if (__size_ & 2) + { + *buf++ = '['; + *buf++ = ']'; + } + if (__left_) + { + *buf++ = '('; + buf = __left_->get_demangled_name(buf); + *buf++ = ')'; + } + *buf++ = ' '; + __node* type = (__node*)__name_; + buf = type->get_demangled_name(buf); + if (__size_ & 4) + { + *buf++ = '('; + if (__right_) + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + __node* type = (__node*)__name_; + bool r = type->fix_forward_references(t_begin, t_end); + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end);; + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end);; + return r; + } +}; + +class __dot_star_expr + : public __node +{ +public: + + __dot_star_expr(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = __left_->size() + 2 + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + buf = __left_->get_demangled_name(buf); + *buf++ = '.'; + *buf++ = '*'; + return __right_->get_demangled_name(buf); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end) && + __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __dot_expr + : public __node +{ +public: + + __dot_expr(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = __left_->size() + 1 + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + buf = __left_->get_demangled_name(buf); + *buf++ = '.'; + return __right_->get_demangled_name(buf); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end) && + __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __arrow_expr + : public __node +{ +public: + + __arrow_expr(__node* op1, __node* op2) + { + __left_ = op1; + __right_ = op2; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = __left_->size() + 2 + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + buf = __left_->get_demangled_name(buf); + *buf++ = '-'; + *buf++ = '>'; + return __right_->get_demangled_name(buf); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end) && + __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __std_qualified_name + : public __node +{ + static const ptrdiff_t n = sizeof("std") - 1; +public: + + __std_qualified_name() + { + } + virtual size_t first_size() const + { + return n; + } + + virtual char* first_demangled_name(char* buf) const + { + *buf++ = 's'; + *buf++ = 't'; + *buf++ = 'd'; + return buf; + } +}; + +class __sub_allocator + : public __node +{ + static const ptrdiff_t n = sizeof("std::allocator") - 1; +public: + + virtual size_t first_size() const + { + return n; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "std::allocator", n); + return buf + n; + } +}; + +class __sub_basic_string + : public __node +{ + static const ptrdiff_t n = sizeof("std::basic_string") - 1; +public: + + virtual size_t first_size() const + { + return n; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "std::basic_string", n); + return buf + n; + } +}; + +class __sub_string + : public __node +{ + static const size_t n = sizeof("std::string") - 1; + static const size_t ne = sizeof("std::basic_string<char, std::char_traits<char>, std::allocator<char> >") - 1; +public: + + virtual size_t first_size() const + { + if (__size_) + return ne; + return n; + } + virtual char* first_demangled_name(char* buf) const + { + if (__size_) + { + strncpy(buf, "std::basic_string<char, std::char_traits<char>, std::allocator<char> >", ne); + buf += ne; + } + else + { + strncpy(buf, "std::string", n); + buf += n; + } + return buf; + } + + virtual size_t base_size() const + { + return 12; + } + virtual char* get_base_name(char* buf) const + { + strncpy(buf, "basic_string", 12); + return buf + 12; + } + + virtual __node* base_name() const + { + const_cast<size_t&>(__size_) = true; + return const_cast<__node*>(static_cast<const __node*>(this)); + } +}; + +class __sub_istream + : public __node +{ + static const ptrdiff_t n = sizeof("std::istream") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "std::istream", n); + return buf + n; + } +}; + +class __sub_ostream + : public __node +{ + static const ptrdiff_t n = sizeof("std::ostream") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "std::ostream", n); + return buf + n; + } +}; + +class __sub_iostream + : public __node +{ + static const ptrdiff_t n = sizeof("std::iostream") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "std::iostream", n); + return buf + n; + } +}; + +class __sub + : public __node +{ +public: + + explicit __sub(__node* arg) + { + __left_ = arg; + } + explicit __sub(size_t arg) + { + __size_ = arg; + } + virtual size_t first_size() const + { + return __left_->first_size(); + } + virtual char* first_demangled_name(char* buf) const + { + return __left_->first_demangled_name(buf); + } + virtual size_t second_size() const + { + return __left_->second_size(); + } + virtual char* second_demangled_name(char* buf) const + { + return __left_->second_demangled_name(buf); + } + virtual bool ends_with_template(bool parsing = false) const + { + return __left_->ends_with_template(parsing); + } + virtual __node* base_name() const + { + return __left_->base_name(); + } + virtual bool is_reference_or_pointer_to_function_or_array() const + { + return __left_->is_reference_or_pointer_to_function_or_array(); + } + virtual bool is_function() const + { + return __left_->is_function(); + } + virtual bool is_cv_qualifer() const + { + return __left_->is_cv_qualifer(); + } + virtual bool is_ctor_dtor_conv() const + { + return __left_->is_ctor_dtor_conv(); + } + virtual bool is_array() const + { + return __left_->is_array(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + if (__left_ == 0) + { + if (__size_ < t_end - t_begin) + { + __left_ = t_begin[__size_]; + __size_ = 0; + } + else + return false; + } + return true; + } + virtual size_t list_len() const + { + return __left_->list_len(); + } + virtual bool is_sub() const + { + return true; + } +}; + +class __unscoped_template_name + : public __node +{ +public: + __unscoped_template_name(__node* name, __node* args) + {__left_ = name; __right_ = args;} + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = __left_->size() + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + buf = __left_->get_demangled_name(buf); + return __right_->get_demangled_name(buf); + } + virtual bool ends_with_template(bool parsing = false) const + { + return __right_->ends_with_template(parsing); + } + virtual __node* base_name() const + { + return __left_->base_name(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end) && + __right_->fix_forward_references(t_begin, t_end); + } +}; + +// length == 0: __left_ == NULL +// length == 1: __left_ != NULL, __right_ == NULL +// length > 1: __left_ != NULL, __right_ != NULL +class __list + : public __node +{ +public: + explicit __list(__node* type) + {__left_ = type;} + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + if (__left_ == NULL) + const_cast<long&>(__cached_size_) = 0; + else if (__right_ == NULL) + const_cast<long&>(__cached_size_) = __left_->size(); + else + { + size_t off = __right_->size(); + if (off > 0) + off += 2; + const_cast<long&>(__cached_size_) = __left_->size() + off; + } + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_ != NULL) + { + char* t = __left_->get_demangled_name(buf + (__size_ ? 2 : 0)); + if (__size_ == 0) + buf = t; + else if (t != buf+2) + { + *buf++ = ','; + *buf++ = ' '; + buf = t; + } + if (__right_) + buf = __right_->get_demangled_name(buf); + } + return buf; + } + virtual bool ends_with_template(bool parsing = false) const + { + if (__right_ != NULL) + return __right_->ends_with_template(parsing); + if (__left_ != NULL) + return __left_->ends_with_template(parsing); + return false; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } + virtual size_t list_len() const + { + if (!__left_) + return 0; + if (!__right_) + return 1; + return 1 + __right_->list_len(); + } +}; + +class __template_args + : public __node +{ +public: + __template_args(__node* name, __node* list) + { + __left_ = name; + __right_ = list; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + size_t off = 2; + if (__right_) + { + if (__right_->ends_with_template()) + ++off; + off += __right_->size(); + } + const_cast<long&>(__cached_size_) = __left_->size() + off; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + buf = __left_->get_demangled_name(buf); + *buf++ = '<'; + if (__right_) + { + buf = __right_->get_demangled_name(buf); + if (buf[-1] == '>') + *buf++ = ' '; + } + *buf++ = '>'; + return buf; + } + virtual bool ends_with_template(bool parsing = false) const + { + return true; + } + virtual __node* base_name() const + { + return __left_->base_name(); + } + virtual bool is_ctor_dtor_conv() const + { + return __left_->is_ctor_dtor_conv(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __function_args + : public __node +{ +public: + __function_args(__node* list) + {__right_ = list;} + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = 2 + __right_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + *buf++ = '('; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __cv_qualifiers + : public __node +{ +public: + __cv_qualifiers(size_t cv, __node* type) + { + __left_ = type; + __size_ = __left_->is_function() ? cv << 5 : cv; + } + + virtual size_t first_size() const + { + size_t s = __left_->first_size(); + if (__size_ & 4) + s += sizeof(" restrict")-1; + if (__size_ & 2) + s += sizeof(" volatile")-1; + if (__size_ & 1) + s += sizeof(" const")-1; + if (__size_ & 8) + s += sizeof(" &")-1; + if (__size_ & 16) + s += sizeof(" &&")-1; + return s; + } + virtual char* first_demangled_name(char* buf) const + { + buf = __left_->first_demangled_name(buf); + if (__size_ & 1) + { + const size_t n = sizeof(" const")-1; + strncpy(buf, " const", n); + buf += n; + } + if (__size_ & 2) + { + const size_t n = sizeof(" volatile")-1; + strncpy(buf, " volatile", n); + buf += n; + } + if (__size_ & 4) + { + const size_t n = sizeof(" restrict")-1; + strncpy(buf, " restrict", n); + buf += n; + } + if (__size_ & 8) + { + *buf++ = ' '; + *buf++ = '&'; + } + if (__size_ & 16) + { + *buf++ = ' '; + *buf++ = '&'; + *buf++ = '&'; + } + return buf; + } + virtual size_t second_size() const + { + size_t s = __left_->second_size(); + if (__size_ & 128) + s += sizeof(" restrict")-1; + if (__size_ & 64) + s += sizeof(" volatile")-1; + if (__size_ & 32) + s += sizeof(" const")-1; + if (__size_ & 256) + s += sizeof(" &")-1; + if (__size_ & 512) + s += sizeof(" &&")-1; + return s; + } + virtual char* second_demangled_name(char* buf) const + { + buf = __left_->second_demangled_name(buf); + if (__size_ & 32) + { + const size_t n = sizeof(" const")-1; + strncpy(buf, " const", n); + buf += n; + } + if (__size_ & 64) + { + const size_t n = sizeof(" volatile")-1; + strncpy(buf, " volatile", n); + buf += n; + } + if (__size_ & 128) + { + const size_t n = sizeof(" restrict")-1; + strncpy(buf, " restrict", n); + buf += n; + } + if (__size_ & 256) + { + *buf++ = ' '; + *buf++ = '&'; + } + if (__size_ & 512) + { + *buf++ = ' '; + *buf++ = '&'; + *buf++ = '&'; + } + return buf; + } + virtual __node* base_name() const + { + return __left_->base_name(); + } + virtual bool is_reference_or_pointer_to_function_or_array() const + { + return __left_->is_reference_or_pointer_to_function_or_array(); + } + virtual bool is_function() const + { + return __left_->is_function(); + } + virtual bool is_cv_qualifer() const + { + return true; + } + virtual __node* extract_cv(__node*& rt) const + { + if (rt == this) + { + rt = __left_; + return const_cast<__node*>(static_cast<const __node*>(this)); + } + return 0; + } + virtual bool ends_with_template(bool parsing = false) const + { + if (parsing) + return __left_->ends_with_template(parsing); + return false; + } + virtual bool is_ctor_dtor_conv() const + { + return __left_->is_ctor_dtor_conv(); + } + virtual bool is_array() const + { + return __left_->is_array(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end); + } + virtual size_t list_len() const + { + return __left_->list_len(); + } +}; + +class __extended_qualifier + : public __node +{ +public: + __extended_qualifier(__node* name, __node* type) + { + __left_ = type; + __right_ = name; + __size_ = __left_->is_function() ? 1 : 0; + } + + virtual size_t first_size() const + { + size_t s = __left_->first_size(); + if (__size_ == 0) + s += __right_->size() + 1; + return s; + } + virtual char* first_demangled_name(char* buf) const + { + buf = __left_->first_demangled_name(buf); + if (__size_ == 0) + { + *buf++ = ' '; + buf = __right_->get_demangled_name(buf); + } + return buf; + } + virtual size_t second_size() const + { + size_t s = __left_->second_size(); + if (__size_ == 1) + s += __right_->size() + 1; + return s; + } + virtual char* second_demangled_name(char* buf) const + { + buf = __left_->second_demangled_name(buf); + if (__size_ == 1) + { + *buf++ = ' '; + buf = __right_->get_demangled_name(buf); + } + return buf; + } + virtual __node* base_name() const + { + return __left_->base_name(); + } + virtual bool is_reference_or_pointer_to_function_or_array() const + { + return __left_->is_reference_or_pointer_to_function_or_array(); + } + virtual bool is_function() const + { + return __left_->is_function(); + } + virtual bool is_cv_qualifer() const + { + return true; + } + virtual __node* extract_cv(__node*& rt) const + { + if (rt == this) + { + rt = __left_; + return const_cast<__node*>(static_cast<const __node*>(this)); + } + return 0; + } + virtual bool ends_with_template(bool parsing = false) const + { + return __left_->ends_with_template(parsing); + } + virtual bool is_ctor_dtor_conv() const + { + return __left_->is_ctor_dtor_conv(); + } + virtual bool is_array() const + { + return __left_->is_array(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end); + } + virtual size_t list_len() const + { + return __left_->list_len(); + } +}; + +class __function + : public __node +{ +public: + + __function(__node* name, __node* signature, size_t ret_goes_first = true) + { + __size_ = ret_goes_first; + __left_ = name; + __right_ = signature; + } + + virtual size_t first_size() const + { + size_t off = 0; + if (__size_) + { + off = __right_->first_size(); + if (off > 0 && (__left_ == NULL || + !__right_->__left_->is_reference_or_pointer_to_function_or_array())) + ++off; + } + else + off = 5; + if (__left_) + off += __left_->first_size(); + else + ++off; + return off; + } + + virtual size_t second_size() const + { + size_t off = 0; + if (__left_ == NULL) + off = 1; + off += __right_->second_size(); + if (!__size_) + { + off += 2; + off += __right_->first_size(); + } + return off; + } + + virtual char* first_demangled_name(char* buf) const + { + if (__size_) + { + const char* t = buf; + buf = __right_->first_demangled_name(buf); + if (buf != t && (__left_ == NULL || + !__right_->__left_->is_reference_or_pointer_to_function_or_array())) + *buf++ = ' '; + } + else + { + strncpy(buf, "auto ", 5); + buf += 5; + } + if (__left_) + buf = __left_->first_demangled_name(buf); + else + *buf++ = '('; + return buf; + } + virtual char* second_demangled_name(char* buf) const + { + if (__left_ == NULL) + *buf++ = ')'; + buf = __right_->second_demangled_name(buf); + if (!__size_) + { + *buf++ = '-'; + *buf++ = '>'; + buf = __right_->first_demangled_name(buf); + } + return buf; + } + virtual char* get_demangled_name(char* buf) const + { + if (__size_) + { + const char* t = buf; + buf = __right_->first_demangled_name(buf); + if (buf != t && (__left_ == NULL || + !__right_->__left_->is_reference_or_pointer_to_function_or_array())) + *buf++ = ' '; + } + else + { + strncpy(buf, "auto ", 5); + buf += 5; + } + if (__left_) + buf = __left_->first_demangled_name(buf); + buf = __right_->second_demangled_name(buf); + if (!__size_) + { + *buf++ = '-'; + *buf++ = '>'; + buf = __right_->first_demangled_name(buf); + } + return buf; + } + + virtual size_t size() const + { + if (__cached_size_ == -1) + { + size_t off = 0; + if (__size_) + { + off = __right_->first_size(); + if (off > 0 && (__left_ == NULL || + !__right_->__left_->is_reference_or_pointer_to_function_or_array())) + ++off; + } + else + off = 5; + if (__left_) + off += __left_->first_size(); + off += __right_->second_size(); + if (!__size_) + { + off += 2; + off += __right_->first_size(); + } + const_cast<long&>(__cached_size_) = off; + } + return __cached_size_; + } + + virtual bool is_function() const + { + return true; + } + virtual bool is_ctor_dtor_conv() const + { + return __left_->is_ctor_dtor_conv(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __function_signature + : public __node +{ +public: + __function_signature(__node* ret, __node* args) + { + __left_ = ret; + __right_ = args; + } + virtual size_t first_size() const + { + return __left_ ? __left_->first_size() : 0; + } + + virtual size_t second_size() const + { + return 2 + (__right_ ? __right_->size() : 0) + + (__left_ ? __left_->second_size() : 0); + } + + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + buf = __left_->first_demangled_name(buf); + return buf; + } + + virtual char* second_demangled_name(char* buf) const + { + *buf++ = '('; + if (__right_) + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + if (__left_) + buf = __left_->second_demangled_name(buf); + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = r && __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __pointer_to + : public __node +{ +public: + + explicit __pointer_to(__node* type) + { + __left_ = type; + } + virtual size_t first_size() const + { + return __left_->first_size() + (__left_->is_array() ? 3 : 1); + } + virtual size_t second_size() const + { + return __left_->second_size() + (__left_->is_array() ? 1 : 0); + } + virtual char* first_demangled_name(char* buf) const + { + buf = __left_->first_demangled_name(buf); + if (__left_->is_array()) + { + *buf++ = ' '; + *buf++ = '('; + *buf++ = '*'; + } + else + *buf++ = '*'; + return buf; + } + virtual char* second_demangled_name(char* buf) const + { + if (__left_->is_array()) + *buf++ = ')'; + return __left_->second_demangled_name(buf); + } + virtual __node* base_name() const + { + return __left_->base_name(); + } + virtual bool is_reference_or_pointer_to_function_or_array() const + { + return __left_->is_function() || + __left_->is_reference_or_pointer_to_function_or_array(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end); + } + virtual size_t list_len() const + { + return __left_->list_len(); + } +}; + +class __lvalue_reference_to + : public __node +{ +public: + + explicit __lvalue_reference_to(__node* type) + { + __left_ = type; + } + virtual size_t first_size() const + { + return __left_->first_size() + (__left_->is_array() ? 3 : 1); + } + virtual size_t second_size() const + { + return __left_->second_size() + (__left_->is_array() ? 1 : 0); + } + virtual char* first_demangled_name(char* buf) const + { + buf = __left_->first_demangled_name(buf); + if (__left_->is_array()) + { + *buf++ = ' '; + *buf++ = '('; + *buf++ = '&'; + } + else + *buf++ = '&'; + return buf; + } + virtual char* second_demangled_name(char* buf) const + { + if (__left_->is_array()) + *buf++ = ')'; + return __left_->second_demangled_name(buf); + } + virtual __node* base_name() const + { + return __left_->base_name(); + } + virtual bool is_reference_or_pointer_to_function_or_array() const + { + return __left_->is_function(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end); + } + virtual size_t list_len() const + { + return __left_->list_len(); + } +}; + +class __rvalue_reference_to + : public __node +{ +public: + + explicit __rvalue_reference_to(__node* type) + { + __left_ = type; + } + virtual size_t first_size() const + { + return __left_->first_size() + (__left_->is_array() ? 4 : 2); + } + virtual size_t second_size() const + { + return __left_->second_size() + (__left_->is_array() ? 1 : 0); + } + virtual char* first_demangled_name(char* buf) const + { + buf = __left_->first_demangled_name(buf); + if (__left_->is_array()) + { + strncpy(buf, " (&&", 4); + buf += 4; + } + else + { + *buf++ = '&'; + *buf++ = '&'; + } + return buf; + } + virtual char* second_demangled_name(char* buf) const + { + if (__left_->is_array()) + *buf++ = ')'; + return __left_->second_demangled_name(buf); + } + virtual __node* base_name() const + { + return __left_->base_name(); + } + virtual bool is_reference_or_pointer_to_function_or_array() const + { + return __left_->is_function(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end); + } + virtual size_t list_len() const + { + return __left_->list_len(); + } +}; + +class __d_complex + : public __node +{ + static const size_t n = sizeof(" complex") - 1; +public: + + explicit __d_complex(__node* type) + { + __left_ = type; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = n + __left_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + buf = __left_->get_demangled_name(buf); + strncpy(buf, " complex", n); + return buf + n; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end); + } +}; + +class __imaginary + : public __node +{ + static const size_t n = sizeof(" imaginary") - 1; +public: + + explicit __imaginary(__node* type) + { + __left_ = type; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = n + __left_->size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + buf = __left_->get_demangled_name(buf); + strncpy(buf, " imaginary", n); + return buf + n; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end); + } +}; + +class __pack_expansion + : public __node +{ +public: + + explicit __pack_expansion(__node* type) + { + __left_ = type; + } + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + size_t len = __left_->list_len(); + size_t off = 0; + if (len != 0) + { + if (__left_->is_sub() || len == 1) + off = __left_->size(); + else + { + __node* top = __left_; + __node* bottom = top; + while (!bottom->__left_->is_sub()) + bottom = bottom->__left_; + __node* sub = bottom->__left_; + __node* i = sub->__left_; + bool first = true; + top->reset_cached_size(); + while (i) + { + if (!first) + off += 2; + bottom->__left_ = i->__left_; + off += top->size(); + top->reset_cached_size(); + i = i->__right_; + first = false; + } + bottom->__left_ = sub; + } + } + const_cast<long&>(__cached_size_) = off; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + size_t len = __left_->list_len(); + if (len != 0) + { + if (__left_->is_sub() || len == 1) + buf = __left_->get_demangled_name(buf); + else + { + __node* top = __left_; + __node* bottom = top; + while (!bottom->__left_->is_sub()) + bottom = bottom->__left_; + __node* sub = bottom->__left_; + __node* i = sub->__left_; + bool first = true; + top->reset_cached_size(); + while (i) + { + if (!first) + { + *buf++ = ','; + *buf++ = ' '; + } + bottom->__left_ = i->__left_; + buf = top->get_demangled_name(buf); + top->reset_cached_size(); + i = i->__right_; + first = false; + } + bottom->__left_ = sub; + } + } + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end); + } +}; + +class __void + : public __node +{ + static const size_t n = sizeof("void") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "void", n); + return buf + n; + } +}; + +class __wchar_t + : public __node +{ + static const size_t n = sizeof("wchar_t") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "wchar_t", n); + return buf + n; + } +}; + +class __wchar_t_literal + : public __node +{ +public: + explicit __wchar_t_literal(const char* __first, const char* __last) + { + __name_ = __first; + __size_ = __last - __first; + } + + virtual size_t first_size() const + { + return __size_+9; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "(wchar_t)", 9); + buf += 9; + strncpy(buf, __name_, __size_); + return buf + __size_; + } +}; + +class __bool + : public __node +{ + static const size_t n = sizeof("bool") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "bool", n); + return buf + n; + } +}; + +class __bool_literal + : public __node +{ +public: + explicit __bool_literal(const char* __name, unsigned __size) + { + __name_ = __name; + __size_ = __size; + } + + virtual size_t first_size() const + { + return __size_; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, __name_, __size_); + return buf + __size_; + } +}; + +class __char + : public __node +{ + static const size_t n = sizeof("char") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "char", n); + return buf + n; + } +}; + +class __char_literal + : public __node +{ +public: + explicit __char_literal(const char* __first, const char* __last) + { + __name_ = __first; + __size_ = __last - __first; + } + + virtual size_t first_size() const + { + return __size_+6; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "(char)", 6); + buf += 6; + if (*__name_ == 'n') + { + *buf++ = '-'; // strncpy(buf+6, "-", 1); + strncpy(buf, __name_+1, __size_-1); + buf += __size_ - 1; + } + else + { + strncpy(buf, __name_, __size_); + buf += __size_; + } + return buf; + } +}; + +class __signed_char + : public __node +{ + static const size_t n = sizeof("signed char") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "signed char", n); + return buf + n; + } +}; + +class __signed_char_literal + : public __node +{ +public: + explicit __signed_char_literal(const char* __first, const char* __last) + { + __name_ = __first; + __size_ = __last - __first; + } + + virtual size_t first_size() const + { + return __size_+13; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "(signed char)", 13); + buf += 13; + if (*__name_ == 'n') + { + *buf++ = '-'; + strncpy(buf, __name_+1, __size_-1); + buf += __size_ - 1; + } + else + { + strncpy(buf, __name_, __size_); + buf += __size_; + } + return buf; + } +}; + +class __unsigned_char + : public __node +{ + static const size_t n = sizeof("unsigned char") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "unsigned char", n); + return buf + n; + } +}; + +class __unsigned_char_literal + : public __node +{ +public: + explicit __unsigned_char_literal(const char* __first, const char* __last) + { + __name_ = __first; + __size_ = __last - __first; + } + + virtual size_t first_size() const + { + return __size_+15; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "(unsigned char)", 15); + buf += 15; + strncpy(buf, __name_, __size_); + return buf + __size_; + } +}; + +class __short + : public __node +{ + static const size_t n = sizeof("short") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "short", n); + return buf + n; + } +}; + +class __short_literal + : public __node +{ +public: + explicit __short_literal(const char* __first, const char* __last) + { + __name_ = __first; + __size_ = __last - __first; + } + + virtual size_t first_size() const + { + return __size_+7; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "(short)", 7); + buf += 7; + if (*__name_ == 'n') + { + *buf++ = '-'; + strncpy(buf, __name_+1, __size_-1); + buf += __size_ - 1; + } + else + { + strncpy(buf, __name_, __size_); + buf += __size_; + } + return buf; + } +}; + +class __unsigned_short + : public __node +{ + static const size_t n = sizeof("unsigned short") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "unsigned short", n); + return buf + n; + } +}; + +class __unsigned_short_literal + : public __node +{ +public: + explicit __unsigned_short_literal(const char* __first, const char* __last) + { + __name_ = __first; + __size_ = __last - __first; + } + + virtual size_t first_size() const + { + return __size_+16; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "(unsigned short)", 16); + buf += 16; + strncpy(buf, __name_, __size_); + return buf + __size_; + } +}; + +class __int + : public __node +{ + static const size_t n = sizeof("int") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + *buf++ = 'i'; + *buf++ = 'n'; + *buf++ = 't'; + return buf; + } +}; + +class __int_literal + : public __node +{ +public: + explicit __int_literal(const char* __first, const char* __last) + { + __name_ = __first; + __size_ = __last - __first; + } + + virtual size_t first_size() const + { + return __size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (*__name_ == 'n') + { + *buf++ = '-'; + strncpy(buf, __name_+1, __size_-1); + buf += __size_ - 1; + } + else + { + strncpy(buf, __name_, __size_); + buf += __size_; + } + return buf; + } +}; + +class __unsigned_int + : public __node +{ + static const size_t n = sizeof("unsigned int") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "unsigned int", n); + return buf + n; + } +}; + +class __unsigned_int_literal + : public __node +{ +public: + explicit __unsigned_int_literal(const char* __first, const char* __last) + { + __name_ = __first; + __size_ = __last - __first; + } + + virtual size_t first_size() const + { + return __size_+1; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, __name_, __size_); + buf += __size_; + *buf++ = 'u'; + return buf; + } +}; + +class __long + : public __node +{ + static const size_t n = sizeof("long") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "long", n); + return buf + n; + } +}; + +class __long_literal + : public __node +{ +public: + explicit __long_literal(const char* __first, const char* __last) + { + __name_ = __first; + __size_ = __last - __first; + } + + virtual size_t first_size() const + { + return __size_+1; + } + virtual char* first_demangled_name(char* buf) const + { + if (*__name_ == 'n') + { + *buf++ = '-'; // strncpy(buf, "-", 1); + strncpy(buf, __name_+1, __size_-1); + buf += __size_ - 1; + } + else + { + strncpy(buf, __name_, __size_); + buf += __size_; + } + *buf++ = 'l'; + return buf; + } +}; + +class __unsigned_long + : public __node +{ + static const size_t n = sizeof("unsigned long") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "unsigned long", n); + return buf + n; + } +}; + +class __unsigned_long_literal + : public __node +{ +public: + explicit __unsigned_long_literal(const char* __first, const char* __last) + { + __name_ = __first; + __size_ = __last - __first; + } + + virtual size_t first_size() const + { + return __size_+2; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, __name_, __size_); + buf += __size_; + *buf++ = 'u'; + *buf++ = 'l'; + return buf; + } +}; + +class __long_long + : public __node +{ + static const size_t n = sizeof("long long") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "long long", n); + return buf + n; + } +}; + +class __long_long_literal + : public __node +{ +public: + explicit __long_long_literal(const char* __first, const char* __last) + { + __name_ = __first; + __size_ = __last - __first; + } + + virtual size_t first_size() const + { + return __size_+2; + } + virtual char* first_demangled_name(char* buf) const + { + if (*__name_ == 'n') + { + *buf++ = '-'; + strncpy(buf, __name_+1, __size_-1); + buf += __size_ - 1; + } + else + { + strncpy(buf, __name_, __size_); + buf += __size_; + } + *buf++ = 'l'; + *buf++ = 'l'; + return buf; + } +}; + +class __unsigned_long_long + : public __node +{ + static const size_t n = sizeof("unsigned long long") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "unsigned long long", n); + return buf + n; + } +}; + +class __unsigned_long_long_literal + : public __node +{ +public: + explicit __unsigned_long_long_literal(const char* __first, const char* __last) + { + __name_ = __first; + __size_ = __last - __first; + } + + virtual size_t first_size() const + { + return __size_+3; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, __name_, __size_); + buf += __size_; + *buf++ = 'u'; + *buf++ = 'l'; + *buf++ = 'l'; + return buf; + } +}; + +class __int128 + : public __node +{ + static const size_t n = sizeof("__int128") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "__int128", n); + return buf + n; + } +}; + +class __int128_literal + : public __node +{ +public: + explicit __int128_literal(const char* __first, const char* __last) + { + __name_ = __first; + __size_ = __last - __first; + } + + virtual size_t first_size() const + { + return __size_+10; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "(__int128)", 10); + buf += 10; + if (*__name_ == 'n') + { + *buf++ = '-'; + strncpy(buf, __name_+1, __size_-1); + buf += __size_ - 1; + } + else + { + strncpy(buf, __name_, __size_); + buf += __size_; + } + return buf; + } +}; + +class __unsigned_int128 + : public __node +{ + static const size_t n = sizeof("unsigned __int128") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "unsigned __int128", n); + return buf + n; + } +}; + +class __unsigned_int128_literal + : public __node +{ +public: + explicit __unsigned_int128_literal(const char* __first, const char* __last) + { + __name_ = __first; + __size_ = __last - __first; + } + + virtual size_t first_size() const + { + return __size_+19; + } + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "(unsigned __int128)", 19); + buf += 19; + strncpy(buf, __name_, __size_); + return buf + __size_; + } +}; + +class __float_literal + : public __node +{ +public: + explicit __float_literal(float value) + { + __value_ = value; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + char num[20] = {0}; + float v = static_cast<float>(__value_); + const_cast<long&>(__cached_size_) = sprintf(num, "%a", v)+1; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + char num[20] = {0}; + float v = static_cast<float>(__value_); + int n = sprintf(num, "%a", v); + strncpy(buf, num, n); + buf += n; + *buf++ = 'f'; + return buf; + } +}; + +class __float + : public __node +{ + static const size_t n = sizeof("float") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "float", n); + return buf + n; + } +}; + +class __double_literal + : public __node +{ +public: + explicit __double_literal(double value) + { + __value_ = value; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + char num[30] = {0}; + double v = static_cast<double>(__value_); + const_cast<long&>(__cached_size_) = sprintf(num, "%a", v); + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + char num[30] = {0}; + double v = static_cast<double>(__value_); + int n = sprintf(num, "%a", v); + strncpy(buf, num, n); + return buf + n; + } +}; + +class __double + : public __node +{ + static const size_t n = sizeof("double") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "double", n); + return buf + n; + } +}; + +class __long_double + : public __node +{ + static const size_t n = sizeof("long double") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "long double", n); + return buf + n; + } +}; + +class __float128 + : public __node +{ + static const size_t n = sizeof("__float128") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "__float128", n); + return buf + n; + } +}; + +class __ellipsis + : public __node +{ + static const size_t n = sizeof("...") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + *buf++ = '.'; + *buf++ = '.'; + *buf++ = '.'; + return buf; + } +}; + +class __decimal64 + : public __node +{ + static const size_t n = sizeof("decimal64") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "decimal64", n); + return buf + n; + } +}; + +class __decimal128 + : public __node +{ + static const size_t n = sizeof("decimal128") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "decimal128", n); + return buf + n; + } +}; + +class __decimal32 + : public __node +{ + static const size_t n = sizeof("decimal32") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "decimal32", n); + return buf + n; + } +}; + +class __decimal16 + : public __node +{ + static const size_t n = sizeof("decimal16") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "decimal16", n); + return buf + n; + } +}; + +class __d_char32_t + : public __node +{ + static const size_t n = sizeof("char32_t") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "char32_t", n); + return buf + n; + } +}; + +class __d_char16_t + : public __node +{ + static const size_t n = sizeof("char16_t") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "char16_t", n); + return buf + n; + } +}; + +class __auto + : public __node +{ + static const size_t n = sizeof("auto") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "auto", n); + return buf + n; + } +}; + +class __nullptr_t + : public __node +{ + static const size_t n = sizeof("std::nullptr_t") - 1; +public: + + virtual size_t first_size() const {return n;} + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "std::nullptr_t", n); + return buf + n; + } +}; + +class __array + : public __node +{ +public: + + explicit __array(__node* type) + { + __left_ = type; + } + + __array(__node* type, size_t dim) + { + __left_ = type; + __size_ = dim; + } + + __array(__node* type, __node* dim) + { + __left_ = type; + __right_ = dim; + } + + virtual size_t size() const + { + if (__cached_size_ == -1) + { + size_t r = __left_->size() + 3; + if (__right_ != 0) + r += __right_->size(); + else if (__size_ != 0) + r += snprintf(0, 0, "%ld", __size_); + const_cast<long&>(__cached_size_) = r; + } + return __cached_size_; + } + + virtual char* get_demangled_name(char* buf) const + { + buf = __left_->get_demangled_name(buf); + *buf++ = ' '; + *buf++ = '['; + if (__right_ != 0) + buf = __right_->get_demangled_name(buf); + else if (__size_ != 0) + { + size_t rs = sprintf(buf, "%ld", __size_); + buf += rs; + } + *buf++ = ']'; + return buf; + } + + virtual size_t first_size() const + { + return __left_->first_size(); + } + + virtual char* first_demangled_name(char* buf) const + { + return __left_->first_demangled_name(buf); + } + + virtual size_t second_size() const + { + size_t r = 2 + __left_->second_size(); + if (!__left_->is_array()) + ++r; + if (__right_ != 0) + r += __right_->size(); + else if (__size_ != 0) + r += snprintf(0, 0, "%ld", __size_); + return r; + } + + virtual char* second_demangled_name(char* buf) const + { + *buf++ = ' '; + *buf++ = '['; + if (__right_ != 0) + buf = __right_->get_demangled_name(buf); + else if (__size_ != 0) + { + size_t off = sprintf(buf, "%ld", __size_); + buf += off; + } + char* t = buf; + buf = __left_->second_demangled_name(buf); + *t = ']'; + if (buf == t) + ++buf; + return buf; + } + virtual bool is_array() const + { + return true; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + +class __pointer_to_member_type + : public __node +{ +public: + + __pointer_to_member_type(__node* class_type, __node* member_type) + { + __left_ = class_type; + __right_ = member_type; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = __left_->size() + 3 + + __right_->first_size() + + __right_->second_size(); + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + buf = __right_->first_demangled_name(buf); + buf = __left_->get_demangled_name(buf); + *buf++ = ':'; + *buf++ = ':'; + *buf++ = '*'; + return __right_->second_demangled_name(buf); + } + virtual __node* base_name() const + { + return __left_->base_name(); + } + virtual bool is_reference_or_pointer_to_function_or_array() const + { + return __right_->is_function(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end) && + __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __decltype_node + : public __node +{ +public: + + explicit __decltype_node(__node* expr) + { + __right_ = expr; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = 10 + __right_->size(); + return __cached_size_; + } + + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "decltype(", 9); + buf += 9; + buf = __right_->get_demangled_name(buf); + *buf++ = ')'; + return buf; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __nested_delimeter + : public __node +{ +public: + + explicit __nested_delimeter(__node* prev, __node* arg) + { + __left_ = prev; + __right_ = arg; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = __left_->size() + __right_->size() + 2; + return __cached_size_; + } + + virtual char* first_demangled_name(char* buf) const + { + buf = __left_->get_demangled_name(buf); + *buf++ = ':'; + *buf++ = ':'; + return __right_->get_demangled_name(buf); + } + + virtual bool ends_with_template(bool parsing = false) const + { + return __right_->ends_with_template(parsing); + } + virtual __node* base_name() const + { + return __right_->base_name(); + } + virtual bool is_ctor_dtor_conv() const + { + return __right_->is_ctor_dtor_conv(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end) && + __right_->fix_forward_references(t_begin, t_end); + } + virtual __node* extract_cv(__node*& rt) const + { + return __right_->extract_cv(const_cast<__node*&>(__right_)); + } +}; + +class __unresolved_name + : public __node +{ +public: + + __unresolved_name(__node* prev, __node* arg) + { + __left_ = prev; + __right_ = arg; + } + + __unresolved_name(bool global, __node* prev, __node* arg) + { + __size_ = global; + __left_ = prev; + __right_ = arg; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = (__left_ ? __left_->size() + 2 : 0) + + __right_->size() + __size_ * 2; + return __cached_size_; + } + + virtual char* first_demangled_name(char* buf) const + { + if (__size_) + { + *buf++ = ':'; + *buf++ = ':'; + } + if (__left_) + { + buf = __left_->get_demangled_name(buf); + *buf++ = ':'; + *buf++ = ':'; + } + return __right_->get_demangled_name(buf); + } + + virtual bool ends_with_template(bool parsing = false) const + { + return __right_->ends_with_template(parsing); + } + virtual __node* base_name() const + { + return __right_->base_name(); + } + virtual bool is_ctor_dtor_conv() const + { + return __right_->is_ctor_dtor_conv(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = __left_->fix_forward_references(t_begin, t_end); + return r && __right_->fix_forward_references(t_begin, t_end); + } + virtual __node* extract_cv(__node*& rt) const + { + return __right_->extract_cv(const_cast<__node*&>(__right_)); + } +}; + +class __string_literal + : public __node +{ +public: + + virtual size_t first_size() const + { + return 14; + } + + virtual char* first_demangled_name(char* buf) const + { + strncpy(buf, "string literal", 14); + return buf + 14; + } +}; + +class __constructor + : public __node +{ +public: + + explicit __constructor(__node* name) + { + __right_ = name; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = __right_->base_size(); + return __cached_size_; + } + + virtual char* first_demangled_name(char* buf) const + { + return __right_->get_base_name(buf); + } + virtual __node* base_name() const + { + return __right_->base_name(); + } + virtual bool ends_with_template(bool parsing = false) const + { + return __right_->ends_with_template(parsing); + } + virtual bool is_ctor_dtor_conv() const + { + return true; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __destructor + : public __node +{ +public: + + explicit __destructor(__node* name) + { + __right_ = name; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + const_cast<long&>(__cached_size_) = __right_->base_size() + 1; + return __cached_size_; + } + + virtual char* first_demangled_name(char* buf) const + { + *buf++ = '~'; + return __right_->get_base_name(buf); + } + virtual __node* base_name() const + { + return __right_->base_name(); + } + virtual bool is_ctor_dtor_conv() const + { + return true; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __right_->fix_forward_references(t_begin, t_end); + } +}; + +class __dot_suffix + : public __node +{ +public: + __dot_suffix(__node* name, const char* suffix, unsigned sz) + { + __left_ = name; + __name_ = suffix; + __size_ = sz; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + size_t off = __left_->size(); + off += __size_ + 3; + const_cast<long&>(__cached_size_) = off; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + buf = __left_->get_demangled_name(buf); + *buf++ = ' '; + *buf++ = '('; + strncpy(buf, __name_, __size_); + buf += __size_; + *buf++ = ')'; + return buf; + } + virtual __node* base_name() const + { + return __left_->base_name(); + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + return __left_->fix_forward_references(t_begin, t_end); + } +}; + +class __vector_type + : public __node +{ +public: + __vector_type(__node* type, const char* num, size_t sz) + { + __left_ = type; + __name_ = num; + __size_ = sz; + } + + __vector_type(__node* type, __node* num) + { + __left_ = type; + __right_ = num; + } + + virtual size_t first_size() const + { + if (__cached_size_ == -1) + { + size_t off = 5; + if (__left_) + off = __left_->size(); + off += 9; + if (__right_) + off += __right_->size(); + else if (__size_ > 0) + off += __size_; + const_cast<long&>(__cached_size_) = off; + } + return __cached_size_; + } + virtual char* first_demangled_name(char* buf) const + { + if (__left_) + buf = __left_->get_demangled_name(buf); + else + { + strncpy(buf, "pixel", 5); + buf += 5; + } + strncpy(buf, " vector[", 8); + buf += 8; + if (__right_) + buf = __right_->get_demangled_name(buf); + else if (__size_ > 0) + { + strncpy(buf, __name_, __size_); + buf += __size_; + } + *buf++ = ']'; + return buf; + } + virtual __node* base_name() const + { + if (__left_) + return __left_->base_name(); + return __left_; + } + virtual bool fix_forward_references(__node** t_begin, __node** t_end) + { + bool r = true; + if (__left_) + r = __left_->fix_forward_references(t_begin, t_end); + if (__right_) + r = r && __right_->fix_forward_references(t_begin, t_end); + return r; + } +}; + + +enum {invalid_args = -3, invalid_mangled_name, memory_alloc_failure, success, + not_yet_implemented}; + +__demangle_tree::__demangle_tree(const char* mangled_name, char* buf, size_t bs) + : __mangled_name_begin_(0), __mangled_name_end_(0), + __status_(invalid_mangled_name), __root_(0), + __node_begin_(0), __node_end_(0), __node_cap_(0), + __sub_begin_(0), __sub_end_(0), __sub_cap_(0), + __t_begin_(0), __t_end_(0), __t_cap_(0), + __tag_templates_(true), + __fix_forward_references_(false) +{ + size_t n = strlen(mangled_name); + size_t ms = n + 2*n*sizeof(__node) + 2*n*sizeof(__node*); + char* m; + if (ms <= bs) + { + m = buf; + __owns_buf_ = false; + } + else + { + m = static_cast<char*>(malloc(ms)); + __owns_buf_ = true; + } + if (m == NULL) + { + __status_ = memory_alloc_failure; + return; + } + __node_begin_ = __node_end_ = (__node*)(m); + __node_cap_ = __node_begin_ + 2*n; + __sub_begin_ = __sub_end_ = (__node**)(__node_cap_); + __sub_cap_ = __sub_begin_ + n; + __t_begin_ = __t_end_ = (__node**)(__sub_cap_); + __t_cap_ = __t_begin_ + n; + __mangled_name_begin_ = (const char*)(__t_cap_); + __mangled_name_end_ = __mangled_name_begin_ + n; + strncpy(const_cast<char*>(__mangled_name_begin_), mangled_name, n); +} + +__demangle_tree::~__demangle_tree() +{ + if (__owns_buf_) + free(__node_begin_); +} + +__demangle_tree::__demangle_tree(__demangle_tree& t) + : __mangled_name_begin_(t.__mangled_name_begin_), + __mangled_name_end_(t.__mangled_name_end_), + __status_(t.__status_), __root_(t.__root_), + __node_begin_(t.__node_begin_), __node_end_(t.__node_end_), + __node_cap_(t.__node_cap_), + __sub_begin_(t.__sub_begin_), __sub_end_(t.__sub_end_), + __sub_cap_(t.__sub_cap_), + __t_begin_(t.__t_begin_), __t_end_(t.__t_end_), + __t_cap_(t.__t_cap_), + __tag_templates_(t.__tag_templates_), + __fix_forward_references_(t.__fix_forward_references_), + __owns_buf_(t.__owns_buf_) +{ + t.__mangled_name_begin_ = 0; + t.__mangled_name_end_ = 0; + t.__status_ = invalid_mangled_name; + t.__root_ = 0; + t.__node_begin_ = t.__node_end_ = t.__node_cap_ = 0; + t.__sub_begin_ = t.__sub_end_ = t.__sub_cap_ = 0; + t.__t_begin_ = t.__t_end_ = t.__t_cap_ = 0; + t.__owns_buf_ = false; +} + +__demangle_tree::__demangle_tree(__demangle_tree_rv rv) + : __mangled_name_begin_(rv.ptr_->__mangled_name_begin_), + __mangled_name_end_(rv.ptr_->__mangled_name_end_), + __status_(rv.ptr_->__status_), __root_(rv.ptr_->__root_), + __node_begin_(rv.ptr_->__node_begin_), __node_end_(rv.ptr_->__node_end_), + __node_cap_(rv.ptr_->__node_cap_), + __sub_begin_(rv.ptr_->__sub_begin_), __sub_end_(rv.ptr_->__sub_end_), + __sub_cap_(rv.ptr_->__sub_cap_), + __t_begin_(rv.ptr_->__t_begin_), __t_end_(rv.ptr_->__t_end_), + __t_cap_(rv.ptr_->__t_cap_), + __tag_templates_(rv.ptr_->__tag_templates_), + __fix_forward_references_(rv.ptr_->__fix_forward_references_), + __owns_buf_(rv.ptr_->__owns_buf_) +{ + rv.ptr_->__mangled_name_begin_ = 0; + rv.ptr_->__mangled_name_end_ = 0; + rv.ptr_->__status_ = invalid_mangled_name; + rv.ptr_->__root_ = 0; + rv.ptr_->__node_begin_ = rv.ptr_->__node_end_ = rv.ptr_->__node_cap_ = 0; + rv.ptr_->__sub_begin_ = rv.ptr_->__sub_end_ = rv.ptr_->__sub_cap_ = 0; + rv.ptr_->__t_begin_ = rv.ptr_->__t_end_ = rv.ptr_->__t_cap_ = 0; + rv.ptr_->__owns_buf_ = false; +} + +int +__demangle_tree::__status() const +{ + return __status_; +} + +size_t +__demangle_tree::size() const +{ + return __status_ == success ? __root_->size() : 0; +} + +char* +__demangle_tree::__get_demangled_name(char* buf) const +{ + if (__status_ == success) + return __root_->get_demangled_name(buf); + return 0; +} + +template <class _Tp> +bool +__demangle_tree::__make() +{ + if (__node_end_ < __node_cap_) + { + ::new (__node_end_) _Tp(); + __root_ = __node_end_; + ++__node_end_; + return true; + } + __status_ = memory_alloc_failure; + return false; +} + +template <class _Tp, class _A0> +bool +__demangle_tree::__make(_A0 __a0) +{ + if (__node_end_ < __node_cap_) + { + ::new (__node_end_) _Tp(__a0); + __root_ = __node_end_; + ++__node_end_; + return true; + } + __status_ = memory_alloc_failure; + return false; +} + +template <class _Tp, class _A0, class _A1> +bool +__demangle_tree::__make(_A0 __a0, _A1 __a1) +{ + if (__node_end_ < __node_cap_) + { + ::new (__node_end_) _Tp(__a0, __a1); + __root_ = __node_end_; + ++__node_end_; + return true; + } + __status_ = memory_alloc_failure; + return false; +} + +template <class _Tp, class _A0, class _A1, class _A2> +bool +__demangle_tree::__make(_A0 __a0, _A1 __a1, _A2 __a2) +{ + if (__node_end_ < __node_cap_) + { + ::new (__node_end_) _Tp(__a0, __a1, __a2); + __root_ = __node_end_; + ++__node_end_; + return true; + } + __status_ = memory_alloc_failure; + return false; +} + +template <class _Tp, class _A0, class _A1, class _A2, class _A3> +bool +__demangle_tree::__make(_A0 __a0, _A1 __a1, _A2 __a2, _A3 __a3) +{ + if (__node_end_ < __node_cap_) + { + ::new (__node_end_) _Tp(__a0, __a1, __a2, __a3); + __root_ = __node_end_; + ++__node_end_; + return true; + } + __status_ = memory_alloc_failure; + return false; +} + +template <class _Tp, class _A0, class _A1, class _A2, class _A3, class _A4> +bool +__demangle_tree::__make(_A0 __a0, _A1 __a1, _A2 __a2, _A3 __a3, _A4 __a4) +{ + if (__node_end_ < __node_cap_) + { + ::new (__node_end_) _Tp(__a0, __a1, __a2, __a3, __a4); + __root_ = __node_end_; + ++__node_end_; + return true; + } + __status_ = memory_alloc_failure; + return false; +} + +template <class _Tp, class _A0, class _A1, class _A2, class _A3, class _A4, + class _A5> +bool +__demangle_tree::__make(_A0 __a0, _A1 __a1, _A2 __a2, _A3 __a3, _A4 __a4, + _A5 __a5) +{ + if (__node_end_ < __node_cap_) + { + ::new (__node_end_) _Tp(__a0, __a1, __a2, __a3, __a4, __a5); + __root_ = __node_end_; + ++__node_end_; + return true; + } + __status_ = memory_alloc_failure; + return false; +} + +// <CV-qualifiers> ::= [r] [V] [K] # restrict (C99), volatile, const +// [R | O] # & or && + +const char* +__demangle_tree::__parse_cv_qualifiers(const char* first, const char* last, + unsigned& cv, bool look_for_ref_quals) +{ + if (look_for_ref_quals) + { + for (; first != last; ++first) + { + switch (*first) + { + case 'r': + cv |= 4; + break; + case 'V': + cv |= 2; + break; + case 'K': + cv |= 1; + break; + case 'R': + cv |= 8; + break; + case 'O': + cv |= 16; + break; + default: + return first; + } + } + } + else + { + for (; first != last; ++first) + { + switch (*first) + { + case 'r': + cv |= 4; + break; + case 'V': + cv |= 2; + break; + case 'K': + cv |= 1; + break; + default: + return first; + } + } + } + return first; +} + +// <builtin-type> ::= v # void +// ::= w # wchar_t +// ::= b # bool +// ::= c # char +// ::= a # signed char +// ::= h # unsigned char +// ::= s # short +// ::= t # unsigned short +// ::= i # int +// ::= j # unsigned int +// ::= l # long +// ::= m # unsigned long +// ::= x # long long, __int64 +// ::= y # unsigned long long, __int64 +// ::= n # __int128 +// ::= o # unsigned __int128 +// ::= f # float +// ::= d # double +// ::= e # long double, __float80 +// ::= g # __float128 +// ::= z # ellipsis +// ::= Dd # IEEE 754r decimal floating point (64 bits) +// ::= De # IEEE 754r decimal floating point (128 bits) +// ::= Df # IEEE 754r decimal floating point (32 bits) +// ::= Dh # IEEE 754r half-precision floating point (16 bits) +// ::= Di # char32_t +// ::= Ds # char16_t +// ::= Da # auto (in dependent new-expressions) +// ::= Dn # std::nullptr_t (i.e., decltype(nullptr)) +// ::= u <source-name> # vendor extended type + +const char* +__demangle_tree::__parse_builtin_type(const char* first, const char* last) +{ + if (first != last) + { + switch (*first) + { + case 'v': + if (__make<__void>()) + ++first; + break; + case 'w': + if (__make<__wchar_t>()) + ++first; + break; + case 'b': + if (__make<__bool>()) + ++first; + break; + case 'c': + if (__make<__char>()) + ++first; + break; + case 'a': + if (__make<__signed_char>()) + ++first; + break; + case 'h': + if (__make<__unsigned_char>()) + ++first; + break; + case 's': + if (__make<__short>()) + ++first; + break; + case 't': + if (__make<__unsigned_short>()) + ++first; + break; + case 'i': + if (__make<__int>()) + ++first; + break; + case 'j': + if (__make<__unsigned_int>()) + ++first; + break; + case 'l': + if (__make<__long>()) + ++first; + break; + case 'm': + if (__make<__unsigned_long>()) + ++first; + break; + case 'x': + if (__make<__long_long>()) + ++first; + break; + case 'y': + if (__make<__unsigned_long_long>()) + ++first; + break; + case 'n': + if (__make<__int128>()) + ++first; + break; + case 'o': + if (__make<__unsigned_int128>()) + ++first; + break; + case 'f': + if (__make<__float>()) + ++first; + break; + case 'd': + if (__make<__double>()) + ++first; + break; + case 'e': + if (__make<__long_double>()) + ++first; + break; + case 'g': + if (__make<__float128>()) + ++first; + break; + case 'z': + if (__make<__ellipsis>()) + ++first; + break; + case 'D': + if (first+1 != last) + { + switch (first[1]) + { + case 'd': + if (__make<__decimal64>()) + first += 2; + break; + case 'e': + if (__make<__decimal128>()) + first += 2; + break; + case 'f': + if (__make<__decimal32>()) + first += 2; + break; + case 'h': + if (__make<__decimal16>()) + first += 2; + break; + case 'i': + if (__make<__d_char32_t>()) + first += 2; + break; + case 's': + if (__make<__d_char16_t>()) + first += 2; + break; + case 'a': + if (__make<__auto>()) + first += 2; + break; + case 'n': + if (__make<__nullptr_t>()) + first += 2; + break; + } + } + break; + } + } + return first; +} + +// <bare-function-type> ::= <signature type>+ +// # types are possible return type, then parameter types + +const char* +__demangle_tree::__parse_bare_function_type(const char* first, const char* last) +{ + if (first != last) + { + __tag_templates_ = false; + const char* t = __parse_type(first, last); + if (t != first && __make<__list>(__root_)) + { + const char* t0 = t; + __node* head = __root_; + __node* prev = head; + while (true) + { + t = __parse_type(t0, last); + if (t != t0) + { + if (__make<__list>(__root_)) + { + t0 = t; + prev->__right_ = __root_; + __root_->__size_ = prev->__size_ + 1; + prev = __root_; + } + else + break; + } + else + { + first = t; + __root_ = head; + break; + } + } + } + __tag_templates_ = true; + } + return first; +} + +// <function-type> ::= F [Y] <bare-function-type> E + +const char* +__demangle_tree::__parse_function_type(const char* first, const char* last) +{ + if (first != last && *first == 'F') + { + const char* t = first+1; + if (t != last) + { + bool externC = false; + if (*t == 'Y') + { + externC = true; + if (++t == last) + return first; + } + const char* t1 = __parse_type(t, last); + if (t1 != t) + { + __node* ret = __root_; + t = t1; + t1 = __parse_bare_function_type(t, last); + if (t1 != t && t1 != last && *t1 == 'E') + { + if (dynamic_cast<__void*>(__root_->__left_) != NULL) + __root_->__left_ = NULL; + if (__make<__function_signature>(ret, __root_)) + { + if (__make<__function>((__node*)0, __root_)) + first = t1+1; + } + } + } + } + } + return first; +} + +const char* +__demangle_tree::__parse_hex_number(const char* first, const char* last, unsigned long long& n) +{ + const char* t = first; + for (; t != last && isxdigit(*t); ++t) + { + if (t == first) + n = 0; + if (isdigit(*t)) + n = n * 16 + *t - '0'; + else if (isupper(*t)) + n = n * 16 + *t - 'A' + 10; + else + n = n * 16 + *t - 'a' + 10; + } + first = t; + return first; +} + +// <expr-primary> ::= L <type> <value number> E # integer literal +// ::= L <type> <value float> E # floating literal +// ::= L <string type> E # string literal +// ::= L <nullptr type> E # nullptr literal (i.e., "LDnE") +// ::= L <type> <real-part float> _ <imag-part float> E # complex floating point literal (C 2000) +// ::= L <mangled-name> E # external name + +const char* +__demangle_tree::__parse_expr_primary(const char* first, const char* last) +{ + if (last - first >= 4 && *first == 'L') + { + switch (first[1]) + { + case 'w': + { + const char* t = __parse_number(first+2, last); + if (t != first+2 && t != last && *t == 'E') + { + if (__make<__wchar_t_literal>(first+2, t)) + first = t+1; + } + } + break; + case 'b': + if (first[3] == 'E') + { + switch (first[2]) + { + case '0': + if (__make<__bool_literal>("false", 5)) + first += 4; + break; + case '1': + if (__make<__bool_literal>("true", 4)) + first += 4; + break; + } + } + break; + case 'c': + { + const char* t = __parse_number(first+2, last); + if (t != first+2 && t != last && *t == 'E') + { + if (__make<__char_literal>(first+2, t)) + first = t+1; + } + } + break; + case 'a': + { + const char* t = __parse_number(first+2, last); + if (t != first+2 && t != last && *t == 'E') + { + if (__make<__signed_char_literal>(first+2, t)) + first = t+1; + } + } + break; + case 'h': + { + const char* t = __parse_number(first+2, last); + if (t != first+2 && t != last && *t == 'E') + { + if (__make<__unsigned_char_literal>(first+2, t)) + first = t+1; + } + } + break; + case 's': + { + const char* t = __parse_number(first+2, last); + if (t != first+2 && t != last && *t == 'E') + { + if (__make<__short_literal>(first+2, t)) + first = t+1; + } + } + break; + case 't': + { + const char* t = __parse_number(first+2, last); + if (t != first+2 && t != last && *t == 'E') + { + if (__make<__unsigned_short_literal>(first+2, t)) + first = t+1; + } + } + break; + case 'i': + { + const char* t = __parse_number(first+2, last); + if (t != first+2 && t != last && *t == 'E') + { + if (__make<__int_literal>(first+2, t)) + first = t+1; + } + } + break; + case 'j': + { + const char* t = __parse_number(first+2, last); + if (t != first+2 && t != last && *t == 'E') + { + if (__make<__unsigned_int_literal>(first+2, t)) + first = t+1; + } + } + break; + case 'l': + { + const char* t = __parse_number(first+2, last); + if (t != first+2 && t != last && *t == 'E') + { + if (__make<__long_literal>(first+2, t)) + first = t+1; + } + } + break; + case 'm': + { + const char* t = __parse_number(first+2, last); + if (t != first+2 && t != last && *t == 'E') + { + if (__make<__unsigned_long_literal>(first+2, t)) + first = t+1; + } + } + break; + case 'x': + { + const char* t = __parse_number(first+2, last); + if (t != first+2 && t != last && *t == 'E') + { + if (__make<__long_long_literal>(first+2, t)) + first = t+1; + } + } + break; + case 'y': + { + const char* t = __parse_number(first+2, last); + if (t != first+2 && t != last && *t == 'E') + { + if (__make<__unsigned_long_long_literal>(first+2, t)) + first = t+1; + } + } + break; + case 'n': + { + const char* t = __parse_number(first+2, last); + if (t != first+2 && t != last && *t == 'E') + { + if (__make<__int128_literal>(first+2, t)) + first = t+1; + } + } + break; + case 'o': + { + const char* t = __parse_number(first+2, last); + if (t != first+2 && t != last && *t == 'E') + { + if (__make<__unsigned_int128_literal>(first+2, t)) + first = t+1; + } + } + break; + case 'f': + { + if (last - (first+2) <= 8) + return first; + unsigned long long j; + const char* t = __parse_hex_number(first+2, first+10, j); + if (t != first+2 && t != last && *t == 'E') + { + unsigned i = static_cast<unsigned>(j); + float value = *(float*)&i; + if (__make<__float_literal>(value)) + first = t+1; + } + } + break; + case 'd': + { + if (last - (first+2) <= 16) + return first; + unsigned long long j; + const char* t = __parse_hex_number(first+2, first+18, j); + if (t != first+2 && t != last && *t == 'E') + { + double value = *(double*)&j; + if (__make<__double_literal>(value)) + first = t+1; + } + } + break; + case 'e': + break; + case '_': + if (first[2] == 'Z') + { + const char* t = __parse_encoding(first+3, last); + if (t != first+3 && t != last && *t == 'E') + first = t+1; + } + break; + default: + { + // might be named type + const char* t = __parse_type(first+1, last); + if (t != first+1 && t != last) + { + if (*t != 'E') + { + const char* n = t; + for (; n != last && isdigit(*n); ++n) + ; + if (n != t && n != last && *n == 'E') + { + if (__make<__cast_literal>(__root_, t, n)) + { + first = n+1; + break; + } + } + } + else + { + first = t+1; + break; + } + } + } +// assert(!"case in __parse_expr_primary not implemented"); + __status_ = not_yet_implemented; + } + } + return first; +} + +// <unnamed-type-name> ::= Ut [ <nonnegative number> ] _ +// ::= <closure-type-name> +// +// <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _ +// +// <lambda-sig> ::= <parameter type>+ # Parameter types or "v" if the lambda has no parameters + +const char* +__demangle_tree::__parse_unnamed_type_name(const char* first, const char* last) +{ + if (last - first > 2 && first[0] == 'U') + { + switch (first[1]) + { + case 't': + case 'l': + first += 2; + __status_ = not_yet_implemented; + break; + } + } + return first; +} + +// <ctor-dtor-name> ::= C1 # complete object constructor +// ::= C2 # base object constructor +// ::= C3 # complete object allocating constructor +// ::= D0 # deleting destructor +// ::= D1 # complete object destructor +// ::= D2 # base object destructor + +const char* +__demangle_tree::__parse_ctor_dtor_name(const char* first, const char* last) +{ + if (last-first >= 2) + { + switch (first[0]) + { + case 'C': + switch (first[1]) + { + case '1': + case '2': + case '3': + if (__make<__constructor>(__root_->base_name())) + first += 2; + break; + } + break; + case 'D': + switch (first[1]) + { + case '0': + case '1': + case '2': + if (__make<__destructor>(__root_->base_name())) + first += 2; + break; + } + break; + } + } + return first; +} + +const char* +__demangle_tree::__parse_unscoped_template_name(const char* first, const char* last) +{ +// assert(!"__parse_unscoped_template_name not implemented"); + __status_ = not_yet_implemented; + return first; +} + +// <discriminator> := _ <non-negative number> # when number < 10 +// := __ <non-negative number> _ # when number >= 10 +// extension := decimal-digit+ + +const char* +__demangle_tree::__parse_discriminator(const char* first, const char* last) +{ + // parse but ignore discriminator + if (first != last) + { + if (*first == '_') + { + const char* t1 = first+1; + if (t1 != last) + { + if (isdigit(*t1)) + first = t1+1; + else if (*t1 == '_') + { + for (++t1; t1 != last && isdigit(*t1); ++t1) + ; + if (t1 != last && *t1 == '_') + first = t1 + 1; + } + } + } + else if (isdigit(*first)) + { + const char* t1 = first+1; + for (; t1 != last && isdigit(*t1); ++t1) + ; + first = t1; + } + } + return first; +} + +// <local-name> := Z <function encoding> E <entity name> [<discriminator>] +// := Z <function encoding> E s [<discriminator>] +// := Z <function encoding> Ed [ <parameter number> ] _ <entity name> + +const char* +__demangle_tree::__parse_local_name(const char* first, const char* last) +{ + if (first != last && *first == 'Z') + { + const char* t = __parse_encoding(first+1, last); + if (t != first+1 && t != last && *t == 'E' && ++t != last) + { + __node* encoding = __root_; + switch (*t) + { + case 's': + { + const char*t1 = __parse_discriminator(t+1, last); + if (__make<__string_literal>()) + { + if (__make<__nested_delimeter>(encoding, __root_)) + first = t1; + } + } + break; + case 'd': +// assert(!"__parse_local_name d not implemented"); + __status_ = not_yet_implemented; + break; + default: + { + const char*t1 = __parse_name(t, last); + if (t1 != t) + { + // parse but ignore discriminator + t1 = __parse_discriminator(t1, last); + if (__make<__nested_delimeter>(encoding, __root_)) + first = t1; + } + } + break; + } + } + } + return first; +} + +// <destructor-name> ::= <unresolved-type> # e.g., ~T or ~decltype(f()) +// ::= <simple-id> # e.g., ~A<2*N> + +const char* +__demangle_tree::__parse_destructor_name(const char* first, const char* last) +{ + if (first != last) + { + const char* t = __parse_unresolved_type(first, last); + if (t == first) + t = __parse_simple_id(first, last); + if (t != first && __make<__destructor>(__root_)) + first = t; + } + return first; +} + +// <simple-id> ::= <source-name> [ <template-args> ] + +const char* +__demangle_tree::__parse_simple_id(const char* first, const char* last) +{ + if (first != last) + { + const char* t = __parse_source_name(first, last); + if (t != first) + first = __parse_template_args(t, last); + else + first = t; + } + return first; +} + +// <base-unresolved-name> ::= <simple-id> # unresolved name +// extension ::= <operator-name> # unresolved operator-function-id +// extension ::= <operator-name> <template-args> # unresolved operator template-id +// ::= on <operator-name> # unresolved operator-function-id +// ::= on <operator-name> <template-args> # unresolved operator template-id +// ::= dn <destructor-name> # destructor or pseudo-destructor; +// # e.g. ~X or ~X<N-1> + +const char* +__demangle_tree::__parse_base_unresolved_name(const char* first, const char* last) +{ + if (last - first >= 2) + { + if ((first[0] == 'o' || first[0] == 'd') && first[1] == 'n') + { + if (first[0] == 'o') + { + const char* t = __parse_operator_name(first+2, last); + if (t != first+2) + first = __parse_template_args(t, last); + else + first = t; + } + else + { + const char* t = __parse_destructor_name(first+2, last); + if (t != first+2) + first = t; + } + } + else + { + const char* t = __parse_simple_id(first, last); + if (t == first) + { + t = __parse_operator_name(first, last); + if (t != first) + t = __parse_template_args(t, last); + } + if (t != first) + first = t; + } + } + return first; +} + +// <unresolved-type> ::= <template-param> +// ::= <decltype> +// ::= <substitution> + +const char* +__demangle_tree::__parse_unresolved_type(const char* first, const char* last) +{ + if (first != last) + { + const char* t; + switch (*first) + { + case 'T': + t = __parse_template_param(first, last); + if (t != first) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t; + } + } + break; + case 'D': + t = __parse_decltype(first, last); + if (t != first) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t; + } + } + break; + case 'S': + t = __parse_substitution(first, last); + if (t != first) + first = t; + break; + } + } + return first; +} + +// <unresolved-qualifier-level> ::= <source-name> [ <template-args> ] + +const char* +__demangle_tree::__parse_unresolved_qualifier_level(const char* first, const char* last) +{ + if (first != last) + { + const char* t = __parse_source_name(first, last); + if (t != first) + first = __parse_template_args(t, last); + } + return first; +} + +// <unresolved-name> +// extension ::= srN <unresolved-type> [<template-args>] <unresolved-qualifier-level>* E <base-unresolved-name> +// ::= [gs] <base-unresolved-name> # x or (with "gs") ::x +// ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name> +// # A::x, N::y, A<T>::z; "gs" means leading "::" +// ::= sr <unresolved-type> <base-unresolved-name> # T::x / decltype(p)::x +// # T::N::x /decltype(p)::N::x +// (ignored) ::= srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name> + +const char* +__demangle_tree::__parse_unresolved_name(const char* first, const char* last) +{ + if (last - first > 2) + { + const char* t = first; + bool global = false; + if (t[0] == 'g' && t[1] == 's') + { + global = true; + t += 2; + } + const char* t2 = __parse_base_unresolved_name(t, last); + if (t2 != t) + { + if (__make<__unresolved_name>(global, (__node*)0, __root_)) + first = t2; + } + else if (last - t > 2 && t[0] == 's' && t[1] == 'r') + { + if (!global && t[2] == 'N') + { + t2 = __parse_unresolved_type(t+3, last); + if (t2 != t+3 && t2 != last) + { + t = __parse_template_args(t2, last); + if (t == last) + return first; + __node* name = __root_; + while (*t != 'E') + { + t2 = __parse_unresolved_qualifier_level(t, last); + if (t2 == t || t2 == last) + return first; + if (!__make<__nested_delimeter>(name, __root_)) + return first; + name = __root_; + t = t2; + } + t2 = __parse_base_unresolved_name(++t, last); + if (t2 != t && __make<__unresolved_name>(false, name, __root_)) + first = t2; + } + } + else + { + if (!global) + { + t2 = __parse_unresolved_type(t+2, last); + if (t2 != t+2) + { + t = t2; + __node* name = __root_; + t2 = __parse_base_unresolved_name(t, last); + if (t2 != t && __make<__unresolved_name>(false, name, __root_)) + return t2; + return first; + } + } + t2 = __parse_unresolved_qualifier_level(t+2, last); + if (t2 != t+2 && t2 != last) + { + __node* name = __root_; + t = t2; + while (*t != 'E') + { + t2 = __parse_unresolved_qualifier_level(t, last); + if (t2 == t || t2 == last) + return first; + if (!__make<__nested_delimeter>(name, __root_)) + return first; + name = __root_; + t = t2; + } + t2 = __parse_base_unresolved_name(++t, last); + if (t2 != t && __make<__unresolved_name>(global, name, __root_)) + first = t2; + } + } + } + } + return first; +} + +// <function-param> ::= fp <top-level CV-qualifiers> _ # L == 0, first parameter +// ::= fp <top-level CV-qualifiers> <parameter-2 non-negative number> _ # L == 0, second and later parameters +// ::= fL <L-1 non-negative number> p <top-level CV-qualifiers> _ # L > 0, first parameter +// ::= fL <L-1 non-negative number> p <top-level CV-qualifiers> + +const char* +__demangle_tree::__parse_function_param(const char* first, const char* last) +{ + if (last - first >= 3 && *first == 'f') + { + if (first[1] == 'p') + { +// assert(!"__parse_function_param not implemented"); + __status_ = not_yet_implemented; + } + else if (first[1] == 'L') + { +// assert(!"__parse_function_param not implemented"); + __status_ = not_yet_implemented; + } + } + return first; +} + +// at <type> # alignof (a type) + +const char* +__demangle_tree::__parse_alignof_expr(const char* first, const char* last) +{ + if (last - first >= 3 && first[0] == 'a' && first[1] == 't') + { + const char* t = __parse_type(first+2, last); + if (t != first+2) + { + if (__make<__operator_alignof_expression>(__root_)) + first = t; + } + } + return first; +} + +// cc <type> <expression> # const_cast<type> (expression) + +const char* +__demangle_tree::__parse_const_cast_expr(const char* first, const char* last) +{ + if (last - first >= 3 && first[0] == 'c' && first[1] == 'c') + { + const char* t = __parse_type(first+2, last); + if (t != first+2) + { + __node* type = __root_; + const char* t1 = __parse_expression(t, last); + if (t1 != t) + { + if (__make<__const_cast>(type, __root_)) + first = t1; + } + } + } + return first; +} + +// cl <expression>+ E # call + +const char* +__demangle_tree::__parse_call_expr(const char* first, const char* last) +{ + if (last - first >= 4 && first[0] == 'c' && first[1] == 'l') + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + if (t == last) + return first; + __node* name = __root_; + __node* args = 0; + __node* prev = 0; + while (*t != 'E') + { + const char* t1 = __parse_expression(t, last); + if (t1 == t || t1 == last) + return first; + if (!__make<__list>(__root_)) + return first; + if (args == 0) + args = __root_; + if (prev) + { + prev->__right_ = __root_; + __root_->__size_ = prev->__size_ + 1; + } + prev = __root_; + t = t1; + } + ++t; + if (__make<__call_expr>(name, args)) + first = t; + } + } + return first; +} + +// cv <type> <expression> # conversion with one argument +// cv <type> _ <expression>* E # conversion with a different number of arguments + +const char* +__demangle_tree::__parse_conversion_expr(const char* first, const char* last) +{ + if (last - first >= 3 && first[0] == 'c' && first[1] == 'v') + { + const char* t = __parse_type(first+2, last); + if (t != first+2 && t != last) + { + __node* type = __root_; + __node* args = 0; + if (*t != '_') + { + const char* t1 = __parse_expression(t, last); + if (t1 == t) + return first; + args = __root_; + t = t1; + } + else + { + ++t; + if (t == last) + return first; + __node* prev = 0; + while (*t != 'E') + { + const char* t1 = __parse_expression(t, last); + if (t1 == t || t1 == last) + return first; + if (!__make<__list>(__root_)) + return first; + if (args == 0) + args = __root_; + if (prev) + { + prev->__right_ = __root_; + __root_->__size_ = prev->__size_ + 1; + } + prev = __root_; + t = t1; + } + ++t; + } + if (__make<__operator_cast>(type, args)) + first = t; + } + } + return first; +} + +// [gs] da <expression> # delete[] expression + +const char* +__demangle_tree::__parse_delete_array_expr(const char* first, const char* last) +{ + if (last - first >= 4) + { + const char* t = first; + bool parsed_gs = false; + if (t[0] == 'g' && t[1] == 's') + { + t += 2; + parsed_gs = true; + } + if (t[0] == 'd' && t[1] == 'a') + { + t += 2; + const char* t1 = __parse_expression(t, last); + if (t1 != t) + { + if (__make<__delete_array_expr>(parsed_gs, __root_)) + first = t1; + } + } + } + return first; +} + +// dc <type> <expression> # dynamic_cast<type> (expression) + +const char* +__demangle_tree::__parse_dynamic_cast_expr(const char* first, const char* last) +{ + if (last - first >= 3 && first[0] == 'd' && first[1] == 'c') + { + const char* t = __parse_type(first+2, last); + if (t != first+2) + { + __node* type = __root_; + const char* t1 = __parse_expression(t, last); + if (t1 != t) + { + if (__make<__dynamic_cast>(type, __root_)) + first = t1; + } + } + } + return first; +} + +// [gs] dl <expression> # delete expression + +const char* +__demangle_tree::__parse_delete_expr(const char* first, const char* last) +{ + if (last - first >= 4) + { + const char* t = first; + bool parsed_gs = false; + if (t[0] == 'g' && t[1] == 's') + { + t += 2; + parsed_gs = true; + } + if (t[0] == 'd' && t[1] == 'l') + { + t += 2; + const char* t1 = __parse_expression(t, last); + if (t1 != t) + { + if (__make<__delete_expr>(parsed_gs, __root_)) + first = t1; + } + } + } + return first; +} + +// ds <expression> <expression> # expr.*expr + +const char* +__demangle_tree::__parse_dot_star_expr(const char* first, const char* last) +{ + if (last - first >= 3 && first[0] == 'd' && first[1] == 's') + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* expr = __root_; + const char* t1 = __parse_expression(t, last); + if (t1 != t) + { + if (__make<__dot_star_expr>(expr, __root_)) + first = t1; + } + } + } + return first; +} + +// dt <expression> <unresolved-name> # expr.name + +const char* +__demangle_tree::__parse_dot_expr(const char* first, const char* last) +{ + if (last - first >= 3 && first[0] == 'd' && first[1] == 't') + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* expr = __root_; + const char* t1 = __parse_unresolved_name(t, last); + if (t1 != t) + { + if (__make<__dot_expr>(expr, __root_)) + first = t1; + } + } + } + return first; +} + +// mm_ <expression> # prefix -- + +const char* +__demangle_tree::__parse_decrement_expr(const char* first, const char* last) +{ + if (last - first > 3 && first[0] == 'm' && first[1] == 'm' && first[2] == '_') + { + const char* t = __parse_expression(first+3, last); + if (t != first+3) + { + if (__make<__operator_decrement>(true, __root_)) + first = t; + } + } + return first; +} + +// pp_ <expression> # prefix ++ + +const char* +__demangle_tree::__parse_increment_expr(const char* first, const char* last) +{ + if (last - first > 3 && first[0] == 'p' && first[1] == 'p' && first[2] == '_') + { + const char* t = __parse_expression(first+3, last); + if (t != first+3) + { + if (__make<__operator_increment>(true, __root_)) + first = t; + } + } + return first; +} + +// [gs] nw <expression>* _ <type> E # new (expr-list) type +// [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init) +// [gs] na <expression>* _ <type> E # new[] (expr-list) type +// [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init) +// <initializer> ::= pi <expression>* E # parenthesized initialization + +const char* +__demangle_tree::__parse_new_expr(const char* first, const char* last) +{ + if (last - first >= 4) + { + const char* t = first; + bool parsed_gs = false; + if (t[0] == 'g' && t[1] == 's') + { + t += 2; + parsed_gs = true; + } + if (t[0] == 'n' && (t[1] == 'w' || t[1] == 'a')) + { + bool is_array = t[1] == 'a'; + t += 2; + if (t == last) + return first; + __node* expr = 0; + __node* prev = 0; + while (*t != '_') + { + const char* t1 = __parse_expression(t, last); + if (t1 == t || t1 == last) + return first; + if (!__make<__list>(__root_)) + return first; + if (expr == 0) + expr = __root_; + if (prev) + { + prev->__right_ = __root_; + __root_->__size_ = prev->__size_ + 1; + } + prev = __root_; + t = t1; + } + ++t; + const char* t1 = __parse_type(t, last); + if (t1 == t || t1 == last) + return first; + t = t1; + __node* type = __root_; + __node* init = 0; + prev = 0; + bool has_init = false; + if (last - t >= 3 && t[0] == 'p' && t[1] == 'i') + { + t += 2; + has_init = true; + while (*t != 'E') + { + t1 = __parse_expression(t, last); + if (t1 == t || t1 == last) + return first; + if (!__make<__list>(__root_)) + return first; + if (init == 0) + init = __root_; + if (prev) + { + prev->__right_ = __root_; + __root_->__size_ = prev->__size_ + 1; + } + prev = __root_; + t = t1; + } + } + if (*t != 'E') + return first; + if (__make<__new_expr>(parsed_gs, is_array, has_init, + expr, type, init)) + first = t; + } + } + return first; +} + +// pt <expression> <unresolved-name> # expr->name + +const char* +__demangle_tree::__parse_arrow_expr(const char* first, const char* last) +{ + if (last - first >= 3 && first[0] == 'p' && first[1] == 't') + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* expr = __root_; + const char* t1 = __parse_unresolved_name(t, last); + if (t1 != t) + { + if (__make<__arrow_expr>(expr, __root_)) + first = t1; + } + } + } + return first; +} + +// rc <type> <expression> # reinterpret_cast<type> (expression) + +const char* +__demangle_tree::__parse_reinterpret_cast_expr(const char* first, const char* last) +{ + if (last - first >= 3 && first[0] == 'r' && first[1] == 'c') + { + const char* t = __parse_type(first+2, last); + if (t != first+2) + { + __node* type = __root_; + const char* t1 = __parse_expression(t, last); + if (t1 != t) + { + if (__make<__reinterpret_cast>(type, __root_)) + first = t1; + } + } + } + return first; +} + +// sc <type> <expression> # static_cast<type> (expression) + +const char* +__demangle_tree::__parse_static_cast_expr(const char* first, const char* last) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'c') + { + const char* t = __parse_type(first+2, last); + if (t != first+2) + { + __node* type = __root_; + const char* t1 = __parse_expression(t, last); + if (t1 != t) + { + if (__make<__static_cast>(type, __root_)) + first = t1; + } + } + } + return first; +} + +// st <type> # sizeof (a type) + +const char* +__demangle_tree::__parse_sizeof_type_expr(const char* first, const char* last) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 't') + { + const char* t = __parse_type(first+2, last); + if (t != first+2) + { + if (__make<__operator_sizeof_expression>(__root_)) + first = t; + } + } + return first; +} + +// sZ <template-param> # size of a parameter pack + +const char* +__demangle_tree::__parse_sizeof_param_pack_expr(const char* first, const char* last) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'Z' && first[2] == 'T') + { + const char* t = __parse_template_param(first+2, last); + if (t != first+2) + { + if (__make<__operator_sizeof_param_pack>(__root_)) + first = t; + } + } + return first; +} + +// sZ <function-param> # size of a function parameter pack + +const char* +__demangle_tree::__parse_sizeof_function_param_pack_expr(const char* first, const char* last) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'Z' && first[2] == 'f') + { + const char* t = __parse_function_param(first+2, last); + if (t != first+2) + { + if (__make<__operator_sizeof_param_pack>(__root_)) + first = t; + } + } + return first; +} + +// sp <expression> # pack expansion + +const char* +__demangle_tree::__parse_pack_expansion(const char* first, const char* last) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'p') + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + if (__make<__pack_expansion>(__root_)) + first = t; + } + } + return first; +} + +// te <expression> # typeid (expression) +// ti <type> # typeid (type) + +const char* +__demangle_tree::__parse_typeid_expr(const char* first, const char* last) +{ + if (last - first >= 3 && first[0] == 't' && (first[1] == 'e' || first[1] == 'i')) + { + const char* t; + if (first[1] == 'e') + t = __parse_expression(first+2, last); + else + t = __parse_type(first+2, last); + if (t != first+2) + { + if (__make<__typeid>(__root_)) + first = t; + } + } + return first; +} + +// tw <expression> # throw expression + +const char* +__demangle_tree::__parse_throw_expr(const char* first, const char* last) +{ + if (last - first >= 3 && first[0] == 't' && first[1] == 'w') + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + if (__make<__throw>(__root_)) + first = t; + } + } + return first; +} + +// <expression> ::= <unary operator-name> <expression> +// ::= <binary operator-name> <expression> <expression> +// ::= <ternary operator-name> <expression> <expression> <expression> +// ::= cl <expression>+ E # call +// ::= cv <type> <expression> # conversion with one argument +// ::= cv <type> _ <expression>* E # conversion with a different number of arguments +// ::= [gs] nw <expression>* _ <type> E # new (expr-list) type +// ::= [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init) +// ::= [gs] na <expression>* _ <type> E # new[] (expr-list) type +// ::= [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init) +// ::= [gs] dl <expression> # delete expression +// ::= [gs] da <expression> # delete[] expression +// ::= pp_ <expression> # prefix ++ +// ::= mm_ <expression> # prefix -- +// ::= ti <type> # typeid (type) +// ::= te <expression> # typeid (expression) +// ::= dc <type> <expression> # dynamic_cast<type> (expression) +// ::= sc <type> <expression> # static_cast<type> (expression) +// ::= cc <type> <expression> # const_cast<type> (expression) +// ::= rc <type> <expression> # reinterpret_cast<type> (expression) +// ::= st <type> # sizeof (a type) +// ::= at <type> # alignof (a type) +// ::= <template-param> +// ::= <function-param> +// ::= dt <expression> <unresolved-name> # expr.name +// ::= pt <expression> <unresolved-name> # expr->name +// ::= ds <expression> <expression> # expr.*expr +// ::= sZ <template-param> # size of a parameter pack +// ::= sZ <function-param> # size of a function parameter pack +// ::= sp <expression> # pack expansion +// ::= tw <expression> # throw expression +// ::= tr # throw with no operand (rethrow) +// ::= <unresolved-name> # f(p), N::f(p), ::f(p), +// # freestanding dependent name (e.g., T::x), +// # objectless nonstatic member reference +// ::= <expr-primary> + +const char* +__demangle_tree::__parse_expression(const char* first, const char* last) +{ + if (last - first >= 2) + { + const char* t = first; + bool parsed_gs = false; + if (last - first >= 4 && t[0] == 'g' && t[1] == 's') + { + t += 2; + parsed_gs = true; + } + switch (*t) + { + case 'L': + t = __parse_expr_primary(first, last); + break; + case 'T': + t = __parse_template_param(first, last); + break; + case 'f': + t = __parse_function_param(first, last); + break; + case 'a': + if (t[1] == 't') + t = __parse_alignof_expr(first, last); + break; + case 'c': + switch (t[1]) + { + case 'c': + t = __parse_const_cast_expr(first, last); + break; + case 'l': + t = __parse_call_expr(first, last); + break; + case 'v': + t = __parse_conversion_expr(first, last); + break; + } + break; + case 'd': + switch (t[1]) + { + case 'a': + t = __parse_delete_array_expr(first, last); + break; + case 'c': + t = __parse_dynamic_cast_expr(first, last); + break; + case 'l': + t = __parse_delete_expr(first, last); + break; + case 's': + t = __parse_dot_star_expr(first, last); + break; + case 't': + t = __parse_dot_expr(first, last); + break; + } + break; + case 'm': + t = __parse_decrement_expr(first, last); + break; + case 'n': + switch (t[1]) + { + case 'a': + case 'w': + t = __parse_new_expr(first, last); + break; + } + break; + case 'p': + switch (t[1]) + { + case 'p': + t = __parse_increment_expr(first, last); + break; + case 't': + t = __parse_arrow_expr(first, last); + break; + } + break; + case 'r': + t = __parse_reinterpret_cast_expr(first, last); + break; + case 's': + switch (t[1]) + { + case 'c': + t = __parse_static_cast_expr(first, last); + break; + case 'p': + t = __parse_pack_expansion(first, last); + break; + case 't': + t = __parse_sizeof_type_expr(first, last); + break; + case 'Z': + if (last - t >= 3) + { + switch (t[2]) + { + case 'T': + t = __parse_sizeof_param_pack_expr(first, last); + break; + case 'f': + t = __parse_sizeof_function_param_pack_expr(first, last); + break; + } + } + break; + } + break; + case 't': + switch (t[1]) + { + case 'e': + case 'i': + t = __parse_typeid_expr(first, last); + break; + case 'r': + if (__make<__rethrow>()) + t = first +2; + break; + case 'w': + t = __parse_throw_expr(first, last); + break; + } + break; + } + if ((!parsed_gs && t == first) || (parsed_gs && t == first+2)) + { + int op; + t = __parse_operator_name(first, last, &op); + if (t == first) + first = __parse_unresolved_name(first, last); + else + first = t; + } + else + first = t; + } + return first; +} + +// <array-type> ::= A <positive dimension number> _ <element type> +// ::= A [<dimension expression>] _ <element type> + +const char* +__demangle_tree::__parse_array_type(const char* first, const char* last) +{ + if (first != last && *first == 'A' && first+1 != last) + { + if (first[1] == '_') + { + const char* t = __parse_type(first+2, last); + if (t != first+2) + { + if (__make<__array>(__root_)) + first = t; + } + } + else if ('1' <= first[1] && first[1] <= '9') + { + size_t dim = first[1] - '0'; + const char* t = first+2; + for (; t != last && isdigit(*t); ++t) + dim = dim * 10 + *t - '0'; + if (t != last && *t == '_') + { + const char* t2 = __parse_type(t+1, last); + if (t2 != t+1) + { + if (__make<__array>(__root_, dim)) + first = t2; + } + } + } + else + { + const char* t = __parse_expression(first+1, last); + if (t != first+1 && t != last && *t == '_') + { + __node* dim = __root_; + const char* t2 = __parse_type(++t, last); + if (t2 != t) + { + if (__make<__array>(__root_, dim)) + first = t2; + } + } + } + } + return first; +} + +// <class-enum-type> ::= <name> + +const char* +__demangle_tree::__parse_class_enum_type(const char* first, const char* last) +{ + return __parse_name(first, last); +} + +// <pointer-to-member-type> ::= M <class type> <member type> + +const char* +__demangle_tree::__parse_pointer_to_member_type(const char* first, const char* last) +{ + if (first != last && *first == 'M') + { + const char* t = __parse_type(first+1, last); + if (t != first+1) + { + __node* class_type = __root_; + const char* t2 = __parse_type(t, last, true, true); + if (t2 != t) + { + if (__make<__pointer_to_member_type>(class_type, __root_)) + first = t2; + } + } + } + return first; +} + +// <decltype> ::= Dt <expression> E # decltype of an id-expression or class member access (C++0x) +// ::= DT <expression> E # decltype of an expression (C++0x) + +const char* +__demangle_tree::__parse_decltype(const char* first, const char* last) +{ + if (last - first >= 4 && first[0] == 'D') + { + switch (first[1]) + { + case 't': + case 'T': + { + const char* t = __parse_expression(first+2, last); + if (t != first+2 && t != last && *t == 'E') + { + if (__make<__decltype_node>(__root_)) + first = t+1; + } + } + break; + } + } + return first; +} + +// <template-param> ::= T_ # first template parameter +// ::= T <parameter-2 non-negative number> _ + +const char* +__demangle_tree::__parse_template_param(const char* first, const char* last) +{ + if (last - first >= 2) + { + if (*first == 'T') + { + if (first[1] == '_') + { + if (__t_begin_ != __t_end_) + { + if (__make<__sub>(*__t_begin_)) + first += 2; + } + else + { + if (__make<__sub>(size_t(0))) + { + first += 2; + __fix_forward_references_ = true; + } + } + } + else if (isdigit(first[1])) + { + const char* t = first+1; + size_t sub = *t - '0'; + for (++t; t != last && isdigit(*t); ++t) + { + sub *= 10; + sub += *t - '0'; + } + if (t == last || *t != '_') + return first; + ++sub; + if (sub < __t_end_ - __t_begin_) + { + if (__make<__sub>(__t_begin_[sub])) + first = t+1; + } + else + { + if (__make<__sub>(sub)) + { + first = t+1; + __fix_forward_references_ = true; + } + } + } + } + } + return first; +} + +// extension: +// <vector-type> ::= Dv <positive dimension number> _ +// <extended element type> +// ::= Dv [<dimension expression>] _ <element type> +// <extended element type> ::= <element type> +// ::= p # AltiVec vector pixel + +const char* +__demangle_tree::__parse_vector_type(const char* first, const char* last) +{ + if (last - first > 3 && first[0] == 'D' && first[1] == 'v') + { + if ('1' <= first[2] && first[2] <= '9') + { + const char* t = first+3; + while (*t != '_') + { + if (!isdigit(*t) || ++t == last) + return first; + } + const char* num = first + 2; + size_t sz = t - num; + if (++t != last) + { + if (*t != 'p') + { + const char* t1 = __parse_type(t, last); + if (t1 != t) + { + if (__make<__vector_type>(__root_, num, sz)) + first = t1; + } + } + else + { + ++t; + if (__make<__vector_type>((__node*)0, num, sz)) + first = t; + } + } + } + else + { + __node* num = 0; + const char* t1 = first+2; + if (*t1 != '_') + { + const char* t = __parse_expression(t1, last); + if (t != t1) + num = __root_; + t1 = t; + } + if (t1 != last && *t1 == '_' && ++t1 != last) + { + const char* t = __parse_type(t1, last); + if (t != t1) + { + if (__make<__vector_type>(__root_, num)) + first = t; + } + } + } + } + return first; +} + +// <type> ::= <builtin-type> +// ::= <function-type> +// ::= <class-enum-type> +// ::= <array-type> +// ::= <pointer-to-member-type> +// ::= <template-param> +// ::= <template-template-param> <template-args> +// ::= <decltype> +// ::= <substitution> +// ::= <CV-qualifiers> <type> +// ::= P <type> # pointer-to +// ::= R <type> # reference-to +// ::= O <type> # rvalue reference-to (C++0x) +// ::= C <type> # complex pair (C 2000) +// ::= G <type> # imaginary (C 2000) +// ::= Dp <type> # pack expansion (C++0x) +// ::= U <source-name> <type> # vendor extended type qualifier +// extension := <vector-type> # <vector-type> starts with Dv + +const char* +__demangle_tree::__parse_type(const char* first, const char* last, + bool try_to_parse_template_args, + bool look_for_ref_quals) +{ + unsigned cv = 0; + const char* t = __parse_cv_qualifiers(first, last, cv, look_for_ref_quals); + if (t != first) + { + const char* t2 = __parse_type(t, last, try_to_parse_template_args); + if (t2 != t) + { + if (__make<__cv_qualifiers>(cv, __root_)) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t2; + } + } + } + return first; + } + if (first != last) + { + switch (*first) + { + case 'A': + t = __parse_array_type(first, last); + if (t != first) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t; + } + } + break; + case 'C': + t = __parse_type(first+1, last, try_to_parse_template_args); + if (t != first+1) + { + if (__make<__d_complex>(__root_)) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t; + } + } + return first; + } + break; + case 'F': + t = __parse_function_type(first, last); + if (t != first) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t; + } + } + break; + case 'G': + t = __parse_type(first+1, last, try_to_parse_template_args); + if (t != first+1) + { + if (__make<__imaginary>(__root_)) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t; + } + } + return first; + } + break; + case 'M': + t = __parse_pointer_to_member_type(first, last); + if (t != first) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t; + } + } + break; + case 'O': + t = __parse_type(first+1, last, try_to_parse_template_args); + if (t != first+1) + { + if (__make<__rvalue_reference_to>(__root_)) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t; + } + } + return first; + } + break; + case 'P': + t = __parse_type(first+1, last, try_to_parse_template_args); + if (t != first+1) + { + if (__make<__pointer_to>(__root_)) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t; + } + } + return first; + } + break; + case 'R': + t = __parse_type(first+1, last, try_to_parse_template_args); + if (t != first+1) + { + if (__make<__lvalue_reference_to>(__root_)) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t; + } + } + return first; + } + break; + case 'T': + t = __parse_template_param(first, last); + if (t != first) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + if (try_to_parse_template_args) + { + const char* t2 = __parse_template_args(t, last); + if (t2 != t) + { + if (__sub_end_ < __sub_cap_) + { + *__sub_end_++ = __root_; + first = t2; + } + else + __status_ = memory_alloc_failure; + } + else + { + first = t; + } + } + else + { + first = t; + } + } + } + break; + case 'U': + if (first+1 != last) + { + t = __parse_source_name(first+1, last); + if (t != first+1) + { + __node* name = __root_; + const char* t2 = __parse_type(t, last, try_to_parse_template_args); + if (t2 != t) + { + if (__make<__extended_qualifier>(name, __root_)) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t2; + } + } + return first; + } + } + } + break; + case 'S': + if (first+1 != last && first[1] == 't') + { + t = __parse_class_enum_type(first, last); + if (t != first) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t; + } + } + } + else + { + t = __parse_substitution(first, last); + if (t != first) + { + first = t; + // Parsed a substitution. If the substitution is a + // <template-param> it might be followed by <template-args>. + t = __parse_template_args(first, last); + if (t != first) + { + // Need to create substitution for <template-template-param> <template-args> + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t; + } + } + } + } + break; + case 'D': + if (first+1 != last) + { + switch (first[1]) + { + case 'p': + t = __parse_type(first+2, last, try_to_parse_template_args); + if (t != first+1) + { + if (__make<__pack_expansion>(__root_)) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t; + } + } + return first; + } + break; + case 't': + case 'T': + t = __parse_decltype(first, last); + if (t != first) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t; + } + return first; + } + break; + case 'v': + t = __parse_vector_type(first, last); + if (t != first) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t; + } + return first; + } + break; + } + } + // drop through + default: + // must check for builtin-types before class-enum-types to avoid + // ambiguities with operator-names + t = __parse_builtin_type(first, last); + if (t != first) + { + first = t; + } + else + { + t = __parse_class_enum_type(first, last); + if (t != first) + { + if (__sub_end_ == __sub_cap_) + __status_ = memory_alloc_failure; + else + { + *__sub_end_++ = __root_; + first = t; + } + } + } + break; + } + } + return first; +} + +// <number> ::= [n] <non-negative decimal integer> + +const char* +__demangle_tree::__parse_number(const char* first, const char* last) +{ + if (first != last) + { + const char* t = first; + if (*t == 'n') + ++t; + if (t != last) + { + if (*t == '0') + { + first = t+1; + } + else if ('1' <= *t && *t <= '9') + { + first = t+1; + while (first != last && isdigit(*first)) + ++first; + } + } + } + return first; +} + +// <call-offset> ::= h <nv-offset> _ +// ::= v <v-offset> _ +// +// <nv-offset> ::= <offset number> +// # non-virtual base override +// +// <v-offset> ::= <offset number> _ <virtual offset number> +// # virtual base override, with vcall offset + +const char* +__demangle_tree::__parse_call_offset(const char* first, const char* last) +{ + if (first != last) + { + switch (*first) + { + case 'h': + { + const char* t = __parse_number(first + 1, last); + if (t != first + 1 && t != last && *t == '_') + first = t + 1; + } + break; + case 'v': + { + const char* t = __parse_number(first + 1, last); + if (t != first + 1 && t != last && *t == '_') + { + const char* t2 = __parse_number(++t, last); + if (t2 != t && t2 != last && *t2 == '_') + first = t2 + 1; + } + } + break; + } + } + return first; +} + +// <special-name> ::= TV <type> # virtual table +// ::= TT <type> # VTT structure (construction vtable index) +// ::= TI <type> # typeinfo structure +// ::= TS <type> # typeinfo name (null-terminated byte string) +// ::= Tc <call-offset> <call-offset> <base encoding> +// # base is the nominal target function of thunk +// # first call-offset is 'this' adjustment +// # second call-offset is result adjustment +// ::= T <call-offset> <base encoding> +// # base is the nominal target function of thunk +// ::= GV <object name> # Guard variable for one-time initialization +// # No <type> +// extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first +// extension ::= GR <object name> # reference temporary for object + +const char* +__demangle_tree::__parse_special_name(const char* first, const char* last) +{ + if (last - first > 2) + { + const char* t; + switch (*first) + { + case 'T': + switch (first[1]) + { + case 'V': + // TV <type> # virtual table + t = __parse_type(first+2, last); + if (t != first+2 && __make<__vtable>(__root_)) + first = t; + break; + case 'T': + // TT <type> # VTT structure (construction vtable index) + t = __parse_type(first+2, last); + if (t != first+2 && __make<__VTT>(__root_)) + first = t; + break; + case 'I': + // TI <type> # typeinfo structure + t = __parse_type(first+2, last); + if (t != first+2 && __make<__typeinfo>(__root_)) + first = t; + break; + case 'S': + // TS <type> # typeinfo name (null-terminated byte string) + t = __parse_type(first+2, last); + if (t != first+2 && __make<__typeinfo_name>(__root_)) + first = t; + break; + case 'c': + // Tc <call-offset> <call-offset> <base encoding> + { + const char* t0 = __parse_call_offset(first+2, last); + if (t0 == first+2) + break; + const char* t1 = __parse_call_offset(t0, last); + if (t1 == t0) + break; + t = __parse_encoding(t1, last); + if (t != t1 && __make<__covariant_return_thunk>(__root_)) + first = t; + } + break; + case 'C': + // extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first + t = __parse_type(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t0 = __parse_number(t, last); + if (t0 != t && t0 != last && *t0 == '_') + { + const char* t1 = __parse_type(++t0, last); + if (t1 != t0) + { + if (__make<__construction_vtable>(__root_, op1)) + first = t1; + } + } + } + break; + default: + // T <call-offset> <base encoding> + { + const char* t0 = __parse_call_offset(first+1, last); + if (t0 == first+1) + break; + t = __parse_encoding(t0, last); + if (t != t0) + { + if (first[2] == 'v') + { + if (__make<__virtual_thunk>(__root_)) + first = t; + } + else + { + if (__make<__non_virtual_thunk>(__root_)) + first = t; + } + } + } + break; + } + break; + case 'G': + switch (first[1]) + { + case 'V': + // GV <object name> # Guard variable for one-time initialization + t = __parse_name(first+2, last); + if (t != first+2 && __make<__guard_variable>(__root_)) + first = t; + break; + case 'R': + // extension ::= GR <object name> # reference temporary for object + t = __parse_name(first+2, last); + if (t != first+2 && __make<__reference_temporary>(__root_)) + first = t; + break; + } + break; + } + } + return first; +} + +// <operator-name> +// ::= aa # && +// ::= ad # & (unary) +// ::= an # & +// ::= aN # &= +// ::= aS # = +// ::= at # alignof (a type) +// ::= az # alignof (an expression) +// ::= cl # () +// ::= cm # , +// ::= co # ~ +// ::= cv <type> # (cast) +// ::= da # delete[] +// ::= de # * (unary) +// ::= dl # delete +// ::= dv # / +// ::= dV # /= +// ::= eo # ^ +// ::= eO # ^= +// ::= eq # == +// ::= ge # >= +// ::= gt # > +// ::= ix # [] +// ::= le # <= +// ::= ls # << +// ::= lS # <<= +// ::= lt # < +// ::= mi # - +// ::= mI # -= +// ::= ml # * +// ::= mL # *= +// ::= mm # -- (postfix in <expression> context) +// ::= na # new[] +// ::= ne # != +// ::= ng # - (unary) +// ::= nt # ! +// ::= nw # new +// ::= oo # || +// ::= or # | +// ::= oR # |= +// ::= pm # ->* +// ::= pl # + +// ::= pL # += +// ::= pp # ++ (postfix in <expression> context) +// ::= ps # + (unary) +// ::= pt # -> +// ::= qu # ? +// ::= rm # % +// ::= rM # %= +// ::= rs # >> +// ::= rS # >>= +// ::= st # sizeof (a type) +// ::= sz # sizeof (an expression) +// ::= v <digit> <source-name> # vendor extended operator + +const char* +__demangle_tree::__parse_operator_name(const char* first, const char* last, int* type) +{ + if (last - first >= 2) + { + switch (*first) + { + case 'a': + switch (first[1]) + { + case 'a': + // && + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_logical_and>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_logical_and>()) + first += 2; + } + break; + case 'd': + // & (unary) + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + if (__make<__operator_addressof>(__root_)) + { + *type = 1; + first = t; + } + } + } + else + { + if (__make<__operator_addressof>()) + first += 2; + } + break; + case 'n': + // & + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_bit_and>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_bit_and>()) + first += 2; + } + break; + case 'N': + // &= + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_and_equal>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_and_equal>()) + first += 2; + } + break; + case 'S': + // = + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_equal>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_equal>()) + first += 2; + } + break; + case 't': + // alignof (a type) + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + if (__make<__operator_alignof_type>(__root_)) + { + *type = -1; + first = t; + } + } + } + else + { + if (__make<__operator_alignof_type>()) + first += 2; + } + break; + case 'z': + // alignof (an expression) + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + if (__make<__operator_alignof_expression>(__root_)) + { + *type = -1; + first = t; + } + } + } + else + { + if (__make<__operator_alignof_expression>()) + first += 2; + } + break; + } + break; + case 'c': + switch (first[1]) + { + case 'l': + // () + if (__make<__operator_paren>()) + { + first += 2; + if (type) + *type = -1; + } + break; + case 'm': + // , + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_comma>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_comma>()) + first += 2; + } + break; + case 'o': + // ~ + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + if (__make<__operator_tilda>(__root_)) + { + *type = 1; + first = t; + } + } + } + else + { + if (__make<__operator_tilda>()) + first += 2; + } + break; + case 'v': + // cast <type> + { + const char* t = __parse_type(first+2, last, false); + if (t != first+2) + { + __node* cast_type = __root_; + if (type) + { + const char* t2 = __parse_expression(t, last); + if (t2 != t) + { + if (__make<__operator_cast>(cast_type, __root_)) + { + *type = -1; + first = t2; + } + } + } + else + { + if (__make<__operator_cast>(cast_type)) + first = t; + } + } + } + break; + } + break; + case 'd': + switch (first[1]) + { + case 'a': + // delete[] + if (__make<__operator_delete_array>()) + { + first += 2; + if (type) + *type = -1; + } + break; + case 'e': + // * (unary) + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + if (__make<__operator_dereference>(__root_)) + { + *type = 1; + first = t; + } + } + } + else + { + if (__make<__operator_dereference>()) + first += 2; + } + break; + case 'l': + // delete + if (__make<__operator_delete>()) + { + first += 2; + if (type) + *type = -1; + } + break; + case 'v': + // / + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_divide>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_divide>()) + first += 2; + } + break; + case 'V': + // /= + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_divide_equal>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_divide_equal>()) + first += 2; + } + break; + } + break; + case 'e': + switch (first[1]) + { + case 'o': + // ^ + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_xor>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_xor>()) + first += 2; + } + break; + case 'O': + // ^= + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_xor_equal>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_xor_equal>()) + first += 2; + } + break; + case 'q': + // == + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_equality>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_equality>()) + first += 2; + } + break; + } + break; + case 'g': + switch (first[1]) + { + case 'e': + // >= + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_greater_equal>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_greater_equal>()) + first += 2; + } + break; + case 't': + // > + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_greater>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_greater>()) + first += 2; + } + break; + } + break; + case 'i': + // [] + if (first[1] == 'x' && __make<__operator_brackets>()) + { + first += 2; + if (type) + *type = -1; + } + break; + case 'l': + switch (first[1]) + { + case 'e': + // <= + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_less_equal>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_less_equal>()) + first += 2; + } + break; + case 's': + // << + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_left_shift>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_left_shift>()) + first += 2; + } + break; + case 'S': + // <<= + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_left_shift_equal>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_left_shift_equal>()) + first += 2; + } + break; + case 't': + // < + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_less>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_less>()) + first += 2; + } + break; + } + break; + case 'm': + switch (first[1]) + { + case 'i': + // - + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_minus>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_minus>()) + first += 2; + } + break; + case 'I': + // -= + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_minus_equal>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_minus_equal>()) + first += 2; + } + break; + case 'l': + // * + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_times>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_times>()) + first += 2; + } + break; + case 'L': + // *= + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_times_equal>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_times_equal>()) + first += 2; + } + break; + case 'm': + // -- (postfix in <expression> context) + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + if (__make<__operator_decrement>(false, __root_)) + { + *type = 1; + first = t; + } + } + } + else + { + if (__make<__operator_decrement>()) + first += 2; + } + break; + } + break; + case 'n': + switch (first[1]) + { + case 'a': + // new[] + if (__make<__operator_new_array>()) + { + first += 2; + if (type) + *type = -1; + } + break; + case 'e': + // != + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_not_equal>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_not_equal>()) + first += 2; + } + break; + case 'g': + // - (unary) + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + if (__make<__operator_negate>(__root_)) + { + *type = 1; + first = t; + } + } + } + else + { + if (__make<__operator_negate>()) + first += 2; + } + break; + case 't': + // ! + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + if (__make<__operator_logical_not>(__root_)) + { + *type = 1; + first = t; + } + } + } + else + { + if (__make<__operator_logical_not>()) + first += 2; + } + break; + case 'w': + // new + if (__make<__operator_new>()) + { + first += 2; + if (type) + *type = -1; + } + break; + } + break; + case 'o': + switch (first[1]) + { + case 'o': + // || + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_logical_or>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_logical_or>()) + first += 2; + } + break; + case 'r': + // | + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_bit_or>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_bit_or>()) + first += 2; + } + break; + case 'R': + // |= + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_or_equal>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_or_equal>()) + first += 2; + } + break; + } + break; + case 'p': + switch (first[1]) + { + case 'm': + // ->* + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_pointer_to_member>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_pointer_to_member>()) + first += 2; + } + break; + case 'l': + // + + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_plus>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_plus>()) + first += 2; + } + break; + case 'L': + // += + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_plus_equal>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_plus_equal>()) + first += 2; + } + break; + case 'p': + // ++ (postfix in <expression> context) + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + if (__make<__operator_increment>(false, __root_)) + { + *type = 1; + first = t; + } + } + } + else + { + if (__make<__operator_increment>()) + first += 2; + } + break; + case 's': + // + (unary) + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + if (__make<__operator_unary_plus>(__root_)) + { + *type = 1; + first = t; + } + } + } + else + { + if (__make<__operator_unary_plus>()) + first += 2; + } + break; + case 't': + // -> + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_arrow>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_arrow>()) + first += 2; + } + break; + } + break; + case 'q': + // ? + if (first[1] == 'u') + { + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + __node* op2 = __root_; + const char* t3 = __parse_expression(t2, last); + if (t3 != t2) + { + if (__make<__operator_conditional>(op1, op2, __root_)) + { + *type = 3; + first = t3; + } + } + } + } + } + else + { + if (__make<__operator_conditional>()) + first += 2; + } + } + break; + case 'r': + switch (first[1]) + { + case 'm': + // % + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_mod>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_mod>()) + first += 2; + } + break; + case 'M': + // %= + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_mod_equal>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_mod_equal>()) + first += 2; + } + break; + case 's': + // >> + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_right_shift>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_right_shift>()) + first += 2; + } + break; + case 'S': + // >>= + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + __node* op1 = __root_; + const char* t2 = __parse_expression(t, last); + if (t != t2) + { + if (__make<__operator_right_shift_equal>(op1, __root_)) + { + *type = 2; + first = t2; + } + } + } + } + else + { + if (__make<__operator_right_shift_equal>()) + first += 2; + } + break; + } + break; + case 's': + switch (first[1]) + { + case 't': + // sizeof (a type) + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + if (__make<__operator_sizeof_type>(__root_)) + { + *type = -1; + first = t; + } + } + } + else + { + if (__make<__operator_sizeof_type>()) + first += 2; + } + break; + case 'z': + // sizeof (an expression) + if (type) + { + const char* t = __parse_expression(first+2, last); + if (t != first+2) + { + if (__make<__operator_sizeof_expression>(__root_)) + { + *type = -1; + first = t; + } + } + } + else + { + if (__make<__operator_sizeof_expression>()) + first += 2; + } + break; + } + break; + } + } + return first; +} + +// <source-name> ::= <positive length number> <identifier> + +const char* +__demangle_tree::__parse_source_name(const char* first, const char* last) +{ + if (first != last) + { + char c = *first; + if ('1' <= c && c <= '9' && first+1 != last) + { + const char* t = first+1; + size_t n = c - '0'; + for (c = *t; '0' <= c && c <= '9'; c = *t) + { + n = n * 10 + c - '0'; + if (++t == last) + return first; + } + if (last - t >= n && __make<__source_name>(t, n)) + first = t + n; + } + } + return first; +} + +// <unqualified-name> ::= <operator-name> +// ::= <ctor-dtor-name> +// ::= <source-name> +// ::= <unnamed-type-name> + +const char* +__demangle_tree::__parse_unqualified_name(const char* first, const char* last) +{ + const char* t = __parse_source_name(first, last); + if (t == first) + { + t = __parse_ctor_dtor_name(first, last); + if (t == first) + { + t = __parse_operator_name(first, last); + if (t == first) + first = __parse_unnamed_type_name(first, last); + else + first = t; + } + else + first = t; + } + else + first = t; + return first; +} + +// <unscoped-name> ::= <unqualified-name> +// ::= St <unqualified-name> # ::std:: +// extension ::= StL<unqualified-name> + +const char* +__demangle_tree::__parse_unscoped_name(const char* first, const char* last) +{ + if (last - first >= 2) + { + const char* t0 = first; + if (first[0] == 'S' && first[1] == 't') + { + t0 += 2; + if (t0 != last && *t0 == 'L') + ++t0; + } + const char* t1 = __parse_unqualified_name(t0, last); + if (t1 != t0) + { + if (t0 != first) + { + __node* name = __root_; + if (__make<__std_qualified_name>()) + { + if (__make<__nested_delimeter>(__root_, name)) + first = t1; + } + } + else + first = t1; + } + } + return first; +} + +// <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E +// ::= N [<CV-qualifiers>] <template-prefix> <template-args> E +// +// <prefix> ::= <prefix> <unqualified-name> +// ::= <template-prefix> <template-args> +// ::= <template-param> +// ::= <decltype> +// ::= # empty +// ::= <substitution> +// ::= <prefix> <data-member-prefix> +// extension ::= L +// +// <template-prefix> ::= <prefix> <template unqualified-name> +// ::= <template-param> +// ::= <substitution> + +const char* +__demangle_tree::__parse_nested_name(const char* first, const char* last) +{ + if (first != last && *first == 'N') + { + unsigned cv = 0; + const char* t0 = __parse_cv_qualifiers(first+1, last, cv, true); + __node* prev = NULL; + if (last - t0 >= 2 && t0[0] == 'S' && t0[1] == 't') + { + t0 += 2; + if (!__make<__std_qualified_name>()) + return first; + prev = __root_; + } + while (t0 != last) + { + bool can_sub = true; + bool make_nested = true; + const char* t1; + switch (*t0) + { + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + t1 = __parse_source_name(t0, last); + if (t1 == t0 || t1 == last) + return first; + if (*t1 == 'M') + { + // This is a data-member-prefix + ++t1; + } + else if (*t1 == 'I') + { + // has following <template-args> + if (prev) + { + if (!__make<__nested_delimeter>(prev, __root_)) + return first; + make_nested = false; + } + if (__sub_end_ == __sub_cap_) + { + __status_ = memory_alloc_failure; + return first; + } + else + *__sub_end_++ = __root_; + const char* t2 = __parse_template_args(t1, last); + if (t2 == t1) + return first; + t1 = t2; + } + break; + case 'D': + if (t0+1 != last && (t0[1] == 't' || t0[1] == 'T')) + { + t1 = __parse_decltype(t0, last); + break; + } + // check for Dt, DT here, else drop through + case 'C': + t1 = __parse_ctor_dtor_name(t0, last); + if (t1 == t0 || t1 == last) + return first; + if (*t1 == 'I') + { + // has following <template-args> + if (prev) + { + if (!__make<__nested_delimeter>(prev, __root_)) + return first; + make_nested = false; + } + if (__sub_end_ == __sub_cap_) + { + __status_ = memory_alloc_failure; + return first; + } + else + *__sub_end_++ = __root_; + const char* t2 = __parse_template_args(t1, last); + if (t2 == t1) + return first; + t1 = t2; + } + break; + case 'U': + assert(!"__parse_nested_name U"); + // could have following <template-args> + break; + case 'T': + t1 = __parse_template_param(t0, last); + if (t1 == t0 || t1 == last) + return first; + if (*t1 == 'I') + { + // has following <template-args> + if (prev) + { + if (!__make<__nested_delimeter>(prev, __root_)) + return first; + make_nested = false; + } + if (__sub_end_ == __sub_cap_) + { + __status_ = memory_alloc_failure; + return first; + } + else + *__sub_end_++ = __root_; + const char* t2 = __parse_template_args(t1, last); + if (t2 == t1) + return first; + t1 = t2; + } + break; + case 'S': + t1 = __parse_substitution(t0, last); + if (t1 == t0 || t1 == last) + return first; + if (*t1 == 'I') + { + const char* t2 = __parse_template_args(t1, last); + if (t2 == t1) + return first; + t1 = t2; + } + else + can_sub = false; + break; + case 'L': + // extension: ignore L here + ++t0; + continue; + default: + t1 = __parse_operator_name(t0, last); + if (t1 == t0 || t1 == last) + return first; + if (*t1 == 'I') + { + // has following <template-args> + if (prev) + { + if (!__make<__nested_delimeter>(prev, __root_)) + return first; + make_nested = false; + } + if (__sub_end_ == __sub_cap_) + { + __status_ = memory_alloc_failure; + return first; + } + else + *__sub_end_++ = __root_; + const char* t2 = __parse_template_args(t1, last); + if (t2 == t1) + return first; + t1 = t2; + } + break; + } + if (t1 == t0 || t1 == last) + return first; + if (prev && make_nested) + { + if (!__make<__nested_delimeter>(prev, __root_)) + return first; + can_sub = true; + } + if (can_sub && *t1 != 'E') + { + if (__sub_end_ == __sub_cap_) + { + __status_ = memory_alloc_failure; + return first; + } + else + *__sub_end_++ = __root_; + } + if (*t1 == 'E') + { + if (cv != 0) + { + if (!__make<__cv_qualifiers>(cv, __root_)) + return first; + } + first = t1+1; + break; + } + prev = __root_; + t0 = t1; + } + } + return first; +} + +// <template-arg> ::= <type> # type or template +// ::= X <expression> E # expression +// ::= <expr-primary> # simple expressions +// ::= J <template-arg>* E # argument pack +// ::= LZ <encoding> E # extension + +const char* +__demangle_tree::__parse_template_arg(const char* first, const char* last) +{ + if (first != last) + { + const char* t; + switch (*first) + { + case 'X': + t = __parse_expression(first+1, last); + if (t != first+1) + { + if (t != last && *t == 'E') + first = t+1; + } + break; + case 'J': + t = first+1; + if (t == last) + return first; + if (*t == 'E') + { + if (__make<__list>((__node*)0)) + first = t+1; + } + else + { + __node* list = NULL; + __node* prev = NULL; + do + { + const char* t2 = __parse_template_arg(t, last); + if (t2 == t || !__make<__list>(__root_)) + return first; + if (list == 0) + list = __root_; + if (prev) + { + prev->__right_ = __root_; + __root_->__size_ = prev->__size_ + 1; + } + prev = __root_; + t = t2; + } while (t != last && *t != 'E'); + first = t+1; + __root_ = list; + } + break; + case 'L': + // <expr-primary> or LZ <encoding> E + if (first+1 != last && first[1] == 'Z') + { + t = __parse_encoding(first+2, last); + if (t != first+2 && t != last && *t == 'E') + first = t+1; + } + else + first = __parse_expr_primary(first, last); + break; + default: + // <type> + first = __parse_type(first, last); + break; + } + } + return first; +} + +// <template-args> ::= I <template-arg>* E +// extension, the abi says <template-arg>+ + +const char* +__demangle_tree::__parse_template_args(const char* first, const char* last) +{ + if (last - first >= 2 && *first == 'I') + { + __node* args = NULL; + __node* prev = NULL; + __node* name = __root_; + if (__tag_templates_) + __t_end_ = __t_begin_; + const char* t = first+1; + while (*t != 'E') + { + bool prev_tag_templates = __tag_templates_; + __node** prev_t_begin = __t_begin_; + __node** prev_t_end = __t_end_; + if (__tag_templates_) + __t_begin_ = __t_end_; + const char* t2 = __parse_template_arg(t, last); + if (prev_tag_templates) + { + __tag_templates_ = prev_tag_templates; + __t_begin_ = prev_t_begin; + __t_end_ = prev_t_end; + } + if (t2 == t || t2 == last) + break; + if (!__make<__list>(__root_)) + return first; + if (args == 0) + args = __root_; + if (prev) + { + prev->__right_ = __root_; + __root_->__size_ = prev->__size_ + 1; + } + prev = __root_; + if (__tag_templates_) + { + if (__t_end_ == __t_cap_) + { + __status_ = memory_alloc_failure; + return first; + } + if (__root_->__left_) + *__t_end_++ = __root_->__left_; + else + *__t_end_++ = __root_; + } + t = t2; + } + if (t != last && *t == 'E') + { + if (__make<__template_args>(name, args)) + first = t+1; + } + } + return first; +} + +// <substitution> ::= S <seq-id> _ +// ::= S_ +// <substitution> ::= Sa # ::std::allocator +// <substitution> ::= Sb # ::std::basic_string +// <substitution> ::= Ss # ::std::basic_string < char, +// ::std::char_traits<char>, +// ::std::allocator<char> > +// <substitution> ::= Si # ::std::basic_istream<char, std::char_traits<char> > +// <substitution> ::= So # ::std::basic_ostream<char, std::char_traits<char> > +// <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> > + +const char* +__demangle_tree::__parse_substitution(const char* first, const char* last) +{ + if (last - first >= 2) + { + if (*first == 'S') + { + switch (first[1]) + { + case 'a': + if (__make<__sub_allocator>()) + first += 2; + break; + case 'b': + if (__make<__sub_basic_string>()) + first += 2; + break; + case 's': + if (__make<__sub_string>()) + first += 2; + break; + case 'i': + if (__make<__sub_istream>()) + first += 2; + break; + case 'o': + if (__make<__sub_ostream>()) + first += 2; + break; + case 'd': + if (__make<__sub_iostream>()) + first += 2; + break; + case '_': + if (__sub_begin_ != __sub_end_) + { + if (__make<__sub>(*__sub_begin_)) + first += 2; + } + break; + default: + if (isdigit(first[1]) || isupper(first[1])) + { + size_t sub = 0; + const char* t = first+1; + if (isdigit(*t)) + sub = *t - '0'; + else + sub = *t - 'A' + 10; + for (++t; t != last && (isdigit(*t) || isupper(*t)); ++t) + { + sub *= 36; + if (isdigit(*t)) + sub += *t - '0'; + else + sub += *t - 'A' + 10; + } + if (t == last || *t != '_') + return first; + ++sub; + if (sub < __sub_end_ - __sub_begin_) + { + if (__make<__sub>(__sub_begin_[sub])) + first = t+1; + } + } + break; + } + } + } + return first; +} + +// <name> ::= <nested-name> +// ::= <local-name> # See Scope Encoding below +// ::= <unscoped-template-name> <template-args> +// ::= <unscoped-name> + +const char* +__demangle_tree::__parse_name(const char* first, const char* last) +{ + if (first != last) + { + const char* t0 = first; + // extension: ignore L here + if (*t0 == 'L') + ++t0; + const char* t = __parse_nested_name(t0, last); + if (t == t0) + { + t = __parse_local_name(t0, last); + if (t == t0) + { + // not <nested-name> nor <local-name> + // Try to parse <unscoped-template-name> <template-args> or + // <unscoped-name> which are nearly ambiguous. + // This logic occurs nowhere else. + if (last - t0 >= 2) + { + if (t0[0] == 'S' && (t0[1] == '_' || + isdigit(t0[1]) || + isupper(t0[1]) || + t0[1] == 'a' || + t0[1] == 'b')) + { + t = __parse_substitution(t0, last); + if (t != t0) + { + const char* t2 = __parse_template_args(t, last); + if (t2 != t) + first = t2; + } + } + else // Not a substitution, except maybe St + { + t = __parse_unscoped_name(t0, last); + if (t != t0) + { + // unscoped-name might be <unscoped-template-name> + if (t != last && *t == 'I') + { + if (__sub_end_ == __sub_cap_) + { + __status_ = memory_alloc_failure; + return first; + } + *__sub_end_++ = __root_; + const char* t2 = __parse_template_args(t, last); + if (t2 != t) + first = t2; + } + else + { + // <unscoped-name> + first = t; + } + } + } + } + } + else + first = t; + } + else + first = t; + } + return first; +} + +// extension +// <dot-suffix> := .<anything and everything> + +const char* +__demangle_tree::__parse_dot_suffix(const char* first, const char* last) +{ + if (first != last && *first == '.') + { + if (__make<__dot_suffix>(__root_, first, last-first)) + first = last; + } + return first; +} + +// <encoding> ::= <function name> <bare-function-type> +// ::= <data name> +// ::= <special-name> + +const char* +__demangle_tree::__parse_encoding(const char* first, const char* last) +{ + const char* t = __parse_name(first, last); + if (t != first) + { + if (t != last && *t != 'E' && *t != '.') + { + __node* name = __root_; + bool has_return = name->ends_with_template(true) && + !name->is_ctor_dtor_conv(); + __node* ret = NULL; + const char* t2; + __tag_templates_ = false; + if (has_return) + { + t2 = __parse_type(t, last); + if (t2 != t) + { + ret = __root_; + t = t2; + } + else + return first; + } + t2 = __parse_bare_function_type(t, last); + if (t2 != t) + { + if (dynamic_cast<__void*>(__root_->__left_) != NULL) + __root_->__left_ = NULL; + if (__make<__function_signature>(ret, __root_)) + { + __node* cv = name->extract_cv(name); + if (__make<__function>(name, __root_)) + { + if (cv) + { + cv->__left_ = __root_; + cv->__size_ <<= 5; + __root_ = cv; + } + first = t2; + } + } + } + __tag_templates_ = true; + } + else + first = t; + } + else + first = __parse_special_name(first, last); + return first; +} + +// <mangled-name> ::= _Z<encoding> +// ::= <type> + +void +__demangle_tree::__parse() +{ + if (__mangled_name_begin_ == __mangled_name_end_) + { + __status_ = invalid_mangled_name; + return; + } + const char* t = NULL; + if (__mangled_name_end_ - __mangled_name_begin_ >= 2 && + __mangled_name_begin_[0] == '_' && + __mangled_name_begin_[1] == 'Z') + { + t = __parse_encoding(__mangled_name_begin_+2, __mangled_name_end_); + if (t != __mangled_name_begin_+2 && t != __mangled_name_end_ && *t == '.') + t = __parse_dot_suffix(t, __mangled_name_end_); + } + else + t = __parse_type(__mangled_name_begin_, __mangled_name_end_); + if (t == __mangled_name_end_ && __root_) + { + if (__fix_forward_references_) + { + if (__root_->fix_forward_references(__t_begin_, __t_end_)) + __status_ = success; + } + else + __status_ = success; + } +} + +__demangle_tree +__demangle(const char* mangled_name, char* buf, size_t bs) +{ + __demangle_tree t(mangled_name, buf, bs); + if (t.__status() == invalid_mangled_name) + t.__parse(); + return t; +} + +__demangle_tree +__demangle(const char* mangled_name) +{ + return __demangle(mangled_name, 0, 0); +} + +char* +__demangle(__demangle_tree dmg_tree, char* buf, size_t* n, int* status) +{ + if (dmg_tree.__status() != success) + { + if (status) + *status = dmg_tree.__status(); + return NULL; + } +#ifdef DEBUGGING +display(dmg_tree.__root_); +printf("\n"); +#endif + const size_t bs = buf == NULL ? 0 : *n; + ptrdiff_t sm = dmg_tree.__mangled_name_end_ - dmg_tree.__mangled_name_begin_; + ptrdiff_t est = sm + 60 * ( + (dmg_tree.__node_end_ - dmg_tree.__node_begin_) + + (dmg_tree.__sub_end_ - dmg_tree.__sub_begin_) + + (dmg_tree.__t_end_ - dmg_tree.__t_begin_)); + const unsigned N = 4096; + char tmp[N]; + ptrdiff_t s; + if (est <= bs) + { + char* e = dmg_tree.__get_demangled_name(buf); + *e++ = '\0'; + s = e - buf; + } + else if (est <= N) + { + char* e = dmg_tree.__get_demangled_name(tmp); + *e++ = '\0'; + s = e - tmp; + } + else + s = dmg_tree.size() + 1; + if (s > bs) + { + buf = static_cast<char*>(realloc(buf, s)); + if (buf == NULL) + { + if (status) + *status = memory_alloc_failure; + return NULL; + } + if (n) + *n = s; + } + if (est > bs) + { + if (est <= N) + strncpy(buf, tmp, s); + else + *dmg_tree.__get_demangled_name(buf) = '\0'; + } + if (status) + *status = success; + return buf; +} + +} // __libcxxabi + +#pragma GCC visibility pop +#pragma GCC visibility push(default) + +extern "C" +{ + +char* +__cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status) +{ + if (mangled_name == NULL || (buf != NULL && n == NULL)) + { + if (status) + *status = __libcxxabi::invalid_args; + return NULL; + } + const size_t bs = 64 * 1024; + __attribute((aligned(16))) char static_buf[bs]; + + buf = __libcxxabi::__demangle(__libcxxabi::__demangle(mangled_name, + static_buf, bs), + buf, n, status); + return buf; +} + +} // extern "C" + +} // abi diff --git a/system/lib/libcxxabi/src/cxa_exception.cpp b/system/lib/libcxxabi/src/cxa_exception.cpp new file mode 100644 index 00000000..b866f9e4 --- /dev/null +++ b/system/lib/libcxxabi/src/cxa_exception.cpp @@ -0,0 +1,622 @@ +//===------------------------- cxa_exception.cpp --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +// +// This file implements the "Exception Handling APIs" +// http://www.codesourcery.com/public/cxx-abi/abi-eh.html +// +//===----------------------------------------------------------------------===// + +#include "cxxabi.h" + +#include <exception> // for std::terminate +#include <cstdlib> // for malloc, free +#include <string> // for memset +#include <pthread.h> + +#include "cxa_exception.hpp" +#include "cxa_handlers.hpp" + +// +---------------------------+-----------------------------+---------------+ +// | __cxa_exception | _Unwind_Exception CLNGC++\0 | thrown object | +// +---------------------------+-----------------------------+---------------+ +// ^ +// | +// +-------------------------------------------------------+ +// | +// +---------------------------+-----------------------------+ +// | __cxa_dependent_exception | _Unwind_Exception CLNGC++\1 | +// +---------------------------+-----------------------------+ + +namespace __cxxabiv1 { + +#pragma GCC visibility push(default) + +// Utility routines +static +inline +__cxa_exception* +cxa_exception_from_thrown_object(void* thrown_object) +{ + return static_cast<__cxa_exception*>(thrown_object) - 1; +} + +// Note: This is never called when exception_header is masquerading as a +// __cxa_dependent_exception. +static +inline +void* +thrown_object_from_cxa_exception(__cxa_exception* exception_header) +{ + return static_cast<void*>(exception_header + 1); +} + +// Get the exception object from the unwind pointer. +// Relies on the structure layout, where the unwind pointer is right in +// front of the user's exception object +static +inline +__cxa_exception* +cxa_exception_from_exception_unwind_exception(_Unwind_Exception* unwind_exception) +{ + return cxa_exception_from_thrown_object(unwind_exception + 1 ); +} + +static +inline +size_t +cxa_exception_size_from_exception_thrown_size(size_t size) +{ + return size + sizeof (__cxa_exception); +} + +static void setExceptionClass(_Unwind_Exception* unwind_exception) { + unwind_exception->exception_class = kOurExceptionClass; +} + +static void setDependentExceptionClass(_Unwind_Exception* unwind_exception) { + unwind_exception->exception_class = kOurDependentExceptionClass; +} + +// Is it one of ours? +static bool isOurExceptionClass(const _Unwind_Exception* unwind_exception) { + return (unwind_exception->exception_class & get_vendor_and_language) == + (kOurExceptionClass & get_vendor_and_language); +} + +static bool isDependentException(_Unwind_Exception* unwind_exception) { + return (unwind_exception->exception_class & 0xFF) == 0x01; +} + +// This does not need to be atomic +static inline int incrementHandlerCount(__cxa_exception *exception) { + return ++exception->handlerCount; +} + +// This does not need to be atomic +static inline int decrementHandlerCount(__cxa_exception *exception) { + return --exception->handlerCount; +} + +#include "fallback_malloc.ipp" + +// Allocate some memory from _somewhere_ +static void *do_malloc(size_t size) { + void *ptr = std::malloc(size); + if (NULL == ptr) // if malloc fails, fall back to emergency stash + ptr = fallback_malloc(size); + return ptr; +} + +static void do_free(void *ptr) { + is_fallback_ptr(ptr) ? fallback_free(ptr) : std::free(ptr); +} + +/* + If reason isn't _URC_FOREIGN_EXCEPTION_CAUGHT, then the terminateHandler + stored in exc is called. Otherwise the exceptionDestructor stored in + exc is called, and then the memory for the exception is deallocated. + + This is never called for a __cxa_dependent_exception. +*/ +static +void +exception_cleanup_func(_Unwind_Reason_Code reason, _Unwind_Exception* unwind_exception) +{ + __cxa_exception* exception_header = cxa_exception_from_exception_unwind_exception(unwind_exception); + if (_URC_FOREIGN_EXCEPTION_CAUGHT != reason) + std::__terminate(exception_header->terminateHandler); + // Just in case there exists a dependent exception that is pointing to this, + // check the reference count and only destroy this if that count goes to zero. + __cxa_decrement_exception_refcount(unwind_exception + 1); +} + +static LIBCXXABI_NORETURN void failed_throw(__cxa_exception* exception_header) { +// Section 2.5.3 says: +// * For purposes of this ABI, several things are considered exception handlers: +// ** A terminate() call due to a throw. +// and +// * Upon entry, Following initialization of the catch parameter, +// a handler must call: +// * void *__cxa_begin_catch(void *exceptionObject ); + (void) __cxa_begin_catch(&exception_header->unwindHeader); + std::__terminate(exception_header->terminateHandler); +} + +extern "C" { + +// Allocate a __cxa_exception object, and zero-fill it. +// Reserve "thrown_size" bytes on the end for the user's exception +// object. Zero-fill the object. If memory can't be allocated, call +// std::terminate. Return a pointer to the memory to be used for the +// user's exception object. +void * __cxa_allocate_exception (size_t thrown_size) throw() { + size_t actual_size = cxa_exception_size_from_exception_thrown_size(thrown_size); + __cxa_exception* exception_header = static_cast<__cxa_exception*>(do_malloc(actual_size)); + if (NULL == exception_header) + std::terminate(); + std::memset(exception_header, 0, actual_size); + return thrown_object_from_cxa_exception(exception_header); +} + + +// Free a __cxa_exception object allocated with __cxa_allocate_exception. +void __cxa_free_exception (void * thrown_object) throw() { + do_free(cxa_exception_from_thrown_object(thrown_object)); +} + + +// This function shall allocate a __cxa_dependent_exception and +// return a pointer to it. (Really to the object, not past its' end). +// Otherwise, it will work like __cxa_allocate_exception. +void * __cxa_allocate_dependent_exception () { + size_t actual_size = sizeof(__cxa_dependent_exception); + void *ptr = do_malloc(actual_size); + if (NULL == ptr) + std::terminate(); + std::memset(ptr, 0, actual_size); + return ptr; +} + + +// This function shall free a dependent_exception. +// It does not affect the reference count of the primary exception. +void __cxa_free_dependent_exception (void * dependent_exception) { + do_free(dependent_exception); +} + + +// 2.4.3 Throwing the Exception Object +/* +After constructing the exception object with the throw argument value, +the generated code calls the __cxa_throw runtime library routine. This +routine never returns. + +The __cxa_throw routine will do the following: + +* Obtain the __cxa_exception header from the thrown exception object address, +which can be computed as follows: + __cxa_exception *header = ((__cxa_exception *) thrown_exception - 1); +* Save the current unexpected_handler and terminate_handler in the __cxa_exception header. +* Save the tinfo and dest arguments in the __cxa_exception header. +* Set the exception_class field in the unwind header. This is a 64-bit value +representing the ASCII string "XXXXC++\0", where "XXXX" is a +vendor-dependent string. That is, for implementations conforming to this +ABI, the low-order 4 bytes of this 64-bit value will be "C++\0". +* Increment the uncaught_exception flag. +* Call _Unwind_RaiseException in the system unwind library, Its argument is the +pointer to the thrown exception, which __cxa_throw itself received as an argument. +__Unwind_RaiseException begins the process of stack unwinding, described +in Section 2.5. In special cases, such as an inability to find a +handler, _Unwind_RaiseException may return. In that case, __cxa_throw +will call terminate, assuming that there was no handler for the +exception. +*/ +LIBCXXABI_NORETURN +void +__cxa_throw(void* thrown_object, std::type_info* tinfo, void (*dest)(void*)) +{ + __cxa_eh_globals *globals = __cxa_get_globals(); + __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); + + exception_header->unexpectedHandler = std::get_unexpected(); + exception_header->terminateHandler = std::get_terminate(); + exception_header->exceptionType = tinfo; + exception_header->exceptionDestructor = dest; + setExceptionClass(&exception_header->unwindHeader); + exception_header->referenceCount = 1; // This is a newly allocated exception, no need for thread safety. + globals->uncaughtExceptions += 1; // Not atomically, since globals are thread-local + + exception_header->unwindHeader.exception_cleanup = exception_cleanup_func; +#if __arm__ + _Unwind_SjLj_RaiseException(&exception_header->unwindHeader); +#else + _Unwind_RaiseException(&exception_header->unwindHeader); +#endif + // This only happens when there is no handler, or some unexpected unwinding + // error happens. + failed_throw(exception_header); +} + + +// 2.5.3 Exception Handlers +/* +The adjusted pointer is computed by the personality routine during phase 1 + and saved in the exception header (either __cxa_exception or + __cxa_dependent_exception). + + Requires: exception is native +*/ +void* +__cxa_get_exception_ptr(void* unwind_exception) throw() +{ + return cxa_exception_from_exception_unwind_exception + ( + static_cast<_Unwind_Exception*>(unwind_exception) + )->adjustedPtr; +} + +/* +This routine can catch foreign or native exceptions. If native, the exception +can be a primary or dependent variety. This routine may remain blissfully +ignorant of whether the native exception is primary or dependent. + +If the exception is native: +* Increment's the exception's handler count. +* Push the exception on the stack of currently-caught exceptions if it is not + already there (from a rethrow). +* Decrements the uncaught_exception count. +* Returns the adjusted pointer to the exception object, which is stored in + the __cxa_exception by the personality routine. + +If the exception is foreign, this means it did not originate from one of throw +routines. The foreign exception does not necessarily have a __cxa_exception +header. However we can catch it here with a catch (...), or with a call +to terminate or unexpected during unwinding. +* Do not try to increment the exception's handler count, we don't know where + it is. +* Push the exception on the stack of currently-caught exceptions only if the + stack is empty. The foreign exception has no way to link to the current + top of stack. If the stack is not empty, call terminate. Even with an + empty stack, this is hacked in by pushing a pointer to an imaginary + __cxa_exception block in front of the foreign exception. It would be better + if the __cxa_eh_globals structure had a stack of _Unwind_Exception, but it + doesn't. It has a stack of __cxa_exception (which has a next* in it). +* Do not decrement the uncaught_exception count because we didn't increment it + in __cxa_throw (or one of our rethrow functions). +* If we haven't terminated, assume the exception object is just past the + _Unwind_Exception and return a pointer to that. +*/ +void* +__cxa_begin_catch(void* unwind_arg) throw() +{ + _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(unwind_arg); + bool native_exception = isOurExceptionClass(unwind_exception); + __cxa_eh_globals* globals = __cxa_get_globals(); + // exception_header is a hackish offset from a foreign exception, but it + // works as long as we're careful not to try to access any __cxa_exception + // parts. + __cxa_exception* exception_header = + cxa_exception_from_exception_unwind_exception + ( + static_cast<_Unwind_Exception*>(unwind_exception) + ); + if (native_exception) + { + // Increment the handler count, removing the flag about being rethrown + exception_header->handlerCount = exception_header->handlerCount < 0 ? + -exception_header->handlerCount + 1 : exception_header->handlerCount + 1; + // place the exception on the top of the stack if it's not already + // there by a previous rethrow + if (exception_header != globals->caughtExceptions) + { + exception_header->nextException = globals->caughtExceptions; + globals->caughtExceptions = exception_header; + } + globals->uncaughtExceptions -= 1; // Not atomically, since globals are thread-local + return exception_header->adjustedPtr; + } + // Else this is a foreign exception + // If the caughtExceptions stack is not empty, terminate + if (globals->caughtExceptions != 0) + std::terminate(); + // Push the foreign exception on to the stack + globals->caughtExceptions = exception_header; + return unwind_exception + 1; +} + + +/* +Upon exit for any reason, a handler must call: + void __cxa_end_catch (); + +This routine can be called for either a native or foreign exception. +For a native exception: +* Locates the most recently caught exception and decrements its handler count. +* Removes the exception from the caught exception stack, if the handler count goes to zero. +* If the handler count goes down to zero, and the exception was not re-thrown + by throw, it locates the primary exception (which may be the same as the one + it's handling) and decrements its reference count. If that reference count + goes to zero, the function destroys the exception. In any case, if the current + exception is a dependent exception, it destroys that. + +For a foreign exception: +* If it has been rethrown, there is nothing to do. +* Otherwise delete the exception and pop the catch stack to empty. +*/ +void __cxa_end_catch() +{ + static_assert(sizeof(__cxa_exception) == sizeof(__cxa_dependent_exception), + "sizeof(__cxa_exception) must be equal to sizeof(__cxa_dependent_exception)"); + __cxa_eh_globals* globals = __cxa_get_globals_fast(); // __cxa_get_globals called in __cxa_begin_catch + __cxa_exception* exception_header = globals->caughtExceptions; + // If we've rethrown a foreign exception, then globals->caughtExceptions + // will have been made an empty stack by __cxa_rethrow() and there is + // nothing more to be done. Do nothing! + if (NULL != exception_header) + { + bool native_exception = isOurExceptionClass(&exception_header->unwindHeader); + if (native_exception) + { + // This is a native exception + if (exception_header->handlerCount < 0) + { + // The exception has been rethrown by __cxa_rethrow, so don't delete it + if (0 == incrementHandlerCount(exception_header)) + { + // Remove from the chain of uncaught exceptions + globals->caughtExceptions = exception_header->nextException; + // but don't destroy + } + // Keep handlerCount negative in case there are nested catch's + // that need to be told that this exception is rethrown. Don't + // erase this rethrow flag until the exception is recaught. + } + else + { + // The native exception has not been rethrown + if (0 == decrementHandlerCount(exception_header)) + { + // Remove from the chain of uncaught exceptions + globals->caughtExceptions = exception_header->nextException; + // Destroy this exception, being careful to distinguish + // between dependent and primary exceptions + if (isDependentException(&exception_header->unwindHeader)) + { + // Reset exception_header to primaryException and deallocate the dependent exception + __cxa_dependent_exception* dep_exception_header = + reinterpret_cast<__cxa_dependent_exception*>(exception_header); + exception_header = + cxa_exception_from_thrown_object(dep_exception_header->primaryException); + __cxa_free_dependent_exception(dep_exception_header); + } + // Destroy the primary exception only if its referenceCount goes to 0 + // (this decrement must be atomic) + __cxa_decrement_exception_refcount(thrown_object_from_cxa_exception(exception_header)); + } + } + } + else + { + // The foreign exception has not been rethrown. Pop the stack + // and delete it. If there are nested catch's and they try + // to touch a foreign exception in any way, that is undefined + // behavior. They likely can't since the only way to catch + // a foreign exception is with catch (...)! + _Unwind_DeleteException(&globals->caughtExceptions->unwindHeader); + globals->caughtExceptions = 0; + } + } +} + +// Note: exception_header may be masquerading as a __cxa_dependent_exception +// and that's ok. exceptionType is there too. +// However watch out for foreign exceptions. Return null for them. +std::type_info * __cxa_current_exception_type() { +// get the current exception + __cxa_eh_globals *globals = __cxa_get_globals_fast(); + if (NULL == globals) + return NULL; // If there have never been any exceptions, there are none now. + __cxa_exception *exception_header = globals->caughtExceptions; + if (NULL == exception_header) + return NULL; // No current exception + if (!isOurExceptionClass(&exception_header->unwindHeader)) + return NULL; + return exception_header->exceptionType; +} + +// 2.5.4 Rethrowing Exceptions +/* This routine can rethrow native or foreign exceptions. +If the exception is native: +* marks the exception object on top of the caughtExceptions stack + (in an implementation-defined way) as being rethrown. +* If the caughtExceptions stack is empty, it calls terminate() + (see [C++FDIS] [except.throw], 15.1.8). +* It then calls _Unwind_RaiseException which should not return + (terminate if it does). + Note: exception_header may be masquerading as a __cxa_dependent_exception + and that's ok. +*/ +LIBCXXABI_NORETURN +void +__cxa_rethrow() +{ + __cxa_eh_globals* globals = __cxa_get_globals(); + __cxa_exception* exception_header = globals->caughtExceptions; + if (NULL == exception_header) + std::terminate(); // throw; called outside of a exception handler + bool native_exception = isOurExceptionClass(&exception_header->unwindHeader); + if (native_exception) + { + // Mark the exception as being rethrown (reverse the effects of __cxa_begin_catch) + exception_header->handlerCount = -exception_header->handlerCount; + globals->uncaughtExceptions += 1; + // __cxa_end_catch will remove this exception from the caughtExceptions stack if necessary + } + else // this is a foreign exception + { + // The only way to communicate to __cxa_end_catch that we've rethrown + // a foreign exception, so don't delete us, is to pop the stack here + // which must be empty afterwards. Then __cxa_end_catch will do + // nothing + globals->caughtExceptions = 0; + } +#if __arm__ + (void) _Unwind_SjLj_Resume_or_Rethrow(&exception_header->unwindHeader); +#else + (void)_Unwind_RaiseException(&exception_header->unwindHeader); +#endif + + // If we get here, some kind of unwinding error has occurred. + // There is some weird code generation bug happening with + // Apple clang version 4.0 (tags/Apple/clang-418.0.2) (based on LLVM 3.1svn) + // If we call failed_throw here. Turns up with -O2 or higher, and -Os. + __cxa_begin_catch(&exception_header->unwindHeader); + if (native_exception) + std::__terminate(exception_header->terminateHandler); + // Foreign exception: can't get exception_header->terminateHandler + std::terminate(); +} + +/* + If thrown_object is not null, atomically increment the referenceCount field + of the __cxa_exception header associated with the thrown object referred to + by thrown_object. + + Requires: If thrown_object is not NULL, it is a native exception. +*/ +void +__cxa_increment_exception_refcount(void* thrown_object) throw() +{ + if (thrown_object != NULL ) + { + __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); + __sync_add_and_fetch(&exception_header->referenceCount, 1); + } +} + +/* + If thrown_object is not null, atomically decrement the referenceCount field + of the __cxa_exception header associated with the thrown object referred to + by thrown_object. If the referenceCount drops to zero, destroy and + deallocate the exception. + + Requires: If thrown_object is not NULL, it is a native exception. +*/ +void +__cxa_decrement_exception_refcount(void* thrown_object) throw() +{ + if (thrown_object != NULL ) + { + __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); + if (__sync_sub_and_fetch(&exception_header->referenceCount, size_t(1)) == 0) + { + if (NULL != exception_header->exceptionDestructor) + exception_header->exceptionDestructor(thrown_object); + __cxa_free_exception(thrown_object); + } + } +} + +/* + Returns a pointer to the thrown object (if any) at the top of the + caughtExceptions stack. Atommically increment the exception's referenceCount. + If there is no such thrown object or if the thrown object is foreign, + returns null. + + We can use __cxa_get_globals_fast here to get the globals because if there have + been no exceptions thrown, ever, on this thread, we can return NULL without + the need to allocate the exception-handling globals. +*/ +void* +__cxa_current_primary_exception() throw() +{ +// get the current exception + __cxa_eh_globals* globals = __cxa_get_globals_fast(); + if (NULL == globals) + return NULL; // If there are no globals, there is no exception + __cxa_exception* exception_header = globals->caughtExceptions; + if (NULL == exception_header) + return NULL; // No current exception + if (!isOurExceptionClass(&exception_header->unwindHeader)) + return NULL; // Can't capture a foreign exception (no way to refcount it) + if (isDependentException(&exception_header->unwindHeader)) { + __cxa_dependent_exception* dep_exception_header = + reinterpret_cast<__cxa_dependent_exception*>(exception_header); + exception_header = cxa_exception_from_thrown_object(dep_exception_header->primaryException); + } + void* thrown_object = thrown_object_from_cxa_exception(exception_header); + __cxa_increment_exception_refcount(thrown_object); + return thrown_object; +} + +/* + If reason isn't _URC_FOREIGN_EXCEPTION_CAUGHT, then the terminateHandler + stored in exc is called. Otherwise the referenceCount stored in the + primary exception is decremented, destroying the primary if necessary. + Finally the dependent exception is destroyed. +*/ +static +void +dependent_exception_cleanup(_Unwind_Reason_Code reason, _Unwind_Exception* unwind_exception) +{ + __cxa_dependent_exception* dep_exception_header = + reinterpret_cast<__cxa_dependent_exception*>(unwind_exception + 1) - 1; + if (_URC_FOREIGN_EXCEPTION_CAUGHT != reason) + std::__terminate(dep_exception_header->terminateHandler); + __cxa_decrement_exception_refcount(dep_exception_header->primaryException); + __cxa_free_dependent_exception(dep_exception_header); +} + +/* + If thrown_object is not null, allocate, initialize and thow a dependent + exception. +*/ +void +__cxa_rethrow_primary_exception(void* thrown_object) +{ + if ( thrown_object != NULL ) + { + // thrown_object guaranteed to be native because + // __cxa_current_primary_exception returns NULL for foreign exceptions + __cxa_exception* exception_header = cxa_exception_from_thrown_object(thrown_object); + __cxa_dependent_exception* dep_exception_header = + static_cast<__cxa_dependent_exception*>(__cxa_allocate_dependent_exception()); + dep_exception_header->primaryException = thrown_object; + __cxa_increment_exception_refcount(thrown_object); + dep_exception_header->exceptionType = exception_header->exceptionType; + dep_exception_header->unexpectedHandler = std::get_unexpected(); + dep_exception_header->terminateHandler = std::get_terminate(); + setDependentExceptionClass(&dep_exception_header->unwindHeader); + __cxa_get_globals()->uncaughtExceptions += 1; + dep_exception_header->unwindHeader.exception_cleanup = dependent_exception_cleanup; +#if __arm__ + _Unwind_SjLj_RaiseException(&dep_exception_header->unwindHeader); +#else + _Unwind_RaiseException(&dep_exception_header->unwindHeader); +#endif + // Some sort of unwinding error. Note that terminate is a handler. + __cxa_begin_catch(&dep_exception_header->unwindHeader); + } + // If we return client will call terminate() +} + +bool +__cxa_uncaught_exception() throw() +{ + // This does not report foreign exceptions in flight + __cxa_eh_globals* globals = __cxa_get_globals_fast(); + if (globals == 0) + return false; + return globals->uncaughtExceptions != 0; +} + +} // extern "C" + +#pragma GCC visibility pop + +} // abi diff --git a/system/lib/libcxxabi/src/cxa_exception.hpp b/system/lib/libcxxabi/src/cxa_exception.hpp new file mode 100644 index 00000000..cf019d4c --- /dev/null +++ b/system/lib/libcxxabi/src/cxa_exception.hpp @@ -0,0 +1,118 @@ +//===------------------------- cxa_exception.hpp --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +// +// This file implements the "Exception Handling APIs" +// http://www.codesourcery.com/public/cxx-abi/abi-eh.html +// +//===----------------------------------------------------------------------===// + +#include <exception> // for std::unexpected_handler and std::terminate_handler +#include <cxxabi.h> +#include "unwind.h" + +namespace __cxxabiv1 { + +#pragma GCC visibility push(hidden) + +static const uint64_t kOurExceptionClass = 0x434C4E47432B2B00; // CLNGC++\0 +static const uint64_t kOurDependentExceptionClass = 0x434C4E47432B2B01; // CLNGC++\1 +static const uint64_t get_vendor_and_language = 0xFFFFFFFFFFFFFF00; // mask for CLNGC++ + + struct __cxa_exception { +#if __LP64__ + // This is a new field to support C++ 0x exception_ptr. + // For binary compatibility it is at the start of this + // struct which is prepended to the object thrown in + // __cxa_allocate_exception. + size_t referenceCount; +#endif + + // Manage the exception object itself. + std::type_info *exceptionType; + void (*exceptionDestructor)(void *); + std::unexpected_handler unexpectedHandler; + std::terminate_handler terminateHandler; + + __cxa_exception *nextException; + + int handlerCount; + +#ifdef __ARM_EABI_UNWINDER__ + __cxa_exception* nextPropagatingException; + int propagationCount; +#else + int handlerSwitchValue; + const unsigned char *actionRecord; + const unsigned char *languageSpecificData; + void *catchTemp; + void *adjustedPtr; +#endif + +#if !__LP64__ + // This is a new field to support C++ 0x exception_ptr. + // For binary compatibility it is placed where the compiler + // previously adding padded to 64-bit align unwindHeader. + size_t referenceCount; +#endif + + _Unwind_Exception unwindHeader; + }; + +// http://sourcery.mentor.com/archives/cxx-abi-dev/msg01924.html + + struct __cxa_dependent_exception { +#if __LP64__ + void* primaryException; +#endif + + std::type_info *exceptionType; + void (*exceptionDestructor)(void *); + std::unexpected_handler unexpectedHandler; + std::terminate_handler terminateHandler; + + __cxa_exception *nextException; + + int handlerCount; + +#ifdef __ARM_EABI_UNWINDER__ + __cxa_exception* nextPropagatingException; + int propagationCount; +#else + int handlerSwitchValue; + const unsigned char *actionRecord; + const unsigned char *languageSpecificData; + void * catchTemp; + void *adjustedPtr; +#endif + +#if !__LP64__ + void* primaryException; +#endif + + _Unwind_Exception unwindHeader; + }; + + struct __cxa_eh_globals { + __cxa_exception * caughtExceptions; + unsigned int uncaughtExceptions; +#ifdef __ARM_EABI_UNWINDER__ + __cxa_exception* propagatingExceptions; +#endif + }; + +#pragma GCC visibility pop +#pragma GCC visibility push(default) + + extern "C" __cxa_eh_globals * __cxa_get_globals (); + extern "C" __cxa_eh_globals * __cxa_get_globals_fast (); + + extern "C" void * __cxa_allocate_dependent_exception (); + extern "C" void __cxa_free_dependent_exception (void * dependent_exception); + +#pragma GCC visibility pop +} diff --git a/system/lib/libcxxabi/src/cxa_exception_storage.cpp b/system/lib/libcxxabi/src/cxa_exception_storage.cpp new file mode 100644 index 00000000..2c269c7f --- /dev/null +++ b/system/lib/libcxxabi/src/cxa_exception_storage.cpp @@ -0,0 +1,91 @@ +//===--------------------- cxa_exception_storage.cpp ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +// +// This file implements the storage for the "Caught Exception Stack" +// http://www.codesourcery.com/public/cxx-abi/abi-eh.html (section 2.2.2) +// +//===----------------------------------------------------------------------===// + +#include "cxa_exception.hpp" + +#ifdef HAS_THREAD_LOCAL + +namespace __cxxabiv1 { + +namespace { + __cxa_eh_globals * __globals () { + static thread_local __cxa_eh_globals eh_globals; + return &eh_globals; + } + } + +extern "C" { + __cxa_eh_globals * __cxa_get_globals () { return __globals (); } + __cxa_eh_globals * __cxa_get_globals_fast () { return __globals (); } + } +} + +#else + +#include <pthread.h> +#include <cstdlib> // for calloc, free +#include "abort_message.h" + +// In general, we treat all pthread errors as fatal. +// We cannot call std::terminate() because that will in turn +// call __cxa_get_globals() and cause infinite recursion. + +namespace __cxxabiv1 { +namespace { + pthread_key_t key_; + pthread_once_t flag_ = PTHREAD_ONCE_INIT; + + void destruct_ (void *p) { + std::free ( p ); + if ( 0 != ::pthread_setspecific ( key_, NULL ) ) + abort_message("cannot zero out thread value for __cxa_get_globals()"); + } + + void construct_ () { + if ( 0 != pthread_key_create ( &key_, destruct_ ) ) + abort_message("cannot create pthread key for __cxa_get_globals()"); + } +} + +extern "C" { + __cxa_eh_globals * __cxa_get_globals () { + // Try to get the globals for this thread + __cxa_eh_globals* retVal = __cxa_get_globals_fast (); + + // If this is the first time we've been asked for these globals, create them + if ( NULL == retVal ) { + retVal = static_cast<__cxa_eh_globals*> + (std::calloc (1, sizeof (__cxa_eh_globals))); + if ( NULL == retVal ) + abort_message("cannot allocate __cxa_eh_globals"); + if ( 0 != pthread_setspecific ( key_, retVal ) ) + abort_message("pthread_setspecific failure in __cxa_get_globals()"); + } + return retVal; + } + + // Note that this implementation will reliably return NULL if not + // preceeded by a call to __cxa_get_globals(). This is an extension + // to the Itanium ABI and is taken advantage of in several places in + // libc++abi. + __cxa_eh_globals * __cxa_get_globals_fast () { + // First time through, create the key. + if (0 != pthread_once(&flag_, construct_)) + abort_message("pthread_once failure in __cxa_get_globals_fast()"); +// static int init = construct_(); + return static_cast<__cxa_eh_globals*>(::pthread_getspecific(key_)); + } + +} +} +#endif diff --git a/system/lib/libcxxabi/src/cxa_guard.cpp b/system/lib/libcxxabi/src/cxa_guard.cpp new file mode 100644 index 00000000..814aaeb1 --- /dev/null +++ b/system/lib/libcxxabi/src/cxa_guard.cpp @@ -0,0 +1,231 @@ +//===---------------------------- cxa_guard.cpp ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "abort_message.h" + +#include <pthread.h> +#include <stdint.h> + +/* + This implementation must be careful to not call code external to this file + which will turn around and try to call __cxa_guard_acquire reentrantly. + For this reason, the headers of this file are as restricted as possible. + Previous implementations of this code for __APPLE__ have used + pthread_mutex_lock and the abort_message utility without problem. This + implementation also uses pthread_cond_wait which has tested to not be a + problem. +*/ + +namespace __cxxabiv1 +{ + +namespace +{ + +#if LIBCXXABI_ARMEABI + +// A 32-bit, 4-byte-aligned static data value. The least significant 2 bits must +// be statically initialized to 0. +typedef uint32_t guard_type; + +// Test the lowest bit. +inline bool is_initialized(guard_type* guard_object) { + return (*guard_object) & 1; +} + +inline void set_initialized(guard_type* guard_object) { + *guard_object |= 1; +} + +#else + +typedef uint64_t guard_type; + +bool is_initialized(guard_type* guard_object) { + char* initialized = (char*)guard_object; + return *initialized; +} + +void set_initialized(guard_type* guard_object) { + char* initialized = (char*)guard_object; + *initialized = 1; +} + +#endif + +pthread_mutex_t guard_mut = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t guard_cv = PTHREAD_COND_INITIALIZER; + +#if __APPLE__ + +typedef uint32_t lock_type; + +#if __LITTLE_ENDIAN__ + +inline +lock_type +get_lock(uint64_t x) +{ + return static_cast<lock_type>(x >> 32); +} + +inline +void +set_lock(uint64_t& x, lock_type y) +{ + x = static_cast<uint64_t>(y) << 32; +} + +#else // __LITTLE_ENDIAN__ + +inline +lock_type +get_lock(uint64_t x) +{ + return static_cast<lock_type>(x); +} + +inline +void +set_lock(uint64_t& x, lock_type y) +{ + x = y; +} + +#endif // __LITTLE_ENDIAN__ + +#else // __APPLE__ + +typedef bool lock_type; + +inline +lock_type +get_lock(uint64_t x) +{ + union + { + uint64_t guard; + uint8_t lock[2]; + } f = {x}; + return f.lock[1] != 0; +} + +inline +void +set_lock(uint64_t& x, lock_type y) +{ + union + { + uint64_t guard; + uint8_t lock[2]; + } f = {0}; + f.lock[1] = y; + x = f.guard; +} + +inline +lock_type +get_lock(uint32_t x) +{ + union + { + uint32_t guard; + uint8_t lock[2]; + } f = {x}; + return f.lock[1] != 0; +} + +inline +void +set_lock(uint32_t& x, lock_type y) +{ + union + { + uint32_t guard; + uint8_t lock[2]; + } f = {0}; + f.lock[1] = y; + x = f.guard; +} + +#endif // __APPLE__ + +} // unnamed namespace + +extern "C" +{ + +int __cxa_guard_acquire(guard_type* guard_object) +{ + char* initialized = (char*)guard_object; + if (pthread_mutex_lock(&guard_mut)) + abort_message("__cxa_guard_acquire failed to acquire mutex"); + int result = *initialized == 0; + if (result) + { +#if __APPLE__ + const lock_type id = pthread_mach_thread_np(pthread_self()); + lock_type lock = get_lock(*guard_object); + if (lock) + { + // if this thread set lock for this same guard_object, abort + if (lock == id) + abort_message("__cxa_guard_acquire detected deadlock"); + do + { + if (pthread_cond_wait(&guard_cv, &guard_mut)) + abort_message("__cxa_guard_acquire condition variable wait failed"); + lock = get_lock(*guard_object); + } while (lock); + result = !is_initialized(guard_object); + if (result) + set_lock(*guard_object, id); + } + else + set_lock(*guard_object, id); +#else // __APPLE__ + while (get_lock(*guard_object)) + if (pthread_cond_wait(&guard_cv, &guard_mut)) + abort_message("__cxa_guard_acquire condition variable wait failed"); + result = *initialized == 0; + if (result) + set_lock(*guard_object, true); +#endif // __APPLE__ + } + if (pthread_mutex_unlock(&guard_mut)) + abort_message("__cxa_guard_acquire failed to release mutex"); + return result; +} + +void __cxa_guard_release(guard_type* guard_object) +{ + if (pthread_mutex_lock(&guard_mut)) + abort_message("__cxa_guard_release failed to acquire mutex"); + *guard_object = 0; + set_initialized(guard_object); + if (pthread_mutex_unlock(&guard_mut)) + abort_message("__cxa_guard_release failed to release mutex"); + if (pthread_cond_broadcast(&guard_cv)) + abort_message("__cxa_guard_release failed to broadcast condition variable"); +} + +void __cxa_guard_abort(guard_type* guard_object) +{ + if (pthread_mutex_lock(&guard_mut)) + abort_message("__cxa_guard_abort failed to acquire mutex"); + *guard_object = 0; + if (pthread_mutex_unlock(&guard_mut)) + abort_message("__cxa_guard_abort failed to release mutex"); + if (pthread_cond_broadcast(&guard_cv)) + abort_message("__cxa_guard_abort failed to broadcast condition variable"); +} + +} // extern "C" + +} // __cxxabiv1 diff --git a/system/lib/libcxxabi/src/cxa_handlers.cpp b/system/lib/libcxxabi/src/cxa_handlers.cpp new file mode 100644 index 00000000..be43181c --- /dev/null +++ b/system/lib/libcxxabi/src/cxa_handlers.cpp @@ -0,0 +1,196 @@ +//===------------------------- cxa_handlers.cpp ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +// +// This file implements the functionality associated with the terminate_handler, +// unexpected_handler, and new_handler. +//===----------------------------------------------------------------------===// + +#include <stdexcept> +#include <new> +#include <exception> +#include "abort_message.h" +#include "cxxabi.h" +#include "cxa_handlers.hpp" +#include "cxa_exception.hpp" +#include "private_typeinfo.h" + +namespace std +{ + +static const char* cause = "uncaught"; + +static void default_terminate_handler() +{ + // If there might be an uncaught exception + using namespace __cxxabiv1; + __cxa_eh_globals* globals = __cxa_get_globals_fast(); + if (globals) + { + __cxa_exception* exception_header = globals->caughtExceptions; + // If there is an uncaught exception + if (exception_header) + { + _Unwind_Exception* unwind_exception = + reinterpret_cast<_Unwind_Exception*>(exception_header + 1) - 1; + bool native_exception = + (unwind_exception->exception_class & get_vendor_and_language) == + (kOurExceptionClass & get_vendor_and_language); + if (native_exception) + { + void* thrown_object = + unwind_exception->exception_class == kOurDependentExceptionClass ? + ((__cxa_dependent_exception*)exception_header)->primaryException : + exception_header + 1; + const __shim_type_info* thrown_type = + static_cast<const __shim_type_info*>(exception_header->exceptionType); + // Try to get demangled name of thrown_type + int status; + char buf[1024]; + size_t len = sizeof(buf); + const char* name = __cxa_demangle(thrown_type->name(), buf, &len, &status); + if (status != 0) + name = thrown_type->name(); + // If the uncaught exception can be caught with std::exception& + const __shim_type_info* catch_type = + static_cast<const __shim_type_info*>(&typeid(exception)); + if (catch_type->can_catch(thrown_type, thrown_object)) + { + // Include the what() message from the exception + const exception* e = static_cast<const exception*>(thrown_object); + abort_message("terminating with %s exception of type %s: %s", + cause, name, e->what()); + } + else + // Else just note that we're terminating with an exception + abort_message("terminating with %s exception of type %s", + cause, name); + } + else + // Else we're terminating with a foreign exception + abort_message("terminating with %s foreign exception", cause); + } + } + // Else just note that we're terminating + abort_message("terminating"); +} + +static void default_unexpected_handler() +{ + cause = "unexpected"; + terminate(); +} + +static terminate_handler __terminate_handler = default_terminate_handler; +static unexpected_handler __unexpected_handler = default_unexpected_handler; +static new_handler __new_handler = 0; + +unexpected_handler +set_unexpected(unexpected_handler func) _NOEXCEPT +{ + if (func == 0) + func = default_unexpected_handler; + return __sync_lock_test_and_set(&__unexpected_handler, func); +} + +unexpected_handler +get_unexpected() _NOEXCEPT +{ + return __sync_fetch_and_add(&__unexpected_handler, (unexpected_handler)0); +} + +__attribute__((visibility("hidden"), noreturn)) +void +__unexpected(unexpected_handler func) +{ + func(); + // unexpected handler should not return + abort_message("unexpected_handler unexpectedly returned"); +} + +__attribute__((noreturn)) +void +unexpected() +{ + __unexpected(get_unexpected()); +} + +terminate_handler +set_terminate(terminate_handler func) _NOEXCEPT +{ + if (func == 0) + func = default_terminate_handler; + return __sync_lock_test_and_set(&__terminate_handler, func); +} + +terminate_handler +get_terminate() _NOEXCEPT +{ + return __sync_fetch_and_add(&__terminate_handler, (terminate_handler)0); +} + +__attribute__((visibility("hidden"), noreturn)) +void +__terminate(terminate_handler func) _NOEXCEPT +{ +#if __has_feature(cxx_exceptions) + try + { +#endif // __has_feature(cxx_exceptions) + func(); + // handler should not return + abort_message("terminate_handler unexpectedly returned"); +#if __has_feature(cxx_exceptions) + } + catch (...) + { + // handler should not throw exception + abort_message("terminate_handler unexpectedly threw an exception"); + } +#endif // #if __has_feature(cxx_exceptions) +} + +__attribute__((noreturn)) +void +terminate() _NOEXCEPT +{ + // If there might be an uncaught exception + using namespace __cxxabiv1; + __cxa_eh_globals* globals = __cxa_get_globals_fast(); + if (globals) + { + __cxa_exception* exception_header = globals->caughtExceptions; + if (exception_header) + { + _Unwind_Exception* unwind_exception = + reinterpret_cast<_Unwind_Exception*>(exception_header + 1) - 1; + bool native_exception = + (unwind_exception->exception_class & get_vendor_and_language) == + (kOurExceptionClass & get_vendor_and_language); + if (native_exception) + { + __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; + __terminate(exception_header->terminateHandler); + } + } + } + __terminate(get_terminate()); +} + +new_handler +set_new_handler(new_handler handler) _NOEXCEPT +{ + return __sync_lock_test_and_set(&__new_handler, handler); +} + +new_handler +get_new_handler() _NOEXCEPT +{ + return __sync_fetch_and_add(&__new_handler, (new_handler)0); +} + +} // std diff --git a/system/lib/libcxxabi/src/cxa_handlers.hpp b/system/lib/libcxxabi/src/cxa_handlers.hpp new file mode 100644 index 00000000..a7f582c0 --- /dev/null +++ b/system/lib/libcxxabi/src/cxa_handlers.hpp @@ -0,0 +1,26 @@ +//===------------------------- cxa_handlers.cpp ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +// +// This file implements the functionality associated with the terminate_handler, +// unexpected_handler, and new_handler. +//===----------------------------------------------------------------------===// + +#include <exception> + +namespace std +{ + +__attribute__((visibility("hidden"), noreturn)) +void +__unexpected(unexpected_handler func); + +__attribute__((visibility("hidden"), noreturn)) +void +__terminate(terminate_handler func) _NOEXCEPT; + +} // std diff --git a/system/lib/libcxxabi/src/cxa_new_delete.cpp b/system/lib/libcxxabi/src/cxa_new_delete.cpp new file mode 100644 index 00000000..6352001b --- /dev/null +++ b/system/lib/libcxxabi/src/cxa_new_delete.cpp @@ -0,0 +1,231 @@ +//===------------------------ cxa_new_delete.cpp --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +// +// This file implements the new and delete operators. +//===----------------------------------------------------------------------===// + +#include <new> +#include <cstdlib> + +/* +[new.delete.single] + +* Executes a loop: Within the loop, the function first attempts to allocate + the requested storage. Whether the attempt involves a call to the Standard C + library function malloc is unspecified. + +* Returns a pointer to the allocated storage if the attempt is successful. + Otherwise, if the current new_handler (18.6.2.5) is a null pointer value, + throws bad_alloc. + +* Otherwise, the function calls the current new_handler function (18.6.2.3). + If the called function returns, the loop repeats. + +* The loop terminates when an attempt to allocate the requested storage is + successful or when a called new_handler function does not return. +*/ +__attribute__((__weak__, __visibility__("default"))) +void * +operator new(std::size_t size) +#if !__has_feature(cxx_noexcept) + throw(std::bad_alloc) +#endif +{ + if (size == 0) + size = 1; + void* p; + while ((p = std::malloc(size)) == 0) + { + std::new_handler nh = std::get_new_handler(); + if (nh) + nh(); + else + throw std::bad_alloc(); + } + return p; +} + +/* +Note: The relationships among these operators is both carefully considered +and standard in C++11. Please do not change them without fully understanding +the consequences of doing so. Reference: +http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2158.html +*/ +/* +[new.delete.single] + +Calls operator new(size). If the call returns normally, returns the result of +that call. Otherwise, returns a null pointer. +*/ +__attribute__((__weak__, __visibility__("default"))) +void* +operator new(size_t size, const std::nothrow_t&) +#if __has_feature(cxx_noexcept) + noexcept +#else + throw() +#endif +{ + void* p = 0; + try + { + p = ::operator new(size); + } + catch (...) + { + } + return p; +} + +/* +[new.delete.array] + +Returns operator new(size). +*/ +__attribute__((__weak__, __visibility__("default"))) +void* +operator new[](size_t size) +#if !__has_feature(cxx_noexcept) + throw(std::bad_alloc) +#endif +{ + return ::operator new(size); +} + +/* +[new.delete.array] + +Calls operator new[](size). If the call returns normally, returns the result +of that call. Otherwise, returns a null pointer. +*/ +__attribute__((__weak__, __visibility__("default"))) +void* +operator new[](size_t size, const std::nothrow_t&) +#if __has_feature(cxx_noexcept) + noexcept +#else + throw() +#endif +{ + void* p = 0; + try + { + p = ::operator new[](size); + } + catch (...) + { + } + return p; +} + +/* +[new.delete.single] + +If ptr is null, does nothing. Otherwise, reclaims the storage allocated by the +earlier call to operator new. +*/ +__attribute__((__weak__, __visibility__("default"))) +void +operator delete(void* ptr) +#if __has_feature(cxx_noexcept) + noexcept +#else + throw() +#endif +{ + if (ptr) + std::free(ptr); +} + +/* +[new.delete.single] + +calls operator delete(ptr) +*/ +__attribute__((__weak__, __visibility__("default"))) +void +operator delete(void* ptr, const std::nothrow_t&) +#if __has_feature(cxx_noexcept) + noexcept +#else + throw() +#endif +{ + ::operator delete(ptr); +} + +/* +[new.delete.array] + +Calls operator delete(ptr) +*/ +__attribute__((__weak__, __visibility__("default"))) +void +operator delete[] (void* ptr) +#if __has_feature(cxx_noexcept) + noexcept +#else + throw() +#endif +{ + ::operator delete(ptr); +} + +/* +[new.delete.array] + +calls operator delete[](ptr) +*/ +__attribute__((__weak__, __visibility__("default"))) +void +operator delete[] (void* ptr, const std::nothrow_t&) +#if __has_feature(cxx_noexcept) + noexcept +#else + throw() +#endif +{ + ::operator delete[](ptr); +} + +namespace std +{ + +// bad_alloc + +bad_alloc::bad_alloc() _NOEXCEPT +{ +} + +bad_alloc::~bad_alloc() _NOEXCEPT +{ +} + +const char* +bad_alloc::what() const _NOEXCEPT +{ + return "std::bad_alloc"; +} + +// bad_array_new_length + +bad_array_new_length::bad_array_new_length() _NOEXCEPT +{ +} + +bad_array_new_length::~bad_array_new_length() _NOEXCEPT +{ +} + +const char* +bad_array_new_length::what() const _NOEXCEPT +{ + return "bad_array_new_length"; +} + +} // std diff --git a/system/lib/libcxxabi/src/cxa_personality.cpp b/system/lib/libcxxabi/src/cxa_personality.cpp new file mode 100644 index 00000000..e49278c0 --- /dev/null +++ b/system/lib/libcxxabi/src/cxa_personality.cpp @@ -0,0 +1,987 @@ +//===------------------------- cxa_exception.cpp --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +// +// This file implements the "Exception Handling APIs" +// http://www.codesourcery.com/public/cxx-abi/abi-eh.html +// http://www.intel.com/design/itanium/downloads/245358.htm +// +//===----------------------------------------------------------------------===// + +#include "unwind.h" +#include "cxa_exception.hpp" +#include "cxa_handlers.hpp" +#include "private_typeinfo.h" +#include <typeinfo> +#include <stdlib.h> +#include <assert.h> + +/* + Exception Header Layout: + ++---------------------------+-----------------------------+---------------+ +| __cxa_exception | _Unwind_Exception CLNGC++\0 | thrown object | ++---------------------------+-----------------------------+---------------+ + ^ + | + +-------------------------------------------------------+ + | ++---------------------------+-----------------------------+ +| __cxa_dependent_exception | _Unwind_Exception CLNGC++\1 | ++---------------------------+-----------------------------+ + + Exception Handling Table Layout: + ++-----------------+--------+ +| lpStartEncoding | (char) | ++---------+-------+--------+---------------+-----------------------+ +| lpStart | (encoded wtih lpStartEncoding) | defaults to funcStart | ++---------+-----+--------+-----------------+---------------+-------+ +| ttypeEncoding | (char) | Encoding of the type_info table | ++---------------+-+------+----+----------------------------+----------------+ +| classInfoOffset | (ULEB128) | Offset to type_info table, defaults to null | ++-----------------++--------+-+----------------------------+----------------+ +| callSiteEncoding | (char) | Encoding for Call Site Table | ++------------------+--+-----+-----+------------------------+--------------------------+ +| callSiteTableLength | (ULEB128) | Call Site Table length, used to find Action table | ++---------------------+-----------+------------------------------------------------+--+ +| Beginning of Call Site Table If the current ip lies within the | +| ... (start, length) range of one of these | +| call sites, there may be action needed. | +| +-------------+---------------------------------+------------------------------+ | +| | start | (encoded with callSiteEncoding) | offset relative to funcStart | | +| | length | (encoded with callSiteEncoding) | lenght of code fragment | | +| | landingPad | (encoded with callSiteEncoding) | offset relative to lpStart | | +| | actionEntry | (ULEB128) | Action Table Index 1-based | | +| | | | actionEntry == 0 -> cleanup | | +| +-------------+---------------------------------+------------------------------+ | +| ... | ++---------------------------------------------------------------------+------------+ +| Beginning of Action Table ttypeIndex == 0 : cleanup | +| ... ttypeIndex > 0 : catch | +| ttypeIndex < 0 : exception spec | +| +--------------+-----------+--------------------------------------+ | +| | ttypeIndex | (SLEB128) | Index into type_info Table (1-based) | | +| | actionOffset | (SLEB128) | Offset into next Action Table entry | | +| +--------------+-----------+--------------------------------------+ | +| ... | ++---------------------------------------------------------------------+-----------------+ +| type_info Table, but classInfoOffset does *not* point here! | +| +----------------+------------------------------------------------+-----------------+ | +| | Nth type_info* | Encoded with ttypeEncoding, 0 means catch(...) | ttypeIndex == N | | +| +----------------+------------------------------------------------+-----------------+ | +| ... | +| +----------------+------------------------------------------------+-----------------+ | +| | 1st type_info* | Encoded with ttypeEncoding, 0 means catch(...) | ttypeIndex == 1 | | +| +----------------+------------------------------------------------+-----------------+ | +| +---------------------------------------+-----------+------------------------------+ | +| | 1st ttypeIndex for 1st exception spec | (ULEB128) | classInfoOffset points here! | | +| | ... | (ULEB128) | | | +| | Mth ttypeIndex for 1st exception spec | (ULEB128) | | | +| | 0 | (ULEB128) | | | +| +---------------------------------------+------------------------------------------+ | +| ... | +| +---------------------------------------+------------------------------------------+ | +| | 0 | (ULEB128) | throw() | | +| +---------------------------------------+------------------------------------------+ | +| ... | +| +---------------------------------------+------------------------------------------+ | +| | 1st ttypeIndex for Nth exception spec | (ULEB128) | | | +| | ... | (ULEB128) | | | +| | Mth ttypeIndex for Nth exception spec | (ULEB128) | | | +| | 0 | (ULEB128) | | | +| +---------------------------------------+------------------------------------------+ | ++---------------------------------------------------------------------------------------+ + +Notes: + +* ttypeIndex in the Action Table, and in the exception spec table, is an index, + not a byte count, if positive. It is a negative index offset of + classInfoOffset and the sizeof entry depends on ttypeEncoding. + But if ttypeIndex is negative, it is a positive 1-based byte offset into the + type_info Table. + And if ttypeIndex is zero, it refers to a catch (...). + +* landingPad can be 0, this implies there is nothing to be done. + +* landingPad != 0 and actionEntry == 0 implies a cleanup needs to be done + @landingPad. + +* A cleanup can also be found under landingPad != 0 and actionEntry != 0 in + the Action Table with ttypeIndex == 0. +*/ + +namespace __cxxabiv1 +{ + +extern "C" +{ + +// private API + +// Heavily borrowed from llvm/examples/ExceptionDemo/ExceptionDemo.cpp + +// DWARF Constants +enum +{ + DW_EH_PE_absptr = 0x00, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + DW_EH_PE_indirect = 0x80, + DW_EH_PE_omit = 0xFF +}; + +/// Read a uleb128 encoded value and advance pointer +/// See Variable Length Data Appendix C in: +/// @link http://dwarfstd.org/Dwarf4.pdf @unlink +/// @param data reference variable holding memory pointer to decode from +/// @returns decoded value +static +uintptr_t +readULEB128(const uint8_t** data) +{ + uintptr_t result = 0; + uintptr_t shift = 0; + unsigned char byte; + const uint8_t *p = *data; + do + { + byte = *p++; + result |= static_cast<uintptr_t>(byte & 0x7F) << shift; + shift += 7; + } while (byte & 0x80); + *data = p; + return result; +} + +/// Read a sleb128 encoded value and advance pointer +/// See Variable Length Data Applendix C in: +/// @link http://dwarfstd.org/Dwarf4.pdf @unlink +/// @param data reference variable holding memory pointer to decode from +/// @returns decoded value +static +uintptr_t +readSLEB128(const uint8_t** data) +{ + uintptr_t result = 0; + uintptr_t shift = 0; + unsigned char byte; + const uint8_t *p = *data; + do + { + byte = *p++; + result |= static_cast<uintptr_t>(byte & 0x7F) << shift; + shift += 7; + } while (byte & 0x80); + *data = p; + if ((byte & 0x40) && (shift < (sizeof(result) << 3))) + result |= static_cast<uintptr_t>(~0) << shift; + return result; +} + +/// Read a pointer encoded value and advance pointer +/// See Variable Length Data in: +/// @link http://dwarfstd.org/Dwarf3.pdf @unlink +/// @param data reference variable holding memory pointer to decode from +/// @param encoding dwarf encoding type +/// @returns decoded value +static +uintptr_t +readEncodedPointer(const uint8_t** data, uint8_t encoding) +{ + uintptr_t result = 0; + if (encoding == DW_EH_PE_omit) + return result; + const uint8_t* p = *data; + // first get value + switch (encoding & 0x0F) + { + case DW_EH_PE_absptr: + result = *((uintptr_t*)p); + p += sizeof(uintptr_t); + break; + case DW_EH_PE_uleb128: + result = readULEB128(&p); + break; + case DW_EH_PE_sleb128: + result = readSLEB128(&p); + break; + case DW_EH_PE_udata2: + result = *((uint16_t*)p); + p += sizeof(uint16_t); + break; + case DW_EH_PE_udata4: + result = *((uint32_t*)p); + p += sizeof(uint32_t); + break; + case DW_EH_PE_udata8: + result = *((uint64_t*)p); + p += sizeof(uint64_t); + break; + case DW_EH_PE_sdata2: + result = *((int16_t*)p); + p += sizeof(int16_t); + break; + case DW_EH_PE_sdata4: + result = *((int32_t*)p); + p += sizeof(int32_t); + break; + case DW_EH_PE_sdata8: + result = *((int64_t*)p); + p += sizeof(int64_t); + break; + default: + // not supported + abort(); + break; + } + // then add relative offset + switch (encoding & 0x70) + { + case DW_EH_PE_absptr: + // do nothing + break; + case DW_EH_PE_pcrel: + if (result) + result += (uintptr_t)(*data); + break; + case DW_EH_PE_textrel: + case DW_EH_PE_datarel: + case DW_EH_PE_funcrel: + case DW_EH_PE_aligned: + default: + // not supported + abort(); + break; + } + // then apply indirection + if (result && (encoding & DW_EH_PE_indirect)) + result = *((uintptr_t*)result); + *data = p; + return result; +} + +static +void +call_terminate(bool native_exception, _Unwind_Exception* unwind_exception) +{ + __cxa_begin_catch(unwind_exception); + if (native_exception) + { + // Use the stored terminate_handler if possible + __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; + std::__terminate(exception_header->terminateHandler); + } + std::terminate(); +} + +static +const __shim_type_info* +get_shim_type_info(int64_t ttypeIndex, const uint8_t* classInfo, + uint8_t ttypeEncoding, bool native_exception, + _Unwind_Exception* unwind_exception) +{ + if (classInfo == 0) + { + // this should not happen. Indicates corrupted eh_table. + call_terminate(native_exception, unwind_exception); + } + switch (ttypeEncoding & 0x0F) + { + case DW_EH_PE_absptr: + ttypeIndex *= sizeof(void*); + break; + case DW_EH_PE_udata2: + case DW_EH_PE_sdata2: + ttypeIndex *= 2; + break; + case DW_EH_PE_udata4: + case DW_EH_PE_sdata4: + ttypeIndex *= 4; + break; + case DW_EH_PE_udata8: + case DW_EH_PE_sdata8: + ttypeIndex *= 8; + break; + default: + // this should not happen. Indicates corrupted eh_table. + call_terminate(native_exception, unwind_exception); + } + classInfo -= ttypeIndex; + return (const __shim_type_info*)readEncodedPointer(&classInfo, ttypeEncoding); +} + +/* + This is checking a thrown exception type, excpType, against a posibly empty + list of catchType's which make up an exception spec. + + An exception spec acts like a catch handler, but in reverse. This "catch + handler" will catch an excpType if and only if none of the catchType's in + the list will catch a excpType. If any catchType in the list can catch an + excpType, then this exception spec does not catch the excpType. +*/ +static +bool +exception_spec_can_catch(int64_t specIndex, const uint8_t* classInfo, + uint8_t ttypeEncoding, const __shim_type_info* excpType, + void* adjustedPtr, _Unwind_Exception* unwind_exception) +{ + if (classInfo == 0) + { + // this should not happen. Indicates corrupted eh_table. + call_terminate(false, unwind_exception); + } + // specIndex is negative of 1-based byte offset into classInfo; + specIndex = -specIndex; + --specIndex; + const uint8_t* temp = classInfo + specIndex; + // If any type in the spec list can catch excpType, return false, else return true + // adjustments to adjustedPtr are ignored. + while (true) + { + uint64_t ttypeIndex = readULEB128(&temp); + if (ttypeIndex == 0) + break; + const __shim_type_info* catchType = get_shim_type_info(ttypeIndex, + classInfo, + ttypeEncoding, + true, + unwind_exception); + void* tempPtr = adjustedPtr; + if (catchType->can_catch(excpType, tempPtr)) + return false; + } + return true; +} + +static +void* +get_thrown_object_ptr(_Unwind_Exception* unwind_exception) +{ + // Even for foreign exceptions, the exception object is *probably* at unwind_exception + 1 + // Regardless, this library is prohibited from touching a foreign exception + void* adjustedPtr = unwind_exception + 1; + if (unwind_exception->exception_class == kOurDependentExceptionClass) + adjustedPtr = ((__cxa_dependent_exception*)adjustedPtr - 1)->primaryException; + return adjustedPtr; +} + +/* + There are 3 types of scans needed: + + 1. Scan for handler with native or foreign exception. If handler found, + save state and return _URC_HANDLER_FOUND, else return _URC_CONTINUE_UNWIND. + May also report an error on invalid input. + May terminate for invalid exception table. + _UA_SEARCH_PHASE + + 2. Scan for handler with foreign exception. Must return _URC_HANDLER_FOUND, + or call terminate. + _UA_CLEANUP_PHASE && _UA_HANDLER_FRAME && !native_exception + + 3. Scan for cleanups. If a handler is found and this isn't forced unwind, + then terminate, otherwise ignore the handler and keep looking for cleanup. + If a cleanup is found, return _URC_HANDLER_FOUND, else return _URC_CONTINUE_UNWIND. + May also report an error on invalid input. + May terminate for invalid exception table. + _UA_CLEANUP_PHASE && !_UA_HANDLER_FRAME +*/ + +namespace +{ + +struct scan_results +{ + int64_t ttypeIndex; // > 0 catch handler, < 0 exception spec handler, == 0 a cleanup + const uint8_t* actionRecord; // Currently unused. Retained to ease future maintenance. + const uint8_t* languageSpecificData; // Needed only for __cxa_call_unexpected + uintptr_t landingPad; // null -> nothing found, else something found + void* adjustedPtr; // Used in cxa_exception.cpp + _Unwind_Reason_Code reason; // One of _URC_FATAL_PHASE1_ERROR, + // _URC_FATAL_PHASE2_ERROR, + // _URC_CONTINUE_UNWIND, + // _URC_HANDLER_FOUND +}; + +} // unnamed namespace + +static +void +scan_eh_tab(scan_results& results, _Unwind_Action actions, bool native_exception, + _Unwind_Exception* unwind_exception, _Unwind_Context* context) +{ + // Initialize results to found nothing but an error + results.ttypeIndex = 0; + results.actionRecord = 0; + results.languageSpecificData = 0; + results.landingPad = 0; + results.adjustedPtr = 0; + results.reason = _URC_FATAL_PHASE1_ERROR; + // Check for consistent actions + if (actions & _UA_SEARCH_PHASE) + { + // Do Phase 1 + if (actions & (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME | _UA_FORCE_UNWIND)) + { + // None of these flags should be set during Phase 1 + // Client error + results.reason = _URC_FATAL_PHASE1_ERROR; + return; + } + } + else if (actions & _UA_CLEANUP_PHASE) + { + if ((actions & _UA_HANDLER_FRAME) && (actions & _UA_FORCE_UNWIND)) + { + // _UA_HANDLER_FRAME should only be set if phase 1 found a handler. + // If _UA_FORCE_UNWIND is set, phase 1 shouldn't have happened. + // Client error + results.reason = _URC_FATAL_PHASE2_ERROR; + return; + } + } + else // Niether _UA_SEARCH_PHASE nor _UA_CLEANUP_PHASE is set + { + // One of these should be set. + // Client error + results.reason = _URC_FATAL_PHASE1_ERROR; + return; + } + // Start scan by getting exception table address + const uint8_t* lsda = (const uint8_t*)_Unwind_GetLanguageSpecificData(context); + if (lsda == 0) + { + // There is no exception table + results.reason = _URC_CONTINUE_UNWIND; + return; + } + results.languageSpecificData = lsda; + // Get the current instruction pointer and offset it before next + // instruction in the current frame which threw the exception. + uintptr_t ip = _Unwind_GetIP(context) - 1; + // Get beginning current frame's code (as defined by the + // emitted dwarf code) + uintptr_t funcStart = _Unwind_GetRegionStart(context); + uintptr_t ipOffset = ip - funcStart; + const uint8_t* classInfo = NULL; + // Note: See JITDwarfEmitter::EmitExceptionTable(...) for corresponding + // dwarf emission + // Parse LSDA header. + uint8_t lpStartEncoding = *lsda++; + const uint8_t* lpStart = (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding); + if (lpStart == 0) + lpStart = (const uint8_t*)funcStart; + uint8_t ttypeEncoding = *lsda++; + if (ttypeEncoding != DW_EH_PE_omit) + { + // Calculate type info locations in emitted dwarf code which + // were flagged by type info arguments to llvm.eh.selector + // intrinsic + uintptr_t classInfoOffset = readULEB128(&lsda); + classInfo = lsda + classInfoOffset; + } + // Walk call-site table looking for range that + // includes current PC. + uint8_t callSiteEncoding = *lsda++; + uint32_t callSiteTableLength = readULEB128(&lsda); + const uint8_t* callSiteTableStart = lsda; + const uint8_t* callSiteTableEnd = callSiteTableStart + callSiteTableLength; + const uint8_t* actionTableStart = callSiteTableEnd; + const uint8_t* callSitePtr = callSiteTableStart; + while (true) + { + // There is one entry per call site. + // The call sites are non-overlapping in [start, start+length) + // The call sites are ordered in increasing value of start + uintptr_t start = readEncodedPointer(&callSitePtr, callSiteEncoding); + uintptr_t length = readEncodedPointer(&callSitePtr, callSiteEncoding); + uintptr_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding); + uintptr_t actionEntry = readULEB128(&callSitePtr); + if ((start <= ipOffset) && (ipOffset < (start + length))) + { + // Found the call site containing ip. + if (landingPad == 0) + { + // No handler here + results.reason = _URC_CONTINUE_UNWIND; + return; + } + landingPad = (uintptr_t)lpStart + landingPad; + if (actionEntry == 0) + { + // Found a cleanup + // If this is a type 1 or type 2 search, there are no handlers + // If this is a type 3 search, you want to install the cleanup. + if ((actions & _UA_CLEANUP_PHASE) && !(actions & _UA_HANDLER_FRAME)) + { + results.ttypeIndex = 0; // Redundant but clarifying + results.landingPad = landingPad; + results.reason = _URC_HANDLER_FOUND; + return; + } + // No handler here + results.reason = _URC_CONTINUE_UNWIND; + return; + } + // Convert 1-based byte offset into + const uint8_t* action = actionTableStart + (actionEntry - 1); + // Scan action entries until you find a matching handler, cleanup, or the end of action list + while (true) + { + const uint8_t* actionRecord = action; + int64_t ttypeIndex = readSLEB128(&action); + if (ttypeIndex > 0) + { + // Found a catch, does it actually catch? + // First check for catch (...) + const __shim_type_info* catchType = + get_shim_type_info(ttypeIndex, classInfo, + ttypeEncoding, native_exception, + unwind_exception); + if (catchType == 0) + { + // Found catch (...) catches everything, including foreign exceptions + // If this is a type 1 search save state and return _URC_HANDLER_FOUND + // If this is a type 2 search save state and return _URC_HANDLER_FOUND + // If this is a type 3 search !_UA_FORCE_UNWIND, we should have found this in phase 1! + // If this is a type 3 search _UA_FORCE_UNWIND, ignore handler and continue scan + if ((actions & _UA_SEARCH_PHASE) || (actions & _UA_HANDLER_FRAME)) + { + // Save state and return _URC_HANDLER_FOUND + results.ttypeIndex = ttypeIndex; + results.actionRecord = actionRecord; + results.landingPad = landingPad; + results.adjustedPtr = get_thrown_object_ptr(unwind_exception); + results.reason = _URC_HANDLER_FOUND; + return; + } + else if (!(actions & _UA_FORCE_UNWIND)) + { + // It looks like the exception table has changed + // on us. Likely stack corruption! + call_terminate(native_exception, unwind_exception); + } + } + // Else this is a catch (T) clause and will never + // catch a foreign exception + else if (native_exception) + { + __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; + void* adjustedPtr = get_thrown_object_ptr(unwind_exception); + const __shim_type_info* excpType = + static_cast<const __shim_type_info*>(exception_header->exceptionType); + if (adjustedPtr == 0 || excpType == 0) + { + // Something very bad happened + call_terminate(native_exception, unwind_exception); + } + if (catchType->can_catch(excpType, adjustedPtr)) + { + // Found a matching handler + // If this is a type 1 search save state and return _URC_HANDLER_FOUND + // If this is a type 3 search and !_UA_FORCE_UNWIND, we should have found this in phase 1! + // If this is a type 3 search and _UA_FORCE_UNWIND, ignore handler and continue scan + if (actions & _UA_SEARCH_PHASE) + { + // Save state and return _URC_HANDLER_FOUND + results.ttypeIndex = ttypeIndex; + results.actionRecord = actionRecord; + results.landingPad = landingPad; + results.adjustedPtr = adjustedPtr; + results.reason = _URC_HANDLER_FOUND; + return; + } + else if (!(actions & _UA_FORCE_UNWIND)) + { + // It looks like the exception table has changed + // on us. Likely stack corruption! + call_terminate(native_exception, unwind_exception); + } + } + } + // Scan next action ... + } + else if (ttypeIndex < 0) + { + // Found an exception spec. If this is a foreign exception, + // it is always caught. + if (native_exception) + { + // Does the exception spec catch this native exception? + __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; + void* adjustedPtr = get_thrown_object_ptr(unwind_exception); + const __shim_type_info* excpType = + static_cast<const __shim_type_info*>(exception_header->exceptionType); + if (adjustedPtr == 0 || excpType == 0) + { + // Something very bad happened + call_terminate(native_exception, unwind_exception); + } + if (exception_spec_can_catch(ttypeIndex, classInfo, + ttypeEncoding, excpType, + adjustedPtr, unwind_exception)) + { + // native exception caught by exception spec + // If this is a type 1 search, save state and return _URC_HANDLER_FOUND + // If this is a type 3 search !_UA_FORCE_UNWIND, we should have found this in phase 1! + // If this is a type 3 search _UA_FORCE_UNWIND, ignore handler and continue scan + if (actions & _UA_SEARCH_PHASE) + { + // Save state and return _URC_HANDLER_FOUND + results.ttypeIndex = ttypeIndex; + results.actionRecord = actionRecord; + results.landingPad = landingPad; + results.adjustedPtr = adjustedPtr; + results.reason = _URC_HANDLER_FOUND; + return; + } + else if (!(actions & _UA_FORCE_UNWIND)) + { + // It looks like the exception table has changed + // on us. Likely stack corruption! + call_terminate(native_exception, unwind_exception); + } + } + } + else + { + // foreign exception caught by exception spec + // If this is a type 1 search, save state and return _URC_HANDLER_FOUND + // If this is a type 2 search, save state and return _URC_HANDLER_FOUND + // If this is a type 3 search !_UA_FORCE_UNWIND, we should have found this in phase 1! + // If this is a type 3 search _UA_FORCE_UNWIND, ignore handler and continue scan + if ((actions & _UA_SEARCH_PHASE) || (actions & _UA_HANDLER_FRAME)) + { + // Save state and return _URC_HANDLER_FOUND + results.ttypeIndex = ttypeIndex; + results.actionRecord = actionRecord; + results.landingPad = landingPad; + results.adjustedPtr = get_thrown_object_ptr(unwind_exception); + results.reason = _URC_HANDLER_FOUND; + return; + } + else if (!(actions & _UA_FORCE_UNWIND)) + { + // It looks like the exception table has changed + // on us. Likely stack corruption! + call_terminate(native_exception, unwind_exception); + } + } + // Scan next action ... + } + else // ttypeIndex == 0 + { + // Found a cleanup + // If this is a type 1 search, ignore it and continue scan + // If this is a type 2 search, ignore it and continue scan + // If this is a type 3 search, save state and return _URC_HANDLER_FOUND + if ((actions & _UA_CLEANUP_PHASE) && !(actions & _UA_HANDLER_FRAME)) + { + // Save state and return _URC_HANDLER_FOUND + results.ttypeIndex = ttypeIndex; + results.actionRecord = actionRecord; + results.landingPad = landingPad; + results.adjustedPtr = get_thrown_object_ptr(unwind_exception); + results.reason = _URC_HANDLER_FOUND; + return; + } + } + const uint8_t* temp = action; + int64_t actionOffset = readSLEB128(&temp); + if (actionOffset == 0) + { + // End of action list, no matching handler or cleanup found + results.reason = _URC_CONTINUE_UNWIND; + return; + } + // Go to next action + action += actionOffset; + } // there is no break out of this loop, only return + } + else if (ipOffset < start) + { + // There is no call site for this ip + // Something bad has happened. We should never get here. + // Possible stack corruption. + call_terminate(native_exception, unwind_exception); + } + } // there is no break out of this loop, only return +} + +// public API + +/* +The personality function branches on actions like so: + +_UA_SEARCH_PHASE + + If _UA_CLEANUP_PHASE or _UA_HANDLER_FRAME or _UA_FORCE_UNWIND there's + an error from above, return _URC_FATAL_PHASE1_ERROR. + + Scan for anything that could stop unwinding: + + 1. A catch clause that will catch this exception + (will never catch foreign). + 2. A catch (...) (will always catch foreign). + 3. An exception spec that will catch this exception + (will always catch foreign). + If a handler is found + If not foreign + Save state in header + return _URC_HANDLER_FOUND + Else a handler not found + return _URC_CONTINUE_UNWIND + +_UA_CLEANUP_PHASE + + If _UA_HANDLER_FRAME + If _UA_FORCE_UNWIND + How did this happen? return _URC_FATAL_PHASE2_ERROR + If foreign + Do _UA_SEARCH_PHASE to recover state + else + Recover state from header + Transfer control to landing pad. return _URC_INSTALL_CONTEXT + + Else + + This branch handles both normal C++ non-catching handlers (cleanups) + and forced unwinding. + Scan for anything that can not stop unwinding: + + 1. A cleanup. + + If a cleanup is found + transfer control to it. return _URC_INSTALL_CONTEXT + Else a cleanup is not found: return _URC_CONTINUE_UNWIND +*/ + +_Unwind_Reason_Code +__gxx_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClass, + _Unwind_Exception* unwind_exception, _Unwind_Context* context) +{ + if (version != 1 || unwind_exception == 0 || context == 0) + return _URC_FATAL_PHASE1_ERROR; + bool native_exception = (exceptionClass & get_vendor_and_language) == + (kOurExceptionClass & get_vendor_and_language); + scan_results results; + if (actions & _UA_SEARCH_PHASE) + { + // Phase 1 search: All we're looking for in phase 1 is a handler that + // halts unwinding + scan_eh_tab(results, actions, native_exception, unwind_exception, context); + if (results.reason == _URC_HANDLER_FOUND) + { + // Found one. Can we cache the results somewhere to optimize phase 2? + if (native_exception) + { + __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; + exception_header->handlerSwitchValue = static_cast<int>(results.ttypeIndex); + exception_header->actionRecord = results.actionRecord; + exception_header->languageSpecificData = results.languageSpecificData; + exception_header->catchTemp = reinterpret_cast<void*>(results.landingPad); + exception_header->adjustedPtr = results.adjustedPtr; + } + return _URC_HANDLER_FOUND; + } + // Did not find a catching-handler. Return the results of the scan + // (normally _URC_CONTINUE_UNWIND, but could have been _URC_FATAL_PHASE1_ERROR + // if we were called improperly). + return results.reason; + } + if (actions & _UA_CLEANUP_PHASE) + { + // Phase 2 search: + // Did we find a catching handler in phase 1? + if (actions & _UA_HANDLER_FRAME) + { + // Yes, phase 1 said we have a catching handler here. + // Did we cache the results of the scan? + if (native_exception) + { + // Yes, reload the results from the cache. + __cxa_exception* exception_header = (__cxa_exception*)(unwind_exception+1) - 1; + results.ttypeIndex = exception_header->handlerSwitchValue; + results.actionRecord = exception_header->actionRecord; + results.languageSpecificData = exception_header->languageSpecificData; + results.landingPad = reinterpret_cast<uintptr_t>(exception_header->catchTemp); + results.adjustedPtr = exception_header->adjustedPtr; + } + else + { + // No, do the scan again to reload the results. + scan_eh_tab(results, actions, native_exception, unwind_exception, context); + // Phase 1 told us we would find a handler. Now in Phase 2 we + // didn't find a handler. The eh table should not be changing! + if (results.reason != _URC_HANDLER_FOUND) + call_terminate(native_exception, unwind_exception); + } + // Jump to the handler + _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (uintptr_t)unwind_exception); + _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), results.ttypeIndex); + _Unwind_SetIP(context, results.landingPad); + return _URC_INSTALL_CONTEXT; + } + // Either we didn't do a phase 1 search (due to forced unwinding), or + // phase 1 reported no catching-handlers. + // Search for a (non-catching) cleanup + scan_eh_tab(results, actions, native_exception, unwind_exception, context); + if (results.reason == _URC_HANDLER_FOUND) + { + // Found a non-catching handler. Jump to it: + _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (uintptr_t)unwind_exception); + _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), results.ttypeIndex); + _Unwind_SetIP(context, results.landingPad); + return _URC_INSTALL_CONTEXT; + } + // Did not find a cleanup. Return the results of the scan + // (normally _URC_CONTINUE_UNWIND, but could have been _URC_FATAL_PHASE2_ERROR + // if we were called improperly). + return results.reason; + } + // We were called improperly: neither a phase 1 or phase 2 search + return _URC_FATAL_PHASE1_ERROR; +} + +__attribute__((noreturn)) +void +__cxa_call_unexpected(void* arg) +{ + _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(arg); + if (unwind_exception == 0) + call_terminate(false, unwind_exception); + __cxa_begin_catch(unwind_exception); + bool native_old_exception = + (unwind_exception->exception_class & get_vendor_and_language) == + (kOurExceptionClass & get_vendor_and_language); + std::unexpected_handler u_handler; + std::terminate_handler t_handler; + __cxa_exception* old_exception_header = 0; + int64_t ttypeIndex; + const uint8_t* lsda; + if (native_old_exception) + { + old_exception_header = (__cxa_exception*)(unwind_exception+1) - 1; + t_handler = old_exception_header->terminateHandler; + u_handler = old_exception_header->unexpectedHandler; + // If std::__unexpected(u_handler) rethrows the same exception, + // these values get overwritten by the rethrow. So save them now: + ttypeIndex = old_exception_header->handlerSwitchValue; + lsda = old_exception_header->languageSpecificData; + } + else + { + t_handler = std::get_terminate(); + u_handler = std::get_unexpected(); + } + try + { + std::__unexpected(u_handler); + } + catch (...) + { + // If the old exception is foreign, then all we can do is terminate. + // We have no way to recover the needed old exception spec. There's + // no way to pass that information here. And the personality routine + // can't call us directly and do anything but terminate() if we throw + // from here. + if (native_old_exception) + { + // Have: + // old_exception_header->languageSpecificData + // old_exception_header->actionRecord + // Need + // const uint8_t* classInfo + // uint8_t ttypeEncoding + uint8_t lpStartEncoding = *lsda++; + const uint8_t* lpStart = (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding); + uint8_t ttypeEncoding = *lsda++; + if (ttypeEncoding == DW_EH_PE_omit) + std::__terminate(t_handler); + uintptr_t classInfoOffset = readULEB128(&lsda); + const uint8_t* classInfo = lsda + classInfoOffset; + // Is this new exception catchable by the exception spec at ttypeIndex? + // The answer is obviously yes if the new and old exceptions are the same exception + // If no + // throw; + __cxa_eh_globals* globals = __cxa_get_globals_fast(); + __cxa_exception* new_exception_header = globals->caughtExceptions; + if (new_exception_header == 0) + // This shouldn't be able to happen! + std::__terminate(t_handler); + bool native_new_exception = + (new_exception_header->unwindHeader.exception_class & get_vendor_and_language) == + (kOurExceptionClass & get_vendor_and_language); + void* adjustedPtr; + if (native_new_exception && (new_exception_header != old_exception_header)) + { + const __shim_type_info* excpType = + static_cast<const __shim_type_info*>(new_exception_header->exceptionType); + adjustedPtr = + new_exception_header->unwindHeader.exception_class == kOurDependentExceptionClass ? + ((__cxa_dependent_exception*)new_exception_header)->primaryException : + new_exception_header + 1; + if (!exception_spec_can_catch(ttypeIndex, classInfo, ttypeEncoding, + excpType, adjustedPtr, unwind_exception)) + { + // We need to __cxa_end_catch, but for the old exception, + // not the new one. This is a little tricky ... + // Disguise new_exception_header as a rethrown exception, but + // don't actually rethrow it. This means you can temporarily + // end the catch clause enclosing new_exception_header without + // __cxa_end_catch destroying new_exception_header. + new_exception_header->handlerCount = -new_exception_header->handlerCount; + globals->uncaughtExceptions += 1; + // Call __cxa_end_catch for new_exception_header + __cxa_end_catch(); + // Call __cxa_end_catch for old_exception_header + __cxa_end_catch(); + // Renter this catch clause with new_exception_header + __cxa_begin_catch(&new_exception_header->unwindHeader); + // Rethrow new_exception_header + throw; + } + } + // Will a std::bad_exception be catchable by the exception spec at + // ttypeIndex? + // If no + // throw std::bad_exception(); + const __shim_type_info* excpType = + static_cast<const __shim_type_info*>(&typeid(std::bad_exception)); + std::bad_exception be; + adjustedPtr = &be; + if (!exception_spec_can_catch(ttypeIndex, classInfo, ttypeEncoding, + excpType, adjustedPtr, unwind_exception)) + { + // We need to __cxa_end_catch for both the old exception and the + // new exception. Technically we should do it in that order. + // But it is expedent to do it in the opposite order: + // Call __cxa_end_catch for new_exception_header + __cxa_end_catch(); + // Throw std::bad_exception will __cxa_end_catch for + // old_exception_header + throw be; + } + } + } + std::__terminate(t_handler); +} + +} // extern "C" + +} // __cxxabiv1 diff --git a/system/lib/libcxxabi/src/cxa_unexpected.cpp b/system/lib/libcxxabi/src/cxa_unexpected.cpp new file mode 100644 index 00000000..f6e6b6ab --- /dev/null +++ b/system/lib/libcxxabi/src/cxa_unexpected.cpp @@ -0,0 +1,27 @@ +//===------------------------- cxa_unexpected.cpp -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <exception> +#include <cxxabi.h> +#include "cxa_exception.hpp" + +namespace __cxxabiv1 +{ + +#pragma GCC visibility push(default) + +extern "C" +{ + +} + +#pragma GCC visibility pop + +} // namespace __cxxabiv1 + diff --git a/system/lib/libcxxabi/src/cxa_vector.cpp b/system/lib/libcxxabi/src/cxa_vector.cpp new file mode 100644 index 00000000..926c01dd --- /dev/null +++ b/system/lib/libcxxabi/src/cxa_vector.cpp @@ -0,0 +1,367 @@ +//===-------------------------- cxa_vector.cpp ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +// +// This file implements the "Array Construction and Destruction APIs" +// http://www.codesourcery.com/public/cxx-abi/abi.html#array-ctor +// +//===----------------------------------------------------------------------===// + +#include "cxxabi.h" + +#include <exception> // for std::terminate + +namespace __cxxabiv1 { + +#pragma mark --Helper routines and classes -- + +namespace { + inline static size_t __get_element_count ( void *p ) { + return static_cast <size_t *> (p)[-1]; + } + + inline static void __set_element_count ( void *p, size_t element_count ) { + static_cast <size_t *> (p)[-1] = element_count; + } + + +// A pair of classes to simplify exception handling and control flow. +// They get passed a block of memory in the constructor, and unless the +// 'release' method is called, they deallocate the memory in the destructor. +// Prefered usage is to allocate some memory, attach it to one of these objects, +// and then, when all the operations to set up the memory block have succeeded, +// call 'release'. If any of the setup operations fail, or an exception is +// thrown, then the block is automatically deallocated. +// +// The only difference between these two classes is the signature for the +// deallocation function (to match new2/new3 and delete2/delete3. + class st_heap_block2 { + public: + typedef void (*dealloc_f)(void *); + + st_heap_block2 ( dealloc_f dealloc, void *ptr ) + : dealloc_ ( dealloc ), ptr_ ( ptr ), enabled_ ( true ) {} + ~st_heap_block2 () { if ( enabled_ ) dealloc_ ( ptr_ ) ; } + void release () { enabled_ = false; } + + private: + dealloc_f dealloc_; + void *ptr_; + bool enabled_; + }; + + class st_heap_block3 { + public: + typedef void (*dealloc_f)(void *, size_t); + + st_heap_block3 ( dealloc_f dealloc, void *ptr, size_t size ) + : dealloc_ ( dealloc ), ptr_ ( ptr ), size_ ( size ), enabled_ ( true ) {} + ~st_heap_block3 () { if ( enabled_ ) dealloc_ ( ptr_, size_ ) ; } + void release () { enabled_ = false; } + + private: + dealloc_f dealloc_; + void *ptr_; + size_t size_; + bool enabled_; + }; + + class st_cxa_cleanup { + public: + typedef void (*destruct_f)(void *); + + st_cxa_cleanup ( void *ptr, size_t &idx, size_t element_size, destruct_f destructor ) + : ptr_ ( ptr ), idx_ ( idx ), element_size_ ( element_size ), + destructor_ ( destructor ), enabled_ ( true ) {} + ~st_cxa_cleanup () { + if ( enabled_ ) + __cxa_vec_cleanup ( ptr_, idx_, element_size_, destructor_ ); + } + + void release () { enabled_ = false; } + + private: + void *ptr_; + size_t &idx_; + size_t element_size_; + destruct_f destructor_; + bool enabled_; + }; + + class st_terminate { + public: + st_terminate ( bool enabled = true ) : enabled_ ( enabled ) {} + ~st_terminate () { if ( enabled_ ) std::terminate (); } + void release () { enabled_ = false; } + private: + bool enabled_ ; + }; +} + +#pragma mark --Externally visible routines-- + +extern "C" { + +// Equivalent to +// +// __cxa_vec_new2(element_count, element_size, padding_size, constructor, +// destructor, &::operator new[], &::operator delete[]) +void* __cxa_vec_new( + size_t element_count, size_t element_size, size_t padding_size, + void (*constructor)(void*), void (*destructor)(void*) ) { + + return __cxa_vec_new2 ( element_count, element_size, padding_size, + constructor, destructor, &::operator new [], &::operator delete [] ); +} + + + +// Given the number and size of elements for an array and the non-negative +// size of prefix padding for a cookie, allocate space (using alloc) for +// the array preceded by the specified padding, initialize the cookie if +// the padding is non-zero, and call the given constructor on each element. +// Return the address of the array proper, after the padding. +// +// If alloc throws an exception, rethrow the exception. If alloc returns +// NULL, return NULL. If the constructor throws an exception, call +// destructor for any already constructed elements, and rethrow the +// exception. If the destructor throws an exception, call std::terminate. +// +// The constructor may be NULL, in which case it must not be called. If the +// padding_size is zero, the destructor may be NULL; in that case it must +// not be called. +// +// Neither alloc nor dealloc may be NULL. +void* __cxa_vec_new2( + size_t element_count, size_t element_size, size_t padding_size, + void (*constructor)(void*), void (*destructor)(void*), + void* (*alloc)(size_t), void (*dealloc)(void*) ) { + + const size_t heap_size = element_count * element_size + padding_size; + char * const heap_block = static_cast<char *> ( alloc ( heap_size )); + char *vec_base = heap_block; + + if ( NULL != vec_base ) { + st_heap_block2 heap ( dealloc, heap_block ); + + // put the padding before the array elements + if ( 0 != padding_size ) { + vec_base += padding_size; + __set_element_count ( vec_base, element_count ); + } + + // Construct the elements + __cxa_vec_ctor ( vec_base, element_count, element_size, constructor, destructor ); + heap.release (); // We're good! + } + + return vec_base; +} + + +// Same as __cxa_vec_new2 except that the deallocation function takes both +// the object address and its size. +void* __cxa_vec_new3( + size_t element_count, size_t element_size, size_t padding_size, + void (*constructor)(void*), void (*destructor)(void*), + void* (*alloc)(size_t), void (*dealloc)(void*, size_t) ) { + + const size_t heap_size = element_count * element_size + padding_size; + char * const heap_block = static_cast<char *> ( alloc ( heap_size )); + char *vec_base = heap_block; + + if ( NULL != vec_base ) { + st_heap_block3 heap ( dealloc, heap_block, heap_size ); + + // put the padding before the array elements + if ( 0 != padding_size ) { + vec_base += padding_size; + __set_element_count ( vec_base, element_count ); + } + + // Construct the elements + __cxa_vec_ctor ( vec_base, element_count, element_size, constructor, destructor ); + heap.release (); // We're good! + } + + return vec_base; +} + + +// Given the (data) addresses of a destination and a source array, an +// element count and an element size, call the given copy constructor to +// copy each element from the source array to the destination array. The +// copy constructor's arguments are the destination address and source +// address, respectively. If an exception occurs, call the given destructor +// (if non-NULL) on each copied element and rethrow. If the destructor +// throws an exception, call terminate(). The constructor and or destructor +// pointers may be NULL. If either is NULL, no action is taken when it +// would have been called. + +void __cxa_vec_cctor( void* dest_array, void* src_array, + size_t element_count, size_t element_size, + void (*constructor) (void*, void*), void (*destructor)(void*) ) { + + if ( NULL != constructor ) { + size_t idx = 0; + char *src_ptr = static_cast<char *>(src_array); + char *dest_ptr = static_cast<char *>(dest_array); + st_cxa_cleanup cleanup ( dest_array, idx, element_size, destructor ); + + for ( idx = 0; idx < element_count; + ++idx, src_ptr += element_size, dest_ptr += element_size ) + constructor ( dest_ptr, src_ptr ); + cleanup.release (); // We're good! + } +} + + +// Given the (data) address of an array, not including any cookie padding, +// and the number and size of its elements, call the given constructor on +// each element. If the constructor throws an exception, call the given +// destructor for any already-constructed elements, and rethrow the +// exception. If the destructor throws an exception, call terminate(). The +// constructor and/or destructor pointers may be NULL. If either is NULL, +// no action is taken when it would have been called. +void __cxa_vec_ctor( + void* array_address, size_t element_count, size_t element_size, + void (*constructor)(void*), void (*destructor)(void*) ) { + + if ( NULL != constructor ) { + size_t idx; + char *ptr = static_cast <char *> ( array_address ); + st_cxa_cleanup cleanup ( array_address, idx, element_size, destructor ); + + // Construct the elements + for ( idx = 0; idx < element_count; ++idx, ptr += element_size ) + constructor ( ptr ); + cleanup.release (); // We're good! + } +} + +// Given the (data) address of an array, the number of elements, and the +// size of its elements, call the given destructor on each element. If the +// destructor throws an exception, rethrow after destroying the remaining +// elements if possible. If the destructor throws a second exception, call +// terminate(). The destructor pointer may be NULL, in which case this +// routine does nothing. +void __cxa_vec_dtor( + void* array_address, size_t element_count, size_t element_size, + void (*destructor)(void*) ) { + + if ( NULL != destructor ) { + char *ptr = static_cast <char *> (array_address); + size_t idx = element_count; + st_cxa_cleanup cleanup ( array_address, idx, element_size, destructor ); + { + st_terminate exception_guard (__cxa_uncaught_exception ()); + ptr += element_count * element_size; // one past the last element + + while ( idx-- > 0 ) { + ptr -= element_size; + destructor ( ptr ); + } + exception_guard.release (); // We're good ! + } + cleanup.release (); // We're still good! + } +} + +// Given the (data) address of an array, the number of elements, and the +// size of its elements, call the given destructor on each element. If the +// destructor throws an exception, call terminate(). The destructor pointer +// may be NULL, in which case this routine does nothing. +void __cxa_vec_cleanup( void* array_address, size_t element_count, + size_t element_size, void (*destructor)(void*) ) { + + if ( NULL != destructor ) { + char *ptr = static_cast <char *> (array_address); + size_t idx = element_count; + st_terminate exception_guard; + + ptr += element_count * element_size; // one past the last element + while ( idx-- > 0 ) { + ptr -= element_size; + destructor ( ptr ); + } + exception_guard.release (); // We're done! + } +} + + +// If the array_address is NULL, return immediately. Otherwise, given the +// (data) address of an array, the non-negative size of prefix padding for +// the cookie, and the size of its elements, call the given destructor on +// each element, using the cookie to determine the number of elements, and +// then delete the space by calling ::operator delete[](void *). If the +// destructor throws an exception, rethrow after (a) destroying the +// remaining elements, and (b) deallocating the storage. If the destructor +// throws a second exception, call terminate(). If padding_size is 0, the +// destructor pointer must be NULL. If the destructor pointer is NULL, no +// destructor call is to be made. +// +// The intent of this function is to permit an implementation to call this +// function when confronted with an expression of the form delete[] p in +// the source code, provided that the default deallocation function can be +// used. Therefore, the semantics of this function are consistent with +// those required by the standard. The requirement that the deallocation +// function be called even if the destructor throws an exception derives +// from the resolution to DR 353 to the C++ standard, which was adopted in +// April, 2003. +void __cxa_vec_delete( void* array_address, + size_t element_size, size_t padding_size, void (*destructor)(void*) ) { + + __cxa_vec_delete2 ( array_address, element_size, padding_size, + destructor, &::operator delete [] ); +} + + +// Same as __cxa_vec_delete, except that the given function is used for +// deallocation instead of the default delete function. If dealloc throws +// an exception, the result is undefined. The dealloc pointer may not be +// NULL. +void __cxa_vec_delete2( void* array_address, + size_t element_size, size_t padding_size, + void (*destructor)(void*), void (*dealloc)(void*) ) { + + if ( NULL != array_address ) { + char *vec_base = static_cast <char *> (array_address); + char *heap_block = vec_base - padding_size; + st_heap_block2 heap ( dealloc, heap_block ); + + if ( 0 != padding_size && NULL != destructor ) // call the destructors + __cxa_vec_dtor ( array_address, __get_element_count ( vec_base ), + element_size, destructor ); + } +} + + +// Same as __cxa_vec_delete, except that the given function is used for +// deallocation instead of the default delete function. The deallocation +// function takes both the object address and its size. If dealloc throws +// an exception, the result is undefined. The dealloc pointer may not be +// NULL. +void __cxa_vec_delete3( void* array_address, + size_t element_size, size_t padding_size, + void (*destructor)(void*), void (*dealloc) (void*, size_t)) { + + if ( NULL != array_address ) { + char *vec_base = static_cast <char *> (array_address); + char *heap_block = vec_base - padding_size; + const size_t element_count = padding_size ? __get_element_count ( vec_base ) : 0; + const size_t heap_block_size = element_size * element_count + padding_size; + st_heap_block3 heap ( dealloc, heap_block, heap_block_size ); + + if ( 0 != padding_size && NULL != destructor ) // call the destructors + __cxa_vec_dtor ( array_address, element_count, element_size, destructor ); + } +} + + +} // extern "C" + +} // abi diff --git a/system/lib/libcxxabi/src/cxa_virtual.cpp b/system/lib/libcxxabi/src/cxa_virtual.cpp new file mode 100644 index 00000000..437b6016 --- /dev/null +++ b/system/lib/libcxxabi/src/cxa_virtual.cpp @@ -0,0 +1,31 @@ +//===-------------------------- cxa_virtual.cpp ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "cxxabi.h" +#include "abort_message.h" + +namespace __cxxabiv1 +{ + +extern "C" +{ + +LIBCXXABI_NORETURN +void __cxa_pure_virtual(void) { + abort_message("Pure virtual function called!"); +} + +LIBCXXABI_NORETURN +void __cxa_deleted_virtual(void) { + abort_message("Deleted virtual function called!"); +} + +} // extern "C" + +} // abi diff --git a/system/lib/libcxxabi/src/exception.cpp b/system/lib/libcxxabi/src/exception.cpp new file mode 100644 index 00000000..c47a9b76 --- /dev/null +++ b/system/lib/libcxxabi/src/exception.cpp @@ -0,0 +1,41 @@ +//===---------------------------- exception.cpp ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <exception> + +#pragma GCC visibility push(default) + +namespace std +{ + +// exception + +exception::~exception() _NOEXCEPT +{ +} + +const char* exception::what() const _NOEXCEPT +{ + return "std::exception"; +} + +// bad_exception + +bad_exception::~bad_exception() _NOEXCEPT +{ +} + +const char* bad_exception::what() const _NOEXCEPT +{ + return "std::bad_exception"; +} + +} // std + +#pragma GCC visibility pop diff --git a/system/lib/libcxxabi/src/fallback_malloc.ipp b/system/lib/libcxxabi/src/fallback_malloc.ipp new file mode 100644 index 00000000..979f0bbd --- /dev/null +++ b/system/lib/libcxxabi/src/fallback_malloc.ipp @@ -0,0 +1,174 @@ +//===------------------------ fallback_malloc.ipp -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +// +// This file implements the "Exception Handling APIs" +// http://www.codesourcery.com/public/cxx-abi/abi-eh.html +// +//===----------------------------------------------------------------------===// + +// A small, simple heap manager based (loosely) on +// the startup heap manager from FreeBSD, optimized for space. +// +// Manages a fixed-size memory pool, supports malloc and free only. +// No support for realloc. +// +// Allocates chunks in multiples of four bytes, with a four byte header +// for each chunk. The overhead of each chunk is kept low by keeping pointers +// as two byte offsets within the heap, rather than (4 or 8 byte) pointers. + +namespace { + +static pthread_mutex_t heap_mutex = PTHREAD_MUTEX_INITIALIZER; + +class mutexor { +public: + mutexor ( pthread_mutex_t *m ) : mtx_(m) { pthread_mutex_lock ( mtx_ ); } + ~mutexor () { pthread_mutex_unlock ( mtx_ ); } +private: + mutexor ( const mutexor &rhs ); + mutexor & operator = ( const mutexor &rhs ); + pthread_mutex_t *mtx_; + }; + + +#define HEAP_SIZE 512 +char heap [ HEAP_SIZE ]; + +typedef unsigned short heap_offset; +typedef unsigned short heap_size; + +struct heap_node { + heap_offset next_node; // offset into heap + heap_size len; // size in units of "sizeof(heap_node)" +}; + +static const heap_node *list_end = (heap_node *) ( &heap [ HEAP_SIZE ] ); // one past the end of the heap +static heap_node *freelist = NULL; + +heap_node *node_from_offset ( const heap_offset offset ) + { return (heap_node *) ( heap + ( offset * sizeof (heap_node))); } + +heap_offset offset_from_node ( const heap_node *ptr ) + { return (((char *) ptr ) - heap) / sizeof (heap_node); } + +void init_heap () { + freelist = (heap_node *) heap; + freelist->next_node = offset_from_node ( list_end ); + freelist->len = HEAP_SIZE / sizeof (heap_node); + } + +// How big a chunk we allocate +size_t alloc_size (size_t len) + { return (len + sizeof(heap_node) - 1) / sizeof(heap_node) + 1; } + +bool is_fallback_ptr ( void *ptr ) + { return ptr >= heap && ptr < ( heap + HEAP_SIZE ); } + +void *fallback_malloc(size_t len) { + heap_node *p, *prev; + const size_t nelems = alloc_size ( len ); + mutexor mtx ( &heap_mutex ); + + if ( NULL == freelist ) + init_heap (); + +// Walk the free list, looking for a "big enough" chunk + for (p = freelist, prev = 0; + p && p != list_end; prev = p, p = node_from_offset ( p->next_node)) { + + if (p->len > nelems) { // chunk is larger, shorten, and return the tail + heap_node *q; + + p->len -= nelems; + q = p + p->len; + q->next_node = 0; + q->len = nelems; + return (void *) (q + 1); + } + + if (p->len == nelems) { // exact size match + if (prev == 0) + freelist = node_from_offset(p->next_node); + else + prev->next_node = p->next_node; + p->next_node = 0; + return (void *) (p + 1); + } + } + return NULL; // couldn't find a spot big enough +} + +// Return the start of the next block +heap_node *after ( struct heap_node *p ) { return p + p->len; } + +void fallback_free (void *ptr) { + struct heap_node *cp = ((struct heap_node *) ptr) - 1; // retrieve the chunk + struct heap_node *p, *prev; + + mutexor mtx ( &heap_mutex ); + +#ifdef DEBUG_FALLBACK_MALLOC + std::cout << "Freeing item at " << offset_from_node ( cp ) << " of size " << cp->len << std::endl; +#endif + + for (p = freelist, prev = 0; + p && p != list_end; prev = p, p = node_from_offset (p->next_node)) { +#ifdef DEBUG_FALLBACK_MALLOC + std::cout << " p, cp, after (p), after(cp) " + << offset_from_node ( p ) << ' ' + << offset_from_node ( cp ) << ' ' + << offset_from_node ( after ( p )) << ' ' + << offset_from_node ( after ( cp )) << std::endl; +#endif + if ( after ( p ) == cp ) { +#ifdef DEBUG_FALLBACK_MALLOC + std::cout << " Appending onto chunk at " << offset_from_node ( p ) << std::endl; +#endif + p->len += cp->len; // make the free heap_node larger + return; + } + else if ( after ( cp ) == p ) { // there's a free heap_node right after +#ifdef DEBUG_FALLBACK_MALLOC + std::cout << " Appending free chunk at " << offset_from_node ( p ) << std::endl; +#endif + cp->len += p->len; + if ( prev == 0 ) { + freelist = cp; + cp->next_node = p->next_node; + } + else + prev->next_node = offset_from_node(cp); + return; + } + } +// Nothing to merge with, add it to the start of the free list +#ifdef DEBUG_FALLBACK_MALLOC + std::cout << " Making new free list entry " << offset_from_node ( cp ) << std::endl; +#endif + cp->next_node = offset_from_node ( freelist ); + freelist = cp; +} + +#ifdef INSTRUMENT_FALLBACK_MALLOC +size_t print_free_list () { + struct heap_node *p, *prev; + heap_size total_free = 0; + if ( NULL == freelist ) + init_heap (); + + for (p = freelist, prev = 0; + p && p != list_end; prev = p, p = node_from_offset (p->next_node)) { + std::cout << ( prev == 0 ? "" : " ") << "Offset: " << offset_from_node ( p ) + << "\tsize: " << p->len << " Next: " << p->next_node << std::endl; + total_free += p->len; + } + std::cout << "Total Free space: " << total_free << std::endl; + return total_free; + } +#endif +} // end unnamed namespace diff --git a/system/lib/libcxxabi/src/private_typeinfo.cpp b/system/lib/libcxxabi/src/private_typeinfo.cpp new file mode 100644 index 00000000..269116b6 --- /dev/null +++ b/system/lib/libcxxabi/src/private_typeinfo.cpp @@ -0,0 +1,1036 @@ +//===----------------------- private_typeinfo.cpp -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "private_typeinfo.h" + +namespace __cxxabiv1 +{ + +#pragma GCC visibility push(hidden) + +// __shim_type_info + +__shim_type_info::~__shim_type_info() +{ +} + +// __fundamental_type_info + +// This miraculously (compiler magic) emits the type_info's for: +// 1. all of the fundamental types +// 2. pointers to all of the fundamental types +// 3. pointers to all of the const fundamental types +__fundamental_type_info::~__fundamental_type_info() +{ +} + +// __array_type_info + +__array_type_info::~__array_type_info() +{ +} + +// __function_type_info + +__function_type_info::~__function_type_info() +{ +} + +// __enum_type_info + +__enum_type_info::~__enum_type_info() +{ +} + +// __class_type_info + +__class_type_info::~__class_type_info() +{ +} + +// __si_class_type_info + +__si_class_type_info::~__si_class_type_info() +{ +} + +// __vmi_class_type_info + +__vmi_class_type_info::~__vmi_class_type_info() +{ +} + +// __pbase_type_info + +__pbase_type_info::~__pbase_type_info() +{ +} + +// __pointer_type_info + +__pointer_type_info::~__pointer_type_info() +{ +} + +// __pointer_to_member_type_info + +__pointer_to_member_type_info::~__pointer_to_member_type_info() +{ +} + +// can_catch + +// A handler is a match for an exception object of type E if +// 1. The handler is of type cv T or cv T& and E and T are the same type +// (ignoring the top-level cv-qualifiers), or +// 2. the handler is of type cv T or cv T& and T is an unambiguous public +// base class of E, or +// 3. the handler is of type cv1 T* cv2 and E is a pointer type that can be +// converted to the type of the handler by either or both of +// A. a standard pointer conversion (4.10) not involving conversions to +// pointers to private or protected or ambiguous classes +// B. a qualification conversion +// 4. the handler is a pointer or pointer to member type and E is +// std::nullptr_t. + +// adjustedPtr: +// +// catch (A& a) : adjustedPtr == &a +// catch (A* a) : adjustedPtr == a +// catch (A** a) : adjustedPtr == a +// +// catch (D2& d2) : adjustedPtr == &d2 (d2 is base class of thrown object) +// catch (D2* d2) : adjustedPtr == d2 +// catch (D2*& d2) : adjustedPtr == d2 +// +// catch (...) : adjustedPtr == & of the exception + +// Handles bullet 1 +bool +__fundamental_type_info::can_catch(const __shim_type_info* thrown_type, + void*&) const +{ + return this == thrown_type; +} + +bool +__array_type_info::can_catch(const __shim_type_info* thrown_type, + void*&) const +{ + // We can get here if someone tries to catch an array by reference. + // However if someone tries to throw an array, it immediately gets + // converted to a pointer, which will not convert back to an array + // at the catch clause. So this can never catch anything. + return false; +} + +bool +__function_type_info::can_catch(const __shim_type_info* thrown_type, + void*&) const +{ + // We can get here if someone tries to catch a function by reference. + // However if someone tries to throw a function, it immediately gets + // converted to a pointer, which will not convert back to a function + // at the catch clause. So this can never catch anything. + return false; +} + +// Handles bullet 1 +bool +__enum_type_info::can_catch(const __shim_type_info* thrown_type, + void*&) const +{ + return this == thrown_type; +} + +// Handles bullets 1 and 2 +bool +__class_type_info::can_catch(const __shim_type_info* thrown_type, + void*& adjustedPtr) const +{ + // bullet 1 + if (this == thrown_type) + return true; + const __class_type_info* thrown_class_type = + dynamic_cast<const __class_type_info*>(thrown_type); + if (thrown_class_type == 0) + return false; + // bullet 2 + __dynamic_cast_info info = {thrown_class_type, 0, this, -1, 0}; + info.number_of_dst_type = 1; + thrown_class_type->has_unambiguous_public_base(&info, adjustedPtr, public_path); + if (info.path_dst_ptr_to_static_ptr == public_path) + { + adjustedPtr = const_cast<void*>(info.dst_ptr_leading_to_static_ptr); + return true; + } + return false; +} + +void +__class_type_info::process_found_base_class(__dynamic_cast_info* info, + void* adjustedPtr, + int path_below) const +{ + if (info->dst_ptr_leading_to_static_ptr == 0) + { + // First time here + info->dst_ptr_leading_to_static_ptr = adjustedPtr; + info->path_dst_ptr_to_static_ptr = path_below; + info->number_to_static_ptr = 1; + } + else if (info->dst_ptr_leading_to_static_ptr == adjustedPtr) + { + // We've been here before. Update path to "most public" + if (info->path_dst_ptr_to_static_ptr == not_public_path) + info->path_dst_ptr_to_static_ptr = path_below; + } + else + { + // We've detected an ambiguous cast from (thrown_class_type, adjustedPtr) + // to a static_type + info->number_to_static_ptr += 1; + info->path_dst_ptr_to_static_ptr = not_public_path; + info->search_done = true; + } +} + +void +__class_type_info::has_unambiguous_public_base(__dynamic_cast_info* info, + void* adjustedPtr, + int path_below) const +{ + if (this == info->static_type) + process_found_base_class(info, adjustedPtr, path_below); +} + +void +__si_class_type_info::has_unambiguous_public_base(__dynamic_cast_info* info, + void* adjustedPtr, + int path_below) const +{ + if (this == info->static_type) + process_found_base_class(info, adjustedPtr, path_below); + else + __base_type->has_unambiguous_public_base(info, adjustedPtr, path_below); +} + +void +__base_class_type_info::has_unambiguous_public_base(__dynamic_cast_info* info, + void* adjustedPtr, + int path_below) const +{ + ptrdiff_t offset_to_base = __offset_flags >> __offset_shift; + if (__offset_flags & __virtual_mask) + { + const char* vtable = *static_cast<const char*const*>(adjustedPtr); + offset_to_base = *reinterpret_cast<const ptrdiff_t*>(vtable + offset_to_base); + } + __base_type->has_unambiguous_public_base(info, + static_cast<char*>(adjustedPtr) + offset_to_base, + (__offset_flags & __public_mask) ? + path_below : + not_public_path); +} + +void +__vmi_class_type_info::has_unambiguous_public_base(__dynamic_cast_info* info, + void* adjustedPtr, + int path_below) const +{ + if (this == info->static_type) + process_found_base_class(info, adjustedPtr, path_below); + else + { + typedef const __base_class_type_info* Iter; + const Iter e = __base_info + __base_count; + Iter p = __base_info; + p->has_unambiguous_public_base(info, adjustedPtr, path_below); + if (++p < e) + { + do + { + p->has_unambiguous_public_base(info, adjustedPtr, path_below); + if (info->search_done) + break; + } while (++p < e); + } + } +} + +// Handles bullets 1 and 4 for both pointers and member pointers +bool +__pbase_type_info::can_catch(const __shim_type_info* thrown_type, + void*&) const +{ + if (this == thrown_type) + return true; + return thrown_type == &typeid(std::nullptr_t); +} + +// Handles bullets 1, 3 and 4 +bool +__pointer_type_info::can_catch(const __shim_type_info* thrown_type, + void*& adjustedPtr) const +{ + // Do the dereference adjustment + adjustedPtr = *static_cast<void**>(adjustedPtr); + // bullets 1 and 4 + if (__pbase_type_info::can_catch(thrown_type, adjustedPtr)) + return true; + // bullet 3 + const __pointer_type_info* thrown_pointer_type = + dynamic_cast<const __pointer_type_info*>(thrown_type); + if (thrown_pointer_type == 0) + return false; + // bullet 3B + if (thrown_pointer_type->__flags & ~__flags) + return false; + if (__pointee == thrown_pointer_type->__pointee) + return true; + // bullet 3A + if (__pointee == &typeid(void)) + return true; + const __class_type_info* catch_class_type = + dynamic_cast<const __class_type_info*>(__pointee); + if (catch_class_type == 0) + return false; + const __class_type_info* thrown_class_type = + dynamic_cast<const __class_type_info*>(thrown_pointer_type->__pointee); + if (thrown_class_type == 0) + return false; + __dynamic_cast_info info = {thrown_class_type, 0, catch_class_type, -1, 0}; + info.number_of_dst_type = 1; + thrown_class_type->has_unambiguous_public_base(&info, adjustedPtr, public_path); + if (info.path_dst_ptr_to_static_ptr == public_path) + { + adjustedPtr = const_cast<void*>(info.dst_ptr_leading_to_static_ptr); + return true; + } + return false; +} + +#pragma GCC visibility pop +#pragma GCC visibility push(default) + +// __dynamic_cast + +// static_ptr: pointer to an object of type static_type; nonnull, and since the +// object is polymorphic, *(void**)static_ptr is a virtual table pointer. +// static_ptr is &v in the expression dynamic_cast<T>(v). +// static_type: static type of the object pointed to by static_ptr. +// dst_type: destination type of the cast (the "T" in "dynamic_cast<T>(v)"). +// src2dst_offset: a static hint about the location of the +// source subobject with respect to the complete object; +// special negative values are: +// -1: no hint +// -2: static_type is not a public base of dst_type +// -3: static_type is a multiple public base type but never a +// virtual base type +// otherwise, the static_type type is a unique public nonvirtual +// base type of dst_type at offset src2dst_offset from the +// origin of dst_type. +// +// (dynamic_ptr, dynamic_type) are the run time type of the complete object +// referred to by static_ptr and a pointer to it. These can be found from +// static_ptr for polymorphic types. +// static_type is guaranteed to be a polymorphic type. +// +// (dynamic_ptr, dynamic_type) is the root of a DAG that grows upward. Each +// node of the tree represents a base class/object of its parent (or parents) below. +// Each node is uniquely represented by a pointer to the object, and a pointer +// to a type_info - its type. Different nodes may have the same pointer and +// different nodes may have the same type. But only one node has a specific +// (pointer-value, type) pair. In C++ two objects of the same type can not +// share the same address. +// +// There are two flavors of nodes which have the type dst_type: +// 1. Those that are derived from (below) (static_ptr, static_type). +// 2. Those that are not derived from (below) (static_ptr, static_type). +// +// Invariants of the DAG: +// +// There is at least one path from the root (dynamic_ptr, dynamic_type) to +// the node (static_ptr, static_type). This path may or may not be public. +// There may be more than one such path (some public some not). Such a path may +// or may not go through a node having type dst_type. +// +// No node of type T appears above a node of the same type. That means that +// there is only one node with dynamic_type. And if dynamic_type == dst_type, +// then there is only one dst_type in the DAG. +// +// No node of type dst_type appears above a node of type static_type. Such +// DAG's are possible in C++, but the compiler computes those dynamic_casts at +// compile time, and only calls __dynamic_cast when dst_type lies below +// static_type in the DAG. +// +// dst_type != static_type: The compiler computes the dynamic_cast in this case too. +// dynamic_type != static_type: The compiler computes the dynamic_cast in this case too. +// +// Returns: +// +// If there is exactly one dst_type of flavor 1, and +// If there is a public path from that dst_type to (static_ptr, static_type), or +// If there are 0 dst_types of flavor 2, and there is a public path from +// (dynamic_ptr, dynamic_type) to (static_ptr, static_type) and a public +// path from (dynamic_ptr, dynamic_type) to the one dst_type, then return +// a pointer to that dst_type. +// Else if there are 0 dst_types of flavor 1 and exactly 1 dst_type of flavor 2, and +// if there is a public path from (dynamic_ptr, dynamic_type) to +// (static_ptr, static_type) and a public path from (dynamic_ptr, dynamic_type) +// to the one dst_type, then return a pointer to that one dst_type. +// Else return nullptr. +// +// If dynamic_type == dst_type, then the above algorithm collapses to the +// following cheaper algorithm: +// +// If there is a public path from (dynamic_ptr, dynamic_type) to +// (static_ptr, static_type), then return dynamic_ptr. +// Else return nullptr. +extern "C" +void* +__dynamic_cast(const void* static_ptr, + const __class_type_info* static_type, + const __class_type_info* dst_type, + std::ptrdiff_t src2dst_offset) +{ + // Possible future optimization: Take advantage of src2dst_offset + // Currently clang always sets src2dst_offset to -1 (no hint). + + // Get (dynamic_ptr, dynamic_type) from static_ptr + void** vtable = *(void***)static_ptr; + ptrdiff_t offset_to_derived = reinterpret_cast<ptrdiff_t>(vtable[-2]); + const void* dynamic_ptr = static_cast<const char*>(static_ptr) + offset_to_derived; + const __class_type_info* dynamic_type = static_cast<const __class_type_info*>(vtable[-1]); + + // Initialize answer to nullptr. This will be changed from the search + // results if a non-null answer is found. Regardless, this is what will + // be returned. + const void* dst_ptr = 0; + // Initialize info struct for this search. + __dynamic_cast_info info = {dst_type, static_ptr, static_type, src2dst_offset, 0}; + + // Find out if we can use a giant short cut in the search + if (dynamic_type == dst_type) + { + // Using giant short cut. Add that information to info. + info.number_of_dst_type = 1; + // Do the search + dynamic_type->search_above_dst(&info, dynamic_ptr, dynamic_ptr, public_path); + // Query the search. + if (info.path_dst_ptr_to_static_ptr == public_path) + dst_ptr = dynamic_ptr; + } + else + { + // Not using giant short cut. Do the search + dynamic_type->search_below_dst(&info, dynamic_ptr, public_path); + // Query the search. + switch (info.number_to_static_ptr) + { + case 0: + if (info.number_to_dst_ptr == 1 && + info.path_dynamic_ptr_to_static_ptr == public_path && + info.path_dynamic_ptr_to_dst_ptr == public_path) + dst_ptr = info.dst_ptr_not_leading_to_static_ptr; + break; + case 1: + if (info.path_dst_ptr_to_static_ptr == public_path || + ( + info.number_to_dst_ptr == 0 && + info.path_dynamic_ptr_to_static_ptr == public_path && + info.path_dynamic_ptr_to_dst_ptr == public_path + ) + ) + dst_ptr = info.dst_ptr_leading_to_static_ptr; + break; + } + } + return const_cast<void*>(dst_ptr); +} + +#pragma GCC visibility pop +#pragma GCC visibility push(hidden) + +// Call this function when you hit a static_type which is a base (above) a dst_type. +// Let caller know you hit a static_type. But only start recording details if +// this is (static_ptr, static_type) -- the node we are casting from. +// If this is (static_ptr, static_type) +// Record the path (public or not) from the dst_type to here. There may be +// multiple paths from the same dst_type to here, record the "most public" one. +// Record the dst_ptr as pointing to (static_ptr, static_type). +// If more than one (dst_ptr, dst_type) points to (static_ptr, static_type), +// then mark this dyanmic_cast as ambiguous and stop the search. +void +__class_type_info::process_static_type_above_dst(__dynamic_cast_info* info, + const void* dst_ptr, + const void* current_ptr, + int path_below) const +{ + // Record that we found a static_type + info->found_any_static_type = true; + if (current_ptr == info->static_ptr) + { + // Record that we found (static_ptr, static_type) + info->found_our_static_ptr = true; + if (info->dst_ptr_leading_to_static_ptr == 0) + { + // First time here + info->dst_ptr_leading_to_static_ptr = dst_ptr; + info->path_dst_ptr_to_static_ptr = path_below; + info->number_to_static_ptr = 1; + // If there is only one dst_type in the entire tree and the path from + // there to here is public then we are done! + if (info->number_of_dst_type == 1 && info->path_dst_ptr_to_static_ptr == public_path) + info->search_done = true; + } + else if (info->dst_ptr_leading_to_static_ptr == dst_ptr) + { + // We've been here before. Update path to "most public" + if (info->path_dst_ptr_to_static_ptr == not_public_path) + info->path_dst_ptr_to_static_ptr = path_below; + // If there is only one dst_type in the entire tree and the path from + // there to here is public then we are done! + if (info->number_of_dst_type == 1 && info->path_dst_ptr_to_static_ptr == public_path) + info->search_done = true; + } + else + { + // We've detected an ambiguous cast from (static_ptr, static_type) + // to a dst_type + info->number_to_static_ptr += 1; + info->search_done = true; + } + } +} + +// Call this function when you hit a static_type which is not a base (above) a dst_type. +// If this is (static_ptr, static_type) +// Record the path (public or not) from (dynamic_ptr, dynamic_type) to here. There may be +// multiple paths from (dynamic_ptr, dynamic_type) to here, record the "most public" one. +void +__class_type_info::process_static_type_below_dst(__dynamic_cast_info* info, + const void* current_ptr, + int path_below) const +{ + if (current_ptr == info->static_ptr) + { + // Record the most public path from (dynamic_ptr, dynamic_type) to + // (static_ptr, static_type) + if (info->path_dynamic_ptr_to_static_ptr != public_path) + info->path_dynamic_ptr_to_static_ptr = path_below; + } +} + +// Call this function when searching below a dst_type node. This function searches +// for a path to (static_ptr, static_type) and for paths to one or more dst_type nodes. +// If it finds a static_type node, there is no need to further search base classes +// above. +// If it finds a dst_type node it should search base classes using search_above_dst +// to find out if this dst_type points to (static_ptr, static_type) or not. +// Either way, the dst_type is recorded as one of two "flavors": one that does +// or does not point to (static_ptr, static_type). +// If this is neither a static_type nor a dst_type node, continue searching +// base classes above. +// All the hoopla surrounding the search code is doing nothing but looking for +// excuses to stop the search prematurely (break out of the for-loop). That is, +// the algorithm below is simply an optimization of this: +// void +// __vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, +// const void* current_ptr, +// int path_below) const +// { +// typedef const __base_class_type_info* Iter; +// if (this == info->static_type) +// process_static_type_below_dst(info, current_ptr, path_below); +// else if (this == info->dst_type) +// { +// // Record the most public access path that got us here +// if (info->path_dynamic_ptr_to_dst_ptr != public_path) +// info->path_dynamic_ptr_to_dst_ptr = path_below; +// bool does_dst_type_point_to_our_static_type = false; +// for (Iter p = __base_info, e= __base_info + __base_count; p < e; ++p) +// { +// p->search_above_dst(info, current_ptr, current_ptr, public_path); +// if (info->found_our_static_ptr) +// does_dst_type_point_to_our_static_type = true; +// // break out early here if you can detect it doesn't matter if you do +// } +// if (!does_dst_type_point_to_our_static_type) +// { +// // We found a dst_type that doesn't point to (static_ptr, static_type) +// // So record the address of this dst_ptr and increment the +// // count of the number of such dst_types found in the tree. +// info->dst_ptr_not_leading_to_static_ptr = current_ptr; +// info->number_to_dst_ptr += 1; +// } +// } +// else +// { +// // This is not a static_type and not a dst_type. +// for (Iter p = __base_info, e = __base_info + __base_count; p < e; ++p) +// { +// p->search_below_dst(info, current_ptr, public_path); +// // break out early here if you can detect it doesn't matter if you do +// } +// } +// } +void +__vmi_class_type_info::search_below_dst(__dynamic_cast_info* info, + const void* current_ptr, + int path_below) const +{ + typedef const __base_class_type_info* Iter; + if (this == info->static_type) + process_static_type_below_dst(info, current_ptr, path_below); + else if (this == info->dst_type) + { + // We've been here before if we've recorded current_ptr in one of these + // two places: + if (current_ptr == info->dst_ptr_leading_to_static_ptr || + current_ptr == info->dst_ptr_not_leading_to_static_ptr) + { + // We've seen this node before, and therefore have already searched + // its base classes above. + // Update path to here that is "most public". + if (path_below == public_path) + info->path_dynamic_ptr_to_dst_ptr = public_path; + } + else // We have haven't been here before + { + // Record the access path that got us here + // If there is more than one dst_type this path doesn't matter. + info->path_dynamic_ptr_to_dst_ptr = path_below; + // Only search above here if dst_type derives from static_type, or + // if it is unknown if dst_type derives from static_type. + if (info->is_dst_type_derived_from_static_type != no) + { + // Set up flags to record results from all base classes + bool is_dst_type_derived_from_static_type = false; + bool does_dst_type_point_to_our_static_type = false; + // We've found a dst_type with a potentially public path to here. + // We have to assume the path is public because it may become + // public later (if we get back to here with a public path). + // We can stop looking above if: + // 1. We've found a public path to (static_ptr, static_type). + // 2. We've found an ambiguous cast from (static_ptr, static_type) to a dst_type. + // This is detected at the (static_ptr, static_type). + // 3. We can prove that there is no public path to (static_ptr, static_type) + // above here. + const Iter e = __base_info + __base_count; + for (Iter p = __base_info; p < e; ++p) + { + // Zero out found flags + info->found_our_static_ptr = false; + info->found_any_static_type = false; + p->search_above_dst(info, current_ptr, current_ptr, public_path); + if (info->search_done) + break; + if (info->found_any_static_type) + { + is_dst_type_derived_from_static_type = true; + if (info->found_our_static_ptr) + { + does_dst_type_point_to_our_static_type = true; + // If we found what we're looking for, stop looking above. + if (info->path_dst_ptr_to_static_ptr == public_path) + break; + // We found a private path to (static_ptr, static_type) + // If there is no diamond then there is only one path + // to (static_ptr, static_type) and we just found it. + if (!(__flags & __diamond_shaped_mask)) + break; + } + else + { + // If we found a static_type that isn't the one we're looking + // for, and if there are no repeated types above here, + // then stop looking. + if (!(__flags & __non_diamond_repeat_mask)) + break; + } + } + } + if (!does_dst_type_point_to_our_static_type) + { + // We found a dst_type that doesn't point to (static_ptr, static_type) + // So record the address of this dst_ptr and increment the + // count of the number of such dst_types found in the tree. + info->dst_ptr_not_leading_to_static_ptr = current_ptr; + info->number_to_dst_ptr += 1; + // If there exists another dst with a private path to + // (static_ptr, static_type), then the cast from + // (dynamic_ptr, dynamic_type) to dst_type is now ambiguous, + // so stop search. + if (info->number_to_static_ptr == 1 && + info->path_dst_ptr_to_static_ptr == not_public_path) + info->search_done = true; + } + // If we found no static_type,s then dst_type doesn't derive + // from static_type, else it does. Record this result so that + // next time we hit a dst_type we will know not to search above + // it if it doesn't derive from static_type. + if (is_dst_type_derived_from_static_type) + info->is_dst_type_derived_from_static_type = yes; + else + info->is_dst_type_derived_from_static_type = no; + } + } + } + else + { + // This is not a static_type and not a dst_type. + const Iter e = __base_info + __base_count; + Iter p = __base_info; + p->search_below_dst(info, current_ptr, path_below); + if (++p < e) + { + if ((__flags & __diamond_shaped_mask) || info->number_to_static_ptr == 1) + { + // If there are multiple paths to a base above from here, or if + // a dst_type pointing to (static_ptr, static_type) has been found, + // then there is no way to break out of this loop early unless + // something below detects the search is done. + do + { + if (info->search_done) + break; + p->search_below_dst(info, current_ptr, path_below); + } while (++p < e); + } + else if (__flags & __non_diamond_repeat_mask) + { + // There are not multiple paths to any base class from here and a + // dst_type pointing to (static_ptr, static_type) has not yet been + // found. + do + { + if (info->search_done) + break; + // If we just found a dst_type with a public path to (static_ptr, static_type), + // then the only reason to continue the search is to make sure + // no other dst_type points to (static_ptr, static_type). + // If !diamond, then we don't need to search here. + if (info->number_to_static_ptr == 1 && + info->path_dst_ptr_to_static_ptr == public_path) + break; + p->search_below_dst(info, current_ptr, path_below); + } while (++p < e); + } + else + { + // There are no repeated types above this node. + // There are no nodes with multiple parents above this node. + // no dst_type has been found to (static_ptr, static_type) + do + { + if (info->search_done) + break; + // If we just found a dst_type with a public path to (static_ptr, static_type), + // then the only reason to continue the search is to make sure sure + // no other dst_type points to (static_ptr, static_type). + // If !diamond, then we don't need to search here. + // if we just found a dst_type with a private path to (static_ptr, static_type), + // then we're only looking for a public path to (static_ptr, static_type) + // and to check for other dst_types. + // If !diamond & !repeat, then there is not a pointer to (static_ptr, static_type) + // and not a dst_type under here. + if (info->number_to_static_ptr == 1) + break; + p->search_below_dst(info, current_ptr, path_below); + } while (++p < e); + } + } + } +} + +// This is the same algorithm as __vmi_class_type_info::search_below_dst but +// simplified to the case that there is only a single base class. +void +__si_class_type_info::search_below_dst(__dynamic_cast_info* info, + const void* current_ptr, + int path_below) const +{ + if (this == info->static_type) + process_static_type_below_dst(info, current_ptr, path_below); + else if (this == info->dst_type) + { + // We've been here before if we've recorded current_ptr in one of these + // two places: + if (current_ptr == info->dst_ptr_leading_to_static_ptr || + current_ptr == info->dst_ptr_not_leading_to_static_ptr) + { + // We've seen this node before, and therefore have already searched + // its base classes above. + // Update path to here that is "most public". + if (path_below == public_path) + info->path_dynamic_ptr_to_dst_ptr = public_path; + } + else // We have haven't been here before + { + // Record the access path that got us here + // If there is more than one dst_type this path doesn't matter. + info->path_dynamic_ptr_to_dst_ptr = path_below; + // Only search above here if dst_type derives from static_type, or + // if it is unknown if dst_type derives from static_type. + if (info->is_dst_type_derived_from_static_type != no) + { + // Set up flags to record results from all base classes + bool is_dst_type_derived_from_static_type = false; + bool does_dst_type_point_to_our_static_type = false; + // Zero out found flags + info->found_our_static_ptr = false; + info->found_any_static_type = false; + __base_type->search_above_dst(info, current_ptr, current_ptr, public_path); + if (info->found_any_static_type) + { + is_dst_type_derived_from_static_type = true; + if (info->found_our_static_ptr) + does_dst_type_point_to_our_static_type = true; + } + if (!does_dst_type_point_to_our_static_type) + { + // We found a dst_type that doesn't point to (static_ptr, static_type) + // So record the address of this dst_ptr and increment the + // count of the number of such dst_types found in the tree. + info->dst_ptr_not_leading_to_static_ptr = current_ptr; + info->number_to_dst_ptr += 1; + // If there exists another dst with a private path to + // (static_ptr, static_type), then the cast from + // (dynamic_ptr, dynamic_type) to dst_type is now ambiguous. + if (info->number_to_static_ptr == 1 && + info->path_dst_ptr_to_static_ptr == not_public_path) + info->search_done = true; + } + // If we found no static_type,s then dst_type doesn't derive + // from static_type, else it does. Record this result so that + // next time we hit a dst_type we will know not to search above + // it if it doesn't derive from static_type. + if (is_dst_type_derived_from_static_type) + info->is_dst_type_derived_from_static_type = yes; + else + info->is_dst_type_derived_from_static_type = no; + } + } + } + else + { + // This is not a static_type and not a dst_type + __base_type->search_below_dst(info, current_ptr, path_below); + } +} + +// This is the same algorithm as __vmi_class_type_info::search_below_dst but +// simplified to the case that there is no base class. +void +__class_type_info::search_below_dst(__dynamic_cast_info* info, + const void* current_ptr, + int path_below) const +{ + typedef const __base_class_type_info* Iter; + if (this == info->static_type) + process_static_type_below_dst(info, current_ptr, path_below); + else if (this == info->dst_type) + { + // We've been here before if we've recorded current_ptr in one of these + // two places: + if (current_ptr == info->dst_ptr_leading_to_static_ptr || + current_ptr == info->dst_ptr_not_leading_to_static_ptr) + { + // We've seen this node before, and therefore have already searched + // its base classes above. + // Update path to here that is "most public". + if (path_below == public_path) + info->path_dynamic_ptr_to_dst_ptr = public_path; + } + else // We have haven't been here before + { + // Record the access path that got us here + // If there is more than one dst_type this path doesn't matter. + info->path_dynamic_ptr_to_dst_ptr = path_below; + // We found a dst_type that doesn't point to (static_ptr, static_type) + // So record the address of this dst_ptr and increment the + // count of the number of such dst_types found in the tree. + info->dst_ptr_not_leading_to_static_ptr = current_ptr; + info->number_to_dst_ptr += 1; + // If there exists another dst with a private path to + // (static_ptr, static_type), then the cast from + // (dynamic_ptr, dynamic_type) to dst_type is now ambiguous. + if (info->number_to_static_ptr == 1 && + info->path_dst_ptr_to_static_ptr == not_public_path) + info->search_done = true; + // We found that dst_type does not derive from static_type + info->is_dst_type_derived_from_static_type = no; + } + } +} + +// Call this function when searching above a dst_type node. This function searches +// for a public path to (static_ptr, static_type). +// This function is guaranteed not to find a node of type dst_type. +// Theoretically this is a very simple function which just stops if it finds a +// static_type node: All the hoopla surrounding the search code is doing +// nothing but looking for excuses to stop the search prematurely (break out of +// the for-loop). That is, the algorithm below is simply an optimization of this: +// void +// __vmi_class_type_info::search_above_dst(__dynamic_cast_info* info, +// const void* dst_ptr, +// const void* current_ptr, +// int path_below) const +// { +// if (this == info->static_type) +// process_static_type_above_dst(info, dst_ptr, current_ptr, path_below); +// else +// { +// typedef const __base_class_type_info* Iter; +// // This is not a static_type and not a dst_type +// for (Iter p = __base_info, e = __base_info + __base_count; p < e; ++p) +// { +// p->search_above_dst(info, dst_ptr, current_ptr, public_path); +// // break out early here if you can detect it doesn't matter if you do +// } +// } +// } +void +__vmi_class_type_info::search_above_dst(__dynamic_cast_info* info, + const void* dst_ptr, + const void* current_ptr, + int path_below) const +{ + if (this == info->static_type) + process_static_type_above_dst(info, dst_ptr, current_ptr, path_below); + else + { + typedef const __base_class_type_info* Iter; + // This is not a static_type and not a dst_type + // Save flags so they can be restored when returning to nodes below. + bool found_our_static_ptr = info->found_our_static_ptr; + bool found_any_static_type = info->found_any_static_type; + // We've found a dst_type below with a path to here. If the path + // to here is not public, there may be another path to here that + // is public. So we have to assume that the path to here is public. + // We can stop looking above if: + // 1. We've found a public path to (static_ptr, static_type). + // 2. We've found an ambiguous cast from (static_ptr, static_type) to a dst_type. + // This is detected at the (static_ptr, static_type). + // 3. We can prove that there is no public path to (static_ptr, static_type) + // above here. + const Iter e = __base_info + __base_count; + Iter p = __base_info; + // Zero out found flags + info->found_our_static_ptr = false; + info->found_any_static_type = false; + p->search_above_dst(info, dst_ptr, current_ptr, path_below); + if (++p < e) + { + do + { + if (info->search_done) + break; + if (info->found_our_static_ptr) + { + // If we found what we're looking for, stop looking above. + if (info->path_dst_ptr_to_static_ptr == public_path) + break; + // We found a private path to (static_ptr, static_type) + // If there is no diamond then there is only one path + // to (static_ptr, static_type) from here and we just found it. + if (!(__flags & __diamond_shaped_mask)) + break; + } + else if (info->found_any_static_type) + { + // If we found a static_type that isn't the one we're looking + // for, and if there are no repeated types above here, + // then stop looking. + if (!(__flags & __non_diamond_repeat_mask)) + break; + } + // Zero out found flags + info->found_our_static_ptr = false; + info->found_any_static_type = false; + p->search_above_dst(info, dst_ptr, current_ptr, path_below); + } while (++p < e); + } + // Restore flags + info->found_our_static_ptr = found_our_static_ptr; + info->found_any_static_type = found_any_static_type; + } +} + +// This is the same algorithm as __vmi_class_type_info::search_above_dst but +// simplified to the case that there is only a single base class. +void +__si_class_type_info::search_above_dst(__dynamic_cast_info* info, + const void* dst_ptr, + const void* current_ptr, + int path_below) const +{ + if (this == info->static_type) + process_static_type_above_dst(info, dst_ptr, current_ptr, path_below); + else + __base_type->search_above_dst(info, dst_ptr, current_ptr, path_below); +} + +// This is the same algorithm as __vmi_class_type_info::search_above_dst but +// simplified to the case that there is no base class. +void +__class_type_info::search_above_dst(__dynamic_cast_info* info, + const void* dst_ptr, + const void* current_ptr, + int path_below) const +{ + if (this == info->static_type) + process_static_type_above_dst(info, dst_ptr, current_ptr, path_below); +} + +// The search functions for __base_class_type_info are simply convenience +// functions for adjusting the current_ptr and path_below as the search is +// passed up to the base class node. + +void +__base_class_type_info::search_above_dst(__dynamic_cast_info* info, + const void* dst_ptr, + const void* current_ptr, + int path_below) const +{ + ptrdiff_t offset_to_base = __offset_flags >> __offset_shift; + if (__offset_flags & __virtual_mask) + { + const char* vtable = *static_cast<const char*const*>(current_ptr); + offset_to_base = *reinterpret_cast<const ptrdiff_t*>(vtable + offset_to_base); + } + __base_type->search_above_dst(info, dst_ptr, + static_cast<const char*>(current_ptr) + offset_to_base, + (__offset_flags & __public_mask) ? + path_below : + not_public_path); +} + +void +__base_class_type_info::search_below_dst(__dynamic_cast_info* info, + const void* current_ptr, + int path_below) const +{ + ptrdiff_t offset_to_base = __offset_flags >> __offset_shift; + if (__offset_flags & __virtual_mask) + { + const char* vtable = *static_cast<const char*const*>(current_ptr); + offset_to_base = *reinterpret_cast<const ptrdiff_t*>(vtable + offset_to_base); + } + __base_type->search_below_dst(info, + static_cast<const char*>(current_ptr) + offset_to_base, + (__offset_flags & __public_mask) ? + path_below : + not_public_path); +} + +#pragma GCC visibility pop + +} // __cxxabiv1 diff --git a/system/lib/libcxxabi/src/private_typeinfo.h b/system/lib/libcxxabi/src/private_typeinfo.h new file mode 100644 index 00000000..fec081ab --- /dev/null +++ b/system/lib/libcxxabi/src/private_typeinfo.h @@ -0,0 +1,246 @@ +//===------------------------ private_typeinfo.h --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __PRIVATE_TYPEINFO_H_ +#define __PRIVATE_TYPEINFO_H_ + +#include <typeinfo> +#include <cstddef> + +namespace __cxxabiv1 +{ + +#pragma GCC visibility push(hidden) + +class __attribute__ ((__visibility__("hidden"))) __shim_type_info + : public std::type_info +{ +public: + virtual ~__shim_type_info(); + + virtual bool can_catch(const __shim_type_info* thrown_type, void*& adjustedPtr) const = 0; +}; + +class __attribute__ ((__visibility__("default"))) __fundamental_type_info + : public __shim_type_info +{ +public: + __attribute__ ((__visibility__("hidden"))) virtual ~__fundamental_type_info(); + __attribute__ ((__visibility__("hidden"))) virtual bool can_catch(const __shim_type_info*, void*&) const; +}; + +class __attribute__ ((__visibility__("default"))) __array_type_info + : public __shim_type_info +{ +public: + __attribute__ ((__visibility__("hidden"))) virtual ~__array_type_info(); + __attribute__ ((__visibility__("hidden"))) virtual bool can_catch(const __shim_type_info*, void*&) const; +}; + +class __attribute__ ((__visibility__("default"))) __function_type_info + : public __shim_type_info +{ +public: + __attribute__ ((__visibility__("hidden"))) virtual ~__function_type_info(); + __attribute__ ((__visibility__("hidden"))) virtual bool can_catch(const __shim_type_info*, void*&) const; +}; + +class __attribute__ ((__visibility__("default"))) __enum_type_info + : public __shim_type_info +{ +public: + __attribute__ ((__visibility__("hidden"))) virtual ~__enum_type_info(); + __attribute__ ((__visibility__("hidden"))) virtual bool can_catch(const __shim_type_info*, void*&) const; +}; + +enum +{ + unknown = 0, + public_path, + not_public_path, + yes, + no +}; + +class __class_type_info; + +struct __dynamic_cast_info +{ +// const data supplied to the search: + + const __class_type_info* const dst_type; + const void* const static_ptr; + const __class_type_info* const static_type; + const std::ptrdiff_t src2dst_offset; + +// Data that represents the answer: + + // pointer to a dst_type which has (static_ptr, static_type) above it + const void* dst_ptr_leading_to_static_ptr; + // pointer to a dst_type which does not have (static_ptr, static_type) above it + const void* dst_ptr_not_leading_to_static_ptr; + + // The following three paths are either unknown, public_path or not_public_path. + // access of path from dst_ptr_leading_to_static_ptr to (static_ptr, static_type) + int path_dst_ptr_to_static_ptr; + // access of path from (dynamic_ptr, dynamic_type) to (static_ptr, static_type) + // when there is no dst_type along the path + int path_dynamic_ptr_to_static_ptr; + // access of path from (dynamic_ptr, dynamic_type) to dst_type + // (not used if there is a (static_ptr, static_type) above a dst_type). + int path_dynamic_ptr_to_dst_ptr; + + // Number of dst_types below (static_ptr, static_type) + int number_to_static_ptr; + // Number of dst_types not below (static_ptr, static_type) + int number_to_dst_ptr; + +// Data that helps stop the search before the entire tree is searched: + + // is_dst_type_derived_from_static_type is either unknown, yes or no. + int is_dst_type_derived_from_static_type; + // Number of dst_type in tree. If 0, then that means unknown. + int number_of_dst_type; + // communicates to a dst_type node that (static_ptr, static_type) was found + // above it. + bool found_our_static_ptr; + // communicates to a dst_type node that a static_type was found + // above it, but it wasn't (static_ptr, static_type) + bool found_any_static_type; + // Set whenever a search can be stopped + bool search_done; +}; + +// Has no base class +class __attribute__ ((__visibility__("default"))) __class_type_info + : public __shim_type_info +{ +public: + __attribute__ ((__visibility__("hidden"))) virtual ~__class_type_info(); + + __attribute__ ((__visibility__("hidden"))) + void process_static_type_above_dst(__dynamic_cast_info*, const void*, const void*, int) const; + __attribute__ ((__visibility__("hidden"))) + void process_static_type_below_dst(__dynamic_cast_info*, const void*, int) const; + __attribute__ ((__visibility__("hidden"))) + void process_found_base_class(__dynamic_cast_info*, void*, int) const; + __attribute__ ((__visibility__("hidden"))) + virtual void search_above_dst(__dynamic_cast_info*, const void*, const void*, int) const; + __attribute__ ((__visibility__("hidden"))) + virtual void search_below_dst(__dynamic_cast_info*, const void*, int) const; + __attribute__ ((__visibility__("hidden"))) + virtual bool can_catch(const __shim_type_info*, void*&) const; + __attribute__ ((__visibility__("hidden"))) + virtual void has_unambiguous_public_base(__dynamic_cast_info*, void*, int) const; +}; + +// Has one non-virtual public base class at offset zero +class __attribute__ ((__visibility__("default"))) __si_class_type_info + : public __class_type_info +{ +public: + const __class_type_info* __base_type; + + __attribute__ ((__visibility__("hidden"))) virtual ~__si_class_type_info(); + + __attribute__ ((__visibility__("hidden"))) + virtual void search_above_dst(__dynamic_cast_info*, const void*, const void*, int) const; + __attribute__ ((__visibility__("hidden"))) + virtual void search_below_dst(__dynamic_cast_info*, const void*, int) const; + __attribute__ ((__visibility__("hidden"))) + virtual void has_unambiguous_public_base(__dynamic_cast_info*, void*, int) const; +}; + +struct __base_class_type_info +{ +public: + const __class_type_info* __base_type; + long __offset_flags; + + enum __offset_flags_masks + { + __virtual_mask = 0x1, + __public_mask = 0x2, // base is public + __offset_shift = 8 + }; + + void search_above_dst(__dynamic_cast_info*, const void*, const void*, int) const; + void search_below_dst(__dynamic_cast_info*, const void*, int) const; + void has_unambiguous_public_base(__dynamic_cast_info*, void*, int) const; +}; + +// Has one or more base classes +class __attribute__ ((__visibility__("default"))) __vmi_class_type_info + : public __class_type_info +{ +public: + unsigned int __flags; + unsigned int __base_count; + __base_class_type_info __base_info[1]; + + enum __flags_masks + { + __non_diamond_repeat_mask = 0x1, // has two or more distinct base class + // objects of the same type + __diamond_shaped_mask = 0x2 // has base class object with two or + // more derived objects + }; + + __attribute__ ((__visibility__("hidden"))) virtual ~__vmi_class_type_info(); + + __attribute__ ((__visibility__("hidden"))) + virtual void search_above_dst(__dynamic_cast_info*, const void*, const void*, int) const; + __attribute__ ((__visibility__("hidden"))) + virtual void search_below_dst(__dynamic_cast_info*, const void*, int) const; + __attribute__ ((__visibility__("hidden"))) + virtual void has_unambiguous_public_base(__dynamic_cast_info*, void*, int) const; +}; + +class __attribute__ ((__visibility__("default"))) __pbase_type_info + : public __shim_type_info +{ +public: + unsigned int __flags; + const __shim_type_info* __pointee; + + enum __masks + { + __const_mask = 0x1, + __volatile_mask = 0x2, + __restrict_mask = 0x4, + __incomplete_mask = 0x8, + __incomplete_class_mask = 0x10 + }; + + __attribute__ ((__visibility__("hidden"))) virtual ~__pbase_type_info(); + __attribute__ ((__visibility__("hidden"))) virtual bool can_catch(const __shim_type_info*, void*&) const; +}; + +class __attribute__ ((__visibility__("default"))) __pointer_type_info + : public __pbase_type_info +{ +public: + __attribute__ ((__visibility__("hidden"))) virtual ~__pointer_type_info(); + __attribute__ ((__visibility__("hidden"))) virtual bool can_catch(const __shim_type_info*, void*&) const; +}; + +class __attribute__ ((__visibility__("default"))) __pointer_to_member_type_info + : public __pbase_type_info +{ +public: + const __class_type_info* __context; + + __attribute__ ((__visibility__("hidden"))) virtual ~__pointer_to_member_type_info(); +}; + +#pragma GCC visibility pop + +} // __cxxabiv1 + +#endif // __PRIVATE_TYPEINFO_H_ diff --git a/system/lib/libcxxabi/src/stdexcept.cpp b/system/lib/libcxxabi/src/stdexcept.cpp new file mode 100644 index 00000000..4aa962d0 --- /dev/null +++ b/system/lib/libcxxabi/src/stdexcept.cpp @@ -0,0 +1,122 @@ +//===------------------------ stdexcept.cpp -------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "stdexcept" +#include "new" +#include <cstdlib> +#include <cstring> +#include <cstdint> +#include <cstddef> + +// Note: optimize for size + +#pragma GCC visibility push(hidden) + +namespace +{ + +class __libcpp_nmstr +{ +private: + const char* str_; + + typedef std::size_t unused_t; + typedef std::int32_t count_t; + + static const std::ptrdiff_t offset = static_cast<std::ptrdiff_t>(2*sizeof(unused_t) + + sizeof(count_t)); + + count_t& count() const _NOEXCEPT {return (count_t&)(*(str_ - sizeof(count_t)));} +public: + explicit __libcpp_nmstr(const char* msg); + __libcpp_nmstr(const __libcpp_nmstr& s) _LIBCPP_CANTTHROW; + __libcpp_nmstr& operator=(const __libcpp_nmstr& s) _LIBCPP_CANTTHROW; + ~__libcpp_nmstr() _LIBCPP_CANTTHROW; + const char* c_str() const _NOEXCEPT {return str_;} +}; + +__libcpp_nmstr::__libcpp_nmstr(const char* msg) +{ + std::size_t len = strlen(msg); + str_ = new char[len + 1 + offset]; + unused_t* c = (unused_t*)str_; + c[0] = c[1] = len; + str_ += offset; + count() = 0; + std::strcpy(const_cast<char*>(c_str()), msg); +} + +inline +__libcpp_nmstr::__libcpp_nmstr(const __libcpp_nmstr& s) + : str_(s.str_) +{ + __sync_add_and_fetch(&count(), 1); +} + +__libcpp_nmstr& +__libcpp_nmstr::operator=(const __libcpp_nmstr& s) +{ + const char* p = str_; + str_ = s.str_; + __sync_add_and_fetch(&count(), 1); + if (__sync_add_and_fetch((count_t*)(p-sizeof(count_t)), -1) < 0) + delete [] (p-offset); + return *this; +} + +inline +__libcpp_nmstr::~__libcpp_nmstr() +{ + if (__sync_add_and_fetch(&count(), -1) < 0) + delete [] (str_ - offset); +} + +} + +#pragma GCC visibility pop + +namespace std // purposefully not using versioning namespace +{ + +logic_error::~logic_error() _NOEXCEPT +{ + __libcpp_nmstr& s = (__libcpp_nmstr&)__imp_; + s.~__libcpp_nmstr(); +} + +const char* +logic_error::what() const _NOEXCEPT +{ + __libcpp_nmstr& s = (__libcpp_nmstr&)__imp_; + return s.c_str(); +} + +runtime_error::~runtime_error() _NOEXCEPT +{ + __libcpp_nmstr& s = (__libcpp_nmstr&)__imp_; + s.~__libcpp_nmstr(); +} + +const char* +runtime_error::what() const _NOEXCEPT +{ + __libcpp_nmstr& s = (__libcpp_nmstr&)__imp_; + return s.c_str(); +} + +domain_error::~domain_error() _NOEXCEPT {} +invalid_argument::~invalid_argument() _NOEXCEPT {} +length_error::~length_error() _NOEXCEPT {} +out_of_range::~out_of_range() _NOEXCEPT {} + +range_error::~range_error() _NOEXCEPT {} +overflow_error::~overflow_error() _NOEXCEPT {} +underflow_error::~underflow_error() _NOEXCEPT {} + +} // std diff --git a/system/lib/libcxxabi/src/temporary.cpp b/system/lib/libcxxabi/src/temporary.cpp new file mode 100644 index 00000000..5facf39d --- /dev/null +++ b/system/lib/libcxxabi/src/temporary.cpp @@ -0,0 +1,41 @@ +//===---------------------------- temporary.cpp ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "abort_message.h" + +#pragma GCC visibility push(default) + +extern "C" +{ + +static +void f1() +{ + abort_message("__cxa_new_handler shouldn't be called"); +} + +static +void f2() +{ + abort_message("__cxa_terminate_handler shouldn't be called"); +} + +static +void f3() +{ + abort_message("__cxa_unexpected_handler shouldn't be called"); +} + +void (*__cxa_new_handler)() = f1; +void (*__cxa_terminate_handler)() = f2; +void (*__cxa_unexpected_handler)() = f3; + +} + +#pragma GCC visibility pop diff --git a/system/lib/libcxxabi/src/typeinfo.cpp b/system/lib/libcxxabi/src/typeinfo.cpp new file mode 100644 index 00000000..9313be04 --- /dev/null +++ b/system/lib/libcxxabi/src/typeinfo.cpp @@ -0,0 +1,53 @@ +//===----------------------------- typeinfo.cpp ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <typeinfo> + +namespace std +{ + +// type_info + +type_info::~type_info() +{ +} + +// bad_cast + +bad_cast::bad_cast() _NOEXCEPT +{ +} + +bad_cast::~bad_cast() _NOEXCEPT +{ +} + +const char* +bad_cast::what() const _NOEXCEPT +{ + return "std::bad_cast"; +} + +// bad_typeid + +bad_typeid::bad_typeid() _NOEXCEPT +{ +} + +bad_typeid::~bad_typeid() _NOEXCEPT +{ +} + +const char* +bad_typeid::what() const _NOEXCEPT +{ + return "std::bad_typeid"; +} + +} // std diff --git a/system/lib/libcxxabi/symbols b/system/lib/libcxxabi/symbols new file mode 100644 index 00000000..8ef205ba --- /dev/null +++ b/system/lib/libcxxabi/symbols @@ -0,0 +1,12 @@ + T __dynamic_cast + D _ZTVN10__cxxabiv116__enum_type_infoE + D _ZTVN10__cxxabiv116__shim_type_infoE + D _ZTVN10__cxxabiv117__array_type_infoE + D _ZTVN10__cxxabiv117__class_type_infoE + D _ZTVN10__cxxabiv117__pbase_type_infoE + D _ZTVN10__cxxabiv119__pointer_type_infoE + D _ZTVN10__cxxabiv120__function_type_infoE + D _ZTVN10__cxxabiv120__si_class_type_infoE + D _ZTVN10__cxxabiv121__vmi_class_type_infoE + D _ZTVN10__cxxabiv123__fundamental_type_infoE + D _ZTVN10__cxxabiv129__pointer_to_member_type_infoE diff --git a/tests/cases/allocavartop.ll b/tests/cases/allocavartop.ll new file mode 100644 index 00000000..e9520a19 --- /dev/null +++ b/tests/cases/allocavartop.ll @@ -0,0 +1,17 @@ +; ModuleID = 'tests/hello_world.bc' +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" +target triple = "i386-pc-linux-gnu" + +@.str = private unnamed_addr constant [15 x i8] c"hello, world!\0A\00", align 1 ; [#uses=1 type=[15 x i8]*] + +; [#uses=0] +define i32 @main(i32 %argc) { +entry: + %retval = alloca i32, i32 %argc, align 4 ; [#uses=1 type=i32*] + store i32 0, i32* %retval + %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([15 x i8]* @.str, i32 0, i32 0)) ; [#uses=0 type=i32] + ret i32 1 +} + +; [#uses=1] +declare i32 @printf(i8*, ...) diff --git a/tests/cases/legalizer_ta2.ll b/tests/cases/legalizer_ta2.ll index 67cd9feb..7e17c707 100644 --- a/tests/cases/legalizer_ta2.ll +++ b/tests/cases/legalizer_ta2.ll @@ -4,6 +4,13 @@ target triple = "i386-pc-linux-gnu" @globaliz = global [300 x i8] zeroinitializer +define i64 @retter(i64 %x) { + store i104 0, i104* bitcast ([300 x i8]* @globaliz to i104*), align 4 ; wipe it out + store i64 %x, i64* bitcast ([300 x i8]* @globaliz to i64*), align 4 + call i32 (i8*)* @puts(i8* bitcast ([300 x i8]* @globaliz to i8*)) + ret i64 7017280452245743464 +} + define i32 @main() { entry: %buffer = alloca i8, i32 1000, align 4 @@ -19,7 +26,7 @@ entry: %temp.buffer = bitcast i8* %buffer to [0 x i8]* %buffer1 = getelementptr [0 x i8]* %temp.buffer, i32 0, i32 1 %bundled1 = bitcast i8* %buffer1 to i104* - store i104 31079605376604435891501163880, i104* %bundled1, align 1 ; unaligned + store i104 -20251329998275065988055750122136, i104* %bundled1, align 1 ; unaligned call i32 (i8*)* @puts(i8* %buffer) ; shifts @@ -116,8 +123,69 @@ a26: store i88 %a27, i88* bitcast ([300 x i8]* @globaliz to i88*), align 4 call i32 (i8*)* @puts(i8* bitcast ([300 x i8]* @globaliz to i8*)) +; phi with constants + br i1 %if, label %a17b, label %a26b + +a17b: + br label %a26b + +a26b: + %a27b = phi i64 [ 55, %a26 ], [ 57, %a17b ] + store i104 0, i104* %bundled, align 4 ; wipe it out + store i64 %a27b, i64* bitcast ([300 x i8]* @globaliz to i64*), align 4 + call i32 (i8*)* @puts(i8* bitcast ([300 x i8]* @globaliz to i8*)) + + store i104 %ored, i104* %bundled, align 4 + %iff = zext i1 %if to i64 + switch i64 %iff, label %a50 [ + i64 1, label %a30 + i64 0, label %a40 + ] + +a50: + store i104 %xored, i104* %bundled, align 4 + br label %a40 + +a30: + store i104 %anded, i104* %bundled, align 4 + br label %a40 + +a40: + call i32 (i8*)* @puts(i8* %buffer) + +; invoke return value + + %inv64 = invoke i64 @retter(i64 8174723217654970232) + to label %a100 unwind label %a111 + +a100: + store i104 0, i104* bitcast ([300 x i8]* @globaliz to i104*), align 4 ; wipe it out + store i64 %inv64, i64* bitcast ([300 x i8]* @globaliz to i64*), align 4 + call i32 (i8*)* @puts(i8* bitcast ([300 x i8]* @globaliz to i8*)) + +; select + + %chosen = select i1 %if, i104 %loaded, i104 -1 + store i104 %chosen, i104* %bundled, align 4 + call i32 (i8*)* @puts(i8* %buffer) + + store i104 0, i104* bitcast ([300 x i8]* @globaliz to i104*), align 4 ; wipe it out + %s64a = trunc i104 %loaded to i64 + %s64b = trunc i104 %ander to i64 + %s64 = select i1 %if, i64 %s64a, i64 -1 + store i64 %s64, i64* bitcast ([300 x i8]* @globaliz to i64*), align 4 + call i32 (i8*)* @puts(i8* bitcast ([300 x i8]* @globaliz to i8*)) + br label %done + +a111: + %aaaa79 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) + cleanup + br label %done + +done: ret i32 1 } declare i32 @puts(i8*) +declare i32 @__gxx_personality_v0(...) diff --git a/tests/cases/legalizer_ta2.txt b/tests/cases/legalizer_ta2.txt index e25076e6..796ee240 100644 --- a/tests/cases/legalizer_ta2.txt +++ b/tests/cases/legalizer_ta2.txt @@ -16,3 +16,9 @@ hello, war`d hello, wor-d hello, wor hello, worl +9 +hello, war`d +xwvutsrq +hgfedcba +hello, world +hello, w diff --git a/tests/cases/longjmp_tiny.ll b/tests/cases/longjmp_tiny.ll new file mode 100644 index 00000000..0045847c --- /dev/null +++ b/tests/cases/longjmp_tiny.ll @@ -0,0 +1,65 @@ +; ModuleID = '/tmp/emscripten_temp/src.cpp.o' +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" +target triple = "i386-pc-linux-gnu" + +@_ZL3buf = internal global [20 x i16] zeroinitializer, align 2 +@.str = private unnamed_addr constant [13 x i8] c"hello world\0A\00", align 1 +@.str1 = private unnamed_addr constant [6 x i8] c"more\0A\00", align 1 + +define i32 @main() { + %retval = alloca i32, align 4 + store i32 0, i32* %retval + %call = call i32 @setjmp(i16* getelementptr inbounds ([20 x i16]* @_ZL3buf, i32 0, i32 0)) returns_twice, !dbg !20 + %tobool = icmp ne i32 %call, 0, !dbg !20 + br i1 %tobool, label %if.else, label %if.then, !dbg !20 + +if.then: ; preds = %entry + %call1 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([13 x i8]* @.str, i32 0, i32 0)), !dbg !22 + call void @longjmp(i16* getelementptr inbounds ([20 x i16]* @_ZL3buf, i32 0, i32 0), i32 10), !dbg !24 + br label %if.end, !dbg !25 + +if.else: ; preds = %entry + %call2 = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([6 x i8]* @.str1, i32 0, i32 0)), !dbg !26 + br label %if.end + +if.end: ; preds = %if.else, %if.then + ret i32 0, !dbg !28 +} + +declare i32 @setjmp(i16*) returns_twice + +declare i32 @printf(i8*, ...) + +declare void @longjmp(i16*, i32) + +!llvm.dbg.cu = !{!0} + +!0 = metadata !{i32 786449, i32 0, i32 4, metadata !"/tmp/emscripten_temp/src.cpp", metadata !"/home/alon/Dev/emscripten", metadata !"clang version 3.1 (trunk 150936)", i1 true, i1 false, metadata !"", i32 0, metadata !1, metadata !1, metadata !3, metadata !12} ; [ DW_TAG_compile_unit ] +!1 = metadata !{metadata !2} +!2 = metadata !{i32 0} +!3 = metadata !{metadata !4} +!4 = metadata !{metadata !5} +!5 = metadata !{i32 786478, i32 0, metadata !6, metadata !"main", metadata !"main", metadata !"", metadata !6, i32 7, metadata !7, i1 false, i1 true, i32 0, i32 0, null, i32 256, i1 false, i32 ()* @main, null, null, metadata !10} ; [ DW_TAG_subprogram ] +!6 = metadata !{i32 786473, metadata !"/tmp/emscripten_temp/src.cpp", metadata !"/home/alon/Dev/emscripten", null} ; [ DW_TAG_file_type ] +!7 = metadata !{i32 786453, i32 0, metadata !"", i32 0, i32 0, i64 0, i64 0, i64 0, i32 0, null, metadata !8, i32 0, i32 0} ; [ DW_TAG_subroutine_type ] +!8 = metadata !{metadata !9} +!9 = metadata !{i32 786468, null, metadata !"int", null, i32 0, i64 32, i64 32, i64 0, i32 0, i32 5} ; [ DW_TAG_base_type ] +!10 = metadata !{metadata !11} +!11 = metadata !{i32 786468} ; [ DW_TAG_base_type ] +!12 = metadata !{metadata !13} +!13 = metadata !{metadata !14} +!14 = metadata !{i32 786484, i32 0, null, metadata !"buf", metadata !"buf", metadata !"_ZL3buf", metadata !6, i32 5, metadata !15, i32 1, i32 1, [20 x i16]* @_ZL3buf} ; [ DW_TAG_variable ] +!15 = metadata !{i32 786454, null, metadata !"jmp_buf", metadata !6, i32 279, i64 0, i64 0, i64 0, i32 0, metadata !16} ; [ DW_TAG_typedef ] +!16 = metadata !{i32 786433, null, metadata !"", null, i32 0, i64 320, i64 16, i32 0, i32 0, metadata !17, metadata !18, i32 0, i32 0} ; [ DW_TAG_array_type ] +!17 = metadata !{i32 786468, null, metadata !"unsigned short", null, i32 0, i64 16, i64 16, i64 0, i32 0, i32 7} ; [ DW_TAG_base_type ] +!18 = metadata !{metadata !19} +!19 = metadata !{i32 786465, i64 0, i64 19} ; [ DW_TAG_subrange_type ] +!20 = metadata !{i32 8, i32 18, metadata !21, null} +!21 = metadata !{i32 786443, metadata !5, i32 7, i32 22, metadata !6, i32 0} ; [ DW_TAG_lexical_block ] +!22 = metadata !{i32 9, i32 15, metadata !23, null} +!23 = metadata !{i32 786443, metadata !21, i32 8, i32 31, metadata !6, i32 1} ; [ DW_TAG_lexical_block ] +!24 = metadata !{i32 10, i32 15, metadata !23, null} +!25 = metadata !{i32 11, i32 13, metadata !23, null} +!26 = metadata !{i32 12, i32 15, metadata !27, null} +!27 = metadata !{i32 786443, metadata !21, i32 11, i32 20, metadata !6, i32 2} ; [ DW_TAG_lexical_block ] +!28 = metadata !{i32 14, i32 13, metadata !21, null} diff --git a/tests/cases/longjmp_tiny.txt b/tests/cases/longjmp_tiny.txt new file mode 100644 index 00000000..8a0aa386 --- /dev/null +++ b/tests/cases/longjmp_tiny.txt @@ -0,0 +1,2 @@ +hello world +more diff --git a/tests/exceptions/output.txt b/tests/exceptions/output.txt index 2db8345e..718f189a 100644 --- a/tests/exceptions/output.txt +++ b/tests/exceptions/output.txt @@ -1,9 +1,12 @@ *CREATING A FOO *CREATING A BAR *CREATING A QUUX +*CREATING A QUUX +*CREATING A CHILD start +test 0 throwing ExFooInstance *COPYING A FOO *COPYING A FOO @@ -12,6 +15,7 @@ outer catch foo: 11 *DESTROYING A FOO (11) +test 1 throwing ExBarInstance *COPYING A BAR *COPYING A BAR @@ -21,6 +25,7 @@ outer catch bar-ref: 22 *DESTROYING A BAR (22) +test 2 throwing ExQuuxInstance *COPYING A QUUX *COPYING A QUUX @@ -29,22 +34,44 @@ inner catch quux: 33 *DESTROYING A QUUX (33) +test 3 + throwing ExQuux ptr +outer catch quux-ptr: 33 + + +test 4 + throwing ExChildInstance +*CREATING A QUUX +*COPYING CHILD +*COPYING A QUUX +inner catch quux: 44 +*DESTROYING A QUUX (44) +*DESTROYING A CHILD (44) +*DESTROYING A QUUX (44) +test 5 + throwing ExChildInstance ptr +outer catch quux-ptr: 44 +test 6 throwing 42 outer catch int: 42 +test 7 throwing NULL outer catch-all +test 8 not throwing end +*DESTROYING A CHILD (44) +*DESTROYING A QUUX (44) *DESTROYING A QUUX (33) *DESTROYING A BAR (22) *DESTROYING A FOO (11) diff --git a/tests/exceptions/typed.cpp b/tests/exceptions/typed.cpp index 29bf548a..a2b77fee 100644 --- a/tests/exceptions/typed.cpp +++ b/tests/exceptions/typed.cpp @@ -21,13 +21,12 @@ public: ExQuux(const ExQuux& other) { x=other.x; printf("*COPYING A QUUX\n"); } ~ExQuux() { printf("*DESTROYING A QUUX (%d)\n", x); } } ExQuuxInstance(33); -// NOTE: Throwing pointers and polymorphic matching not supported. -// class ExChild : public ExQuux { -// public: -// ExChild(int x) : ExQuux(x) { printf("*CREATING A CHILD\n"); } -// ExChild(const ExChild& other) : ExQuux(x) { x=other.x; printf("*COPYING CHILD\n"); } -// ~ExChild() { printf("*DESTROYING A CHILD (%d)\n", x); } -// } ExChildInstance(44); +class ExChild : public ExQuux { +public: + ExChild(int x) : ExQuux(x) { printf("*CREATING A CHILD\n"); } + ExChild(const ExChild& other) : ExQuux(x) { x=other.x; printf("*COPYING CHILD\n"); } + ~ExChild() { printf("*DESTROYING A CHILD (%d)\n", x); } +} ExChildInstance(44); void magic(int which) { try { @@ -41,20 +40,22 @@ void magic(int which) { case 2: printf(" throwing ExQuuxInstance\n"); throw ExQuuxInstance; -// NOTE: Throwing pointers and polymorphic matching not supported. -// case 3: -// printf(" throwing ExQuux ptr\n"); -// throw &ExQuuxInstance; -// case 4: -// printf(" throwing ExChildInstance\n"); -// throw ExChildInstance; + case 3: + printf(" throwing ExQuux ptr\n"); + throw &ExQuuxInstance; + case 4: + printf(" throwing ExChildInstance\n"); + throw ExChildInstance; case 5: + printf(" throwing ExChildInstance ptr\n"); + throw &ExChildInstance; + case 6: printf(" throwing 42\n"); throw 42; - case 6: + case 7: printf(" throwing NULL\n"); throw (void*)0; - case 7: + case 8: printf(" not throwing\n"); } } catch (ExQuux e1) { @@ -67,18 +68,18 @@ void magic(int which) { int main() { printf("start\n\n\n"); - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 9; i++) { + printf("test %d\n", i); try { magic(i); } catch (ExFoo e1) { printf("outer catch foo: %d\n", e1.x); } catch (ExBar& e2) { printf("outer catch bar-ref: %d\n", e2.x); -// NOTE: Throwing pointers and polymorphic matching not supported. -// } catch (ExQuux& e3) { -// printf("outer catch quux-ref: %d\n", e3.x); -// } catch (ExQuux* e4) { -// printf("outer catch quux-ptr: %d\n", e4->x); + } catch (ExQuux& e3) { + printf("outer catch quux-ref: %d\n", e3.x); + } catch (ExQuux* e4) { + printf("outer catch quux-ptr: %d\n", e4->x); } catch (int e5) { printf("outer catch int: %d\n", e5); } catch (...) { diff --git a/tests/hello_function.cpp b/tests/hello_function.cpp new file mode 100644 index 00000000..6037109e --- /dev/null +++ b/tests/hello_function.cpp @@ -0,0 +1,11 @@ + +#include <math.h> + +extern "C" { + +int int_sqrt(int x) { + return sqrt(x); +} + +} + diff --git a/tests/hello_world.js b/tests/hello_world.js index e6fcf070..01082eb4 100644 --- a/tests/hello_world.js +++ b/tests/hello_world.js @@ -8,6 +8,7 @@ var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIR if (ENVIRONMENT_IS_NODE) { // Expose functionality in the same simple way that the shells work + // Note that we pollute the global namespace here, otherwise we break in node print = function(x) { process['stdout'].write(x + '\n'); }; @@ -26,26 +27,30 @@ if (ENVIRONMENT_IS_NODE) { return ret; }; + load = function(f) { + globalEval(read(f)); + }; + arguments_ = process['argv'].slice(2); } else if (ENVIRONMENT_IS_SHELL) { // Polyfill over SpiderMonkey/V8 differences if (!this['read']) { - read = function(f) { snarf(f) }; + this['read'] = function(f) { snarf(f) }; } - if (!this['arguments']) { + if (typeof scriptArgs != 'undefined') { arguments_ = scriptArgs; - } else { + } else if (typeof arguments != 'undefined') { arguments_ = arguments; } } else if (ENVIRONMENT_IS_WEB) { - print = printErr = function(x) { + this['print'] = printErr = function(x) { console.log(x); }; - read = function(url) { + this['read'] = function(url) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); xhr.send(null); @@ -58,7 +63,7 @@ if (ENVIRONMENT_IS_NODE) { } else if (ENVIRONMENT_IS_WORKER) { // We can do very little here... - load = importScripts; + this['load'] = importScripts; } else { throw 'Unknown runtime environment. Where are we?'; @@ -69,17 +74,17 @@ function globalEval(x) { } if (typeof load == 'undefined' && typeof read != 'undefined') { - load = function(f) { + this['load'] = function(f) { globalEval(read(f)); }; } if (typeof printErr === 'undefined') { - printErr = function(){}; + this['printErr'] = function(){}; } if (typeof print === 'undefined') { - print = printErr; + this['print'] = printErr; } // *** Environment setup code *** diff --git a/tests/hello_world_sdl.cpp b/tests/hello_world_sdl.cpp index df69b055..35aec303 100644 --- a/tests/hello_world_sdl.cpp +++ b/tests/hello_world_sdl.cpp @@ -20,7 +20,8 @@ int main() { if (SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); SDL_Flip(screen); - printf("you should see a colored cube."); + printf("you should see a colored cube.\n"); + printf("and here is some text that should be HTML-friendly: amp: |&| double-quote: |\"| quote: |'| less-than, greater-than, html-like tags: |<cheez></cheez>|\nanother line.\n"); SDL_Quit(); diff --git a/tests/runner.py b/tests/runner.py index e2bb13d4..27100cbb 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -279,8 +279,6 @@ process(sys.argv[1]) sys.argv = map(lambda arg: arg if not arg.startswith('test_') else 'default.' + arg, sys.argv) -Cache.erase() # Wipe the cache, so that we always test populating it in the tests, benchmarks, etc. - if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): # Tests @@ -430,49 +428,38 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): def test_i64(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('i64 mode 1 requires ta2') - for i64_mode in [0,1]: - if i64_mode == 0 and Settings.USE_TYPED_ARRAYS != 0: continue # Typed arrays truncate i64 - if i64_mode == 1 and Settings.QUANTUM_SIZE == 1: continue # TODO: i64 mode 1 for q1 + src = ''' + #include <stdio.h> + int main() + { + long long a = 0x2b00505c10; + long long b = a >> 29; + long long c = a >> 32; + long long d = a >> 34; + printf("*%Ld,%Ld,%Ld,%Ld*\\n", a, b, c, d); + unsigned long long ua = 0x2b00505c10; + unsigned long long ub = ua >> 29; + unsigned long long uc = ua >> 32; + unsigned long long ud = ua >> 34; + printf("*%Ld,%Ld,%Ld,%Ld*\\n", ua, ub, uc, ud); + + long long x = 0x0000def123450789ULL; // any bigger than this, and we + long long y = 0x00020ef123456089ULL; // start to run into the double precision limit! + printf("*%Ld,%Ld,%Ld,%Ld,%Ld*\\n", x, y, x | y, x & y, x ^ y, x >> 2, y << 2); - Settings.I64_MODE = i64_mode - src = ''' - #include <stdio.h> - int main() - { - long long a = 0x2b00505c10; - long long b = a >> 29; - long long c = a >> 32; - long long d = a >> 34; - printf("*%Ld,%Ld,%Ld,%Ld*\\n", a, b, c, d); - unsigned long long ua = 0x2b00505c10; - unsigned long long ub = ua >> 29; - unsigned long long uc = ua >> 32; - unsigned long long ud = ua >> 34; - printf("*%Ld,%Ld,%Ld,%Ld*\\n", ua, ub, uc, ud); - - long long x = 0x0000def123450789ULL; // any bigger than this, and we - long long y = 0x00020ef123456089ULL; // start to run into the double precision limit! - printf("*%Ld,%Ld,%Ld,%Ld,%Ld*\\n", x, y, x | y, x & y, x ^ y, x >> 2, y << 2); - - printf("*"); - long long z = 13; - int n = 0; - while (z > 1) { - printf("%.2f,", (float)z); // these must be integers! - z = z >> 1; - n++; - } - printf("*%d*\\n", n); - return 0; + printf("*"); + long long z = 13; + int n = 0; + while (z > 1) { + printf("%.2f,", (float)z); // these must be integers! + z = z >> 1; + n++; } - ''' - self.do_run(src, '*184688860176,344,43,10*\n*184688860176,344,43,10*\n*245127260211081,579378795077769,808077213656969,16428841631881,791648372025088*\n*13.00,6.00,3.00,*3*') - - if Settings.QUANTUM_SIZE == 1: return self.skip('TODO: i64 mode 1 for q1') - - # Stuff that only works in i64_mode = 1 - - Settings.I64_MODE = 1 + printf("*%d*\\n", n); + return 0; + } + ''' + self.do_run(src, '*184688860176,344,43,10*\n*184688860176,344,43,10*\n*245127260211081,579378795077769,808077213656969,16428841631881,791648372025088*\n*13.00,6.00,3.00,*3*') src = r''' #include <time.h> @@ -653,6 +640,166 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): self.do_run(src, '*1*\n*0*\n*0*\n') + def test_i64_b(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + + src = r''' + #include <stdio.h> + #include <sys/time.h> + + typedef long long int64; + + #define PRMJ_USEC_PER_SEC 1000000L + + int main(int argc, char * argv[]) { + int64 sec = 1329409675 + argc; + int64 usec = 2329509675; + int64 mul = int64(sec) * PRMJ_USEC_PER_SEC; + int64 add = mul + int64(usec); + int add_low = add; + int add_high = add >> 32; + printf("*%lld,%lld,%u,%u*\n", mul, add, add_low, add_high); + return 0; + } + ''' + + self.do_run(src, '*1329409676000000,1329412005509675,3663280683,309527*\n') + + def test_i64_cmp(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + + src = r''' + #include <stdio.h> + + typedef long long int64; + + bool compare(int64 val) { + return val == -12; + } + + bool compare2(int64 val) { + return val < -12; + } + + int main(int argc, char * argv[]) { + printf("*%d,%d,%d,%d,%d,%d*\n", argc, compare(argc-1-12), compare(1000+argc), compare2(argc-1-10), compare2(argc-1-14), compare2(argc+1000)); + return 0; + } + ''' + + self.do_run(src, '*1,1,0,0,1,0*\n') + + def test_i64_cmp2(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + src = r''' + #include <inttypes.h> + #include <stdio.h> + + typedef int32_t INT32; + typedef int64_t INT64; + typedef uint8_t UINT8; + + void interface_clock_changed() + { + UINT8 m_divshift; + INT32 m_divisor; + + //INT64 attos = m_attoseconds_per_cycle; + INT64 attos = 279365114840; + m_divshift = 0; + while (attos >= (1UL << 31)) + { + m_divshift++; + printf("m_divshift is %i, on %Ld >?= %lu\n", m_divshift, attos, 1UL << 31); + attos >>= 1; + } + m_divisor = attos; + + printf("m_divisor is %i\n",m_divisor); + } + + int main() { + interface_clock_changed(); + return 0; + } + ''' + self.do_run(src, '''m_divshift is 1, on 279365114840 >?= 2147483648 +m_divshift is 2, on 139682557420 >?= 2147483648 +m_divshift is 3, on 69841278710 >?= 2147483648 +m_divshift is 4, on 34920639355 >?= 2147483648 +m_divshift is 5, on 17460319677 >?= 2147483648 +m_divshift is 6, on 8730159838 >?= 2147483648 +m_divshift is 7, on 4365079919 >?= 2147483648 +m_divshift is 8, on 2182539959 >?= 2147483648 +m_divisor is 1091269979 +''') + + def test_i64_double(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + src = r''' + #include <stdio.h> + + typedef long long int64; + #define JSDOUBLE_HI32_SIGNBIT 0x80000000 + + bool JSDOUBLE_IS_NEGZERO(double d) + { + union { + struct { + unsigned int lo, hi; + } s; + double d; + } x; + if (d != 0) + return false; + x.d = d; + return (x.s.hi & JSDOUBLE_HI32_SIGNBIT) != 0; + } + + bool JSINT64_IS_NEGZERO(int64 l) + { + union { + int64 i; + double d; + } x; + if (l != 0) + return false; + x.i = l; + return x.d == -0; + } + + int main(int argc, char * argv[]) { + printf("*%d,%d,%d,%d*\n", JSDOUBLE_IS_NEGZERO(0), JSDOUBLE_IS_NEGZERO(-0), JSDOUBLE_IS_NEGZERO(-1), JSDOUBLE_IS_NEGZERO(+1)); + printf("*%d,%d,%d,%d*\n", JSINT64_IS_NEGZERO(0), JSINT64_IS_NEGZERO(-0), JSINT64_IS_NEGZERO(-1), JSINT64_IS_NEGZERO(+1)); + return 0; + } + ''' + self.do_run(src, '*0,0,0,0*\n*1,1,0,0*\n') # same as gcc + + def test_i64_umul(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('full i64 stuff only in ta2') + src = r''' + #include <inttypes.h> + #include <stdio.h> + + typedef uint32_t UINT32; + typedef uint64_t UINT64; + + int main() { + volatile UINT32 testu32a = 2375724032U; + UINT32 bigu32 = 0xffffffffU; + volatile UINT64 testu64a = 14746250828952703000U; + + while ((UINT64)testu32a * (UINT64)bigu32 < testu64a) { + printf("testu64a is %llu\n", testu64a); + testu64a /= 2; + } + + return 0; + } + ''' + self.do_run(src, 'testu64a is 14746250828952703000\n') + def test_unaligned(self): if Settings.QUANTUM_SIZE == 1: return self.skip('No meaning to unaligned addresses in q1') @@ -1319,6 +1466,41 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): ''' self.do_run(src, 'Assertion failed: 1 == false') + def test_longjmp(self): + src = r''' + #include <stdio.h> + #include <setjmp.h> + + static jmp_buf buf; + + void second(void) { + printf("second\n"); // prints + longjmp(buf,1); // jumps back to where setjmp was called - making setjmp now return 1 + } + + void first(void) { + second(); + printf("first\n"); // does not print + } + + int main() { + int x = 0; + if ( ! setjmp(buf) ) { + x++; + first(); // when executed, setjmp returns 0 + } else { // when longjmp jumps back, setjmp returns 1 + printf("main: %d\n", x); // prints + } + + return 0; + } + ''' + # gcc -O0 and -O2 differ in what they do with the saved state of local vars - and we match that + if self.emcc_args is None or ('-O1' not in self.emcc_args and '-O2' not in self.emcc_args): + self.do_run(src, 'second\nmain: 1\n') + else: + self.do_run(src, 'second\nmain: 0\n') + def test_exceptions(self): if Settings.QUANTUM_SIZE == 1: return self.skip("we don't support libcxx in q1") @@ -1446,8 +1628,7 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): self.do_run(src, 'success') def test_typed_exceptions(self): - return self.skip('TODO: fix this for llvm 3.0') - + Settings.DISABLE_EXCEPTION_CATCHING = 0 Settings.SAFE_HEAP = 0 # Throwing null will cause an ignorable null pointer access. Settings.EXCEPTION_DEBUG = 0 # Messes up expected output. src = open(path_from_root('tests', 'exceptions', 'typed.cpp'), 'r').read() @@ -1549,6 +1730,8 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): self.do_run(src, '*11,74,32,1012*\n*11*\n*22*') def test_dynamic_cast(self): + if self.emcc_args is None: return self.skip('need libcxxabi') + src = r''' #include <stdio.h> @@ -2104,6 +2287,8 @@ def process(filename): self.do_run(src, '*nameA,nameB*') def test_llvmswitch(self): + Settings.CORRECT_SIGNS = 1 + src = ''' #include <stdio.h> #include <string.h> @@ -2115,23 +2300,22 @@ def process(filename): case 'b': case 'c': return p-1; - case 'd': + case 0xfffffff1: return p+1; } return p; } int main( int argc, const char *argv[] ) { - printf("*%d,%d,%d,%d,%d*\\n", switcher('a'), switcher('b'), switcher('c'), switcher('d'), switcher('e')); + unsigned int x = 0xfffffff1; + x >>= 0; // force it to be unsigned for purpose of checking our switch comparison in signed/unsigned + printf("*%d,%d,%d,%d,%d,%d*\\n", switcher('a'), switcher('b'), switcher('c'), switcher(x), switcher(-15), switcher('e')); return 0; } ''' - self.do_run(src, '*96,97,98,101,101*') + self.do_run(src, '*96,97,98,-14,-14,101*') def test_indirectbr(self): - if Settings.USE_TYPED_ARRAYS == 2: - Settings.I64_MODE = 1 # Unsafe optimizations use 64-bit load/store on two i32s - src = ''' #include <stdio.h> int main(void) { @@ -2439,6 +2623,27 @@ def process(filename): self.do_run(src, '*1*', force_c=True) + def test_atexit(self): + # Confirms they are called in reverse order + src = r''' + #include <stdio.h> + #include <stdlib.h> + + static void cleanA() { + printf("A"); + } + static void cleanB() { + printf("B"); + } + + int main() { + atexit(cleanA); + atexit(cleanB); + return 0; + } + ''' + self.do_run(src, 'BA') + def test_time(self): # XXX Not sure what the right output is here. Looks like the test started failing with daylight savings changes. Modified it to pass again. src = open(path_from_root('tests', 'time', 'src.c'), 'r').read() @@ -3292,15 +3497,12 @@ at function.:blag def test_parseInt(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('i64 mode 1 requires ta2') if Settings.QUANTUM_SIZE == 1: return self.skip('Q1 and I64_1 do not mix well yet') - Settings.I64_MODE = 1 # Necessary to prevent i64s being truncated into i32s, but we do still get doubling - # FIXME: The output here is wrong, due to double rounding of i64s! src = open(path_from_root('tests', 'parseInt', 'src.c'), 'r').read() expected = open(path_from_root('tests', 'parseInt', 'output.txt'), 'r').read() self.do_run(src, expected) def test_printf(self): if Settings.USE_TYPED_ARRAYS != 2: return self.skip('i64 mode 1 requires ta2') - Settings.I64_MODE = 1 self.banned_js_engines = [NODE_JS, V8_ENGINE] # SpiderMonkey and V8 do different things to float64 typed arrays, un-NaNing, etc. src = open(path_from_root('tests', 'printf', 'test.c'), 'r').read() expected = [open(path_from_root('tests', 'printf', 'output.txt'), 'r').read(), @@ -3493,6 +3695,37 @@ def process(filename): self.do_run(src, 'size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\ntexte\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\n', post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h']) + def test_files_m(self): + # Test for Module.stdin etc. + + Settings.CORRECT_SIGNS = 1 + + post = ''' +def process(filename): + src = \'\'\' + var data = [10, 20, 40, 30]; + var Module = { + stdin: function() { return data.pop() || null }, + stdout: function(x) { print('got: ' + x) } + }; + \'\'\' + open(filename, 'r').read() + open(filename, 'w').write(src) +''' + src = r''' + #include <stdio.h> + #include <unistd.h> + + int main () { + char c; + fprintf(stderr, "isatty? %d,%d,%d\n", isatty(fileno(stdin)), isatty(fileno(stdout)), isatty(fileno(stderr))); + while ((c = fgetc(stdin)) != EOF) { + putc(c+5, stdout); + } + return 0; + } + ''' + self.do_run(src, 'isatty? 0,0,1\ngot: 35\ngot: 45\ngot: 25\ngot: 15\n', post_build=post) + def test_folders(self): add_pre_run = ''' def process(filename): @@ -3867,7 +4100,9 @@ def process(filename): printf( "%i %i %i", one, two, three ); } ''' - self.do_run(src, "1 2 3") + for linkable in [0, 1]: + Settings.LINKABLE = linkable # regression check for issue #273 + self.do_run(src, "1 2 3") def test_readdir(self): add_pre_run = ''' @@ -4203,7 +4438,7 @@ def process(filename): ''' # FIXME: should not have so many newlines in output here - self.do_run(src, 'hello world\n\n77.\n') + self.do_run(src, 'hello world\n77.\n') def test_stdvec(self): src = ''' @@ -4813,6 +5048,74 @@ Block 0: ''', post_build=post1) ### Integration tests + def test_ccall(self): + if self.emcc_args is not None and '-O2' in self.emcc_args: + self.emcc_args += ['--closure', '1'] # Use closure here, to test we export things right + + src = r''' + #include <stdio.h> + + // Optimizations might wipe out our functions without this + #define KEEPALIVE __attribute__((used)) + + extern "C" { + int KEEPALIVE get_int() { return 5; } + float KEEPALIVE get_float() { return 3.14; } + char * KEEPALIVE get_string() { return "hello world"; } + void KEEPALIVE print_int(int x) { printf("%d\n", x); } + void KEEPALIVE print_float(float x) { printf("%.2f\n", x); } + void KEEPALIVE print_string(char *x) { printf("%s\n", x); } + int KEEPALIVE multi(int x, float y, int z, char *str) { puts(str); return (x+y)*z; } + int * KEEPALIVE pointer(int *in) { printf("%d\n", *in); static int ret = 21; return &ret; } + } + + int main(int argc, char **argv) { + // keep them alive + if (argc == 10) return get_int(); + if (argc == 11) return get_float(); + if (argc == 12) return get_string()[0]; + if (argc == 13) print_int(argv[0][0]); + if (argc == 14) print_float(argv[0][0]); + if (argc == 15) print_string(argv[0]); + if (argc == 16) pointer((int*)argv[0]); + if (argc % 17 == 12) return multi(argc, float(argc)/2, argc+1, argv[0]); + return 0; + } + ''' + + post = ''' +def process(filename): + src = \'\'\' + var Module = { + 'postRun': function() { + print('*'); + var ret; + ret = ccall('get_int', 'number'); print([typeof ret, ret]); + ret = ccall('get_float', 'number'); print([typeof ret, ret.toFixed(2)]); + ret = ccall('get_string', 'string'); print([typeof ret, ret]); + ret = ccall('print_int', null, ['number'], [12]); print(typeof ret); + ret = ccall('print_float', null, ['number'], [14.56]); print(typeof ret); + ret = ccall('print_string', null, ['string'], ["cheez"]); print(typeof ret); + ret = ccall('multi', 'number', ['number', 'number', 'number', 'string'], [2, 1.4, 3, 'more']); print([typeof ret, ret]); + var p = ccall('malloc', 'pointer', ['number'], [4]); + setValue(p, 650, 'i32'); + ret = ccall('pointer', 'pointer', ['pointer'], [p]); print([typeof ret, getValue(ret, 'i32')]); + print('*'); + // part 2: cwrap + var multi = cwrap('multi', 'number', ['number', 'number', 'number', 'string']); + print(multi(2, 1.4, 3, 'atr')); + print(multi(8, 5.4, 4, 'bret')); + print('*'); + } + }; + \'\'\' + open(filename, 'r').read() + open(filename, 'w').write(src) +''' + + Settings.EXPORTED_FUNCTIONS = ['_get_int', '_get_float', '_get_string', '_print_int', '_print_float', '_print_string', '_multi', '_pointer', '_malloc'] + + self.do_run(src, '*\nnumber,5\nnumber,3.14\nstring,hello world\n12\nundefined\n14.56\nundefined\ncheez\nundefined\nmore\nnumber,10\n650\nnumber,21\n*\natr\n10\nbret\n53\n*\n', post_build=post) + def test_scriptaclass(self): header_filename = os.path.join(self.get_dir(), 'header.h') header = ''' @@ -5394,7 +5697,7 @@ def process(filename): } ''' - if Settings.I64_MODE == 0: # the errors here are very specific to non-i64 mode 1 + if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1 Settings.CORRECT_ROUNDINGS = 0 self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-6**5*') # JS floor operations, always to the negative. This is an undetected error here! self.do_run(src.replace('TYPE', 'int'), '*-2**2**-5**5*') # We get these right, since they are 32-bit and we can shortcut using the |0 trick @@ -5407,7 +5710,7 @@ def process(filename): self.do_run(src.replace('TYPE', 'unsigned int'), '*2147483645**2**-5**5*') # Correct Settings.CORRECT_SIGNS = 0 - if Settings.I64_MODE == 0: # the errors here are very specific to non-i64 mode 1 + if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1 Settings.CORRECT_ROUNDINGS = 2 Settings.CORRECT_ROUNDINGS_LINES = ["src.cpp:13"] # Fix just the last mistake self.do_run(src.replace('TYPE', 'long long'), '*-3**2**-5**5*') @@ -5415,7 +5718,7 @@ def process(filename): self.do_run(src.replace('TYPE', 'unsigned int'), '*-3**2**-5**5*') # No such luck here # And reverse the check with = 2 - if Settings.I64_MODE == 0: # the errors here are very specific to non-i64 mode 1 + if Settings.USE_TYPED_ARRAYS != 2: # the errors here are very specific to non-i64 mode 1 Settings.CORRECT_ROUNDINGS = 3 Settings.CORRECT_ROUNDINGS_LINES = ["src.cpp:999"] self.do_run(src.replace('TYPE', 'long long'), '*-2**2**-5**5*') @@ -5478,16 +5781,21 @@ def process(filename): def test_exit_status(self): Settings.CATCH_EXIT_CODE = 1 - src = ''' + src = r''' #include <stdio.h> #include <stdlib.h> + static void cleanup() { + printf("cleanup\n"); + } + int main() { - printf("hello, world!\\n"); + atexit(cleanup); // this atexit should still be called + printf("hello, world!\n"); exit(118); // Unusual exit status to make sure it's working! } ''' - self.do_run(src, 'hello, world!\nExit Status: 118') + self.do_run(src, 'hello, world!\ncleanup\nExit Status: 118') # Generate tests for everything @@ -5539,11 +5847,6 @@ class %s(T): Settings.CATCH_EXIT_CODE = 0 Settings.EMULATE_UNALIGNED_ACCESSES = int(Settings.USE_TYPED_ARRAYS == 2 and Building.LLVM_OPTS == 2) Settings.DOUBLE_MODE = 1 if Settings.USE_TYPED_ARRAYS and Building.LLVM_OPTS == 0 else 0 - if Settings.USE_TYPED_ARRAYS == 2: - Settings.I64_MODE = 1 - Settings.SAFE_HEAP = 1 # only checks for alignment problems, which is very important with unsafe opts - else: - Settings.I64_MODE = 0 Building.pick_llvm_opts(3) @@ -5577,9 +5880,6 @@ TT = %s del T # T is just a shape for the specific subclasses, we don't test it itself class other(RunnerCore): - def test_reminder(self): - assert 0, 'find appearances of i64 in src/, most are now unneeded' - def test_emcc(self): emcc_debug = os.environ.get('EMCC_DEBUG') @@ -5636,7 +5936,7 @@ Options that are modified or new in %s include: # emcc src.cpp -c and emcc src.cpp -o src.[o|bc] ==> should give a .bc file # regression check: -o js should create "js", with bitcode content - for args in [['-c'], ['-o', 'src.o'], ['-o', 'src.bc'], ['-o', 'js']]: + for args in [['-c'], ['-o', 'src.o'], ['-o', 'src.bc'], ['-o', 'src.so'], ['-o', 'js']]: target = args[1] if len(args) == 2 else 'hello_world.o' clear() Popen([compiler, path_from_root('tests', 'hello_world' + suffix)] + args, stdout=PIPE, stderr=PIPE).communicate() @@ -5650,6 +5950,15 @@ Options that are modified or new in %s include: assert os.path.exists(target + '.js'), 'Expected %s to exist since args are %s : %s' % (target + '.js', str(args), '\n'.join(output)) self.assertContained('hello, world!', run_js(target + '.js')) + # handle singleton archives + clear() + Popen([compiler, path_from_root('tests', 'hello_world' + suffix), '-o', 'a.bc'], stdout=PIPE, stderr=PIPE).communicate() + Popen([LLVM_AR, 'r', 'a.a', 'a.bc'], stdout=PIPE, stderr=PIPE).communicate() + assert os.path.exists('a.a') + output = Popen([compiler, 'a.a']).communicate() + assert os.path.exists('a.out.js'), output + self.assertContained('hello, world!', run_js('a.out.js')) + # emcc src.ll ==> generates .js clear() output = Popen([compiler, path_from_root('tests', 'hello_world.ll')], stdout=PIPE, stderr=PIPE).communicate() @@ -5742,12 +6051,12 @@ Options that are modified or new in %s include: for params, test, text in [ (['-s', 'INLINING_LIMIT=0'], lambda generated: 'function _dump' in generated, 'no inlining without opts'), (['-O1', '-s', 'INLINING_LIMIT=0'], lambda generated: 'function _dump' not in generated, 'inlining'), - (['-s', 'USE_TYPED_ARRAYS=0', '-s', 'I64_MODE=0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'), - (['-s', 'USE_TYPED_ARRAYS=1', '-s', 'I64_MODE=0'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'), + (['-s', 'USE_TYPED_ARRAYS=0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'), + (['-s', 'USE_TYPED_ARRAYS=1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'), ([], lambda generated: 'Module["_dump"]' not in generated, 'dump is not exported by default'), (['-s', 'EXPORTED_FUNCTIONS=["_main", "_dump"]'], lambda generated: 'Module["_dump"]' in generated, 'dump is now exported'), - (['--typed-arrays', '0', '-s', 'I64_MODE=0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'), - (['--typed-arrays', '1', '-s', 'I64_MODE=0'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'), + (['--typed-arrays', '0'], lambda generated: 'new Int32Array' not in generated, 'disable typed arrays'), + (['--typed-arrays', '1'], lambda generated: 'IHEAPU = ' in generated, 'typed arrays 1 selected'), (['--typed-arrays', '2'], lambda generated: 'new Uint16Array' in generated and 'new Uint32Array' in generated, 'typed arrays 2 selected'), (['--llvm-opts', '1'], lambda generated: '_puts(' in generated, 'llvm opts requested'), ]: @@ -5884,13 +6193,134 @@ f.close() clear() output = Popen([EMCC, path_from_root('tests', 'hello_world_gles.c'), '-o', 'something.html', '-DHAVE_BUILTIN_SINCOS', - '-s', 'USE_TYPED_ARRAYS=0', '-s', 'I64_MODE=0', + '-s', 'USE_TYPED_ARRAYS=0', '--shell-file', path_from_root('tests', 'hello_world_gles_shell.html')], stdout=PIPE, stderr=PIPE).communicate() assert len(output[0]) == 0, output[0] assert os.path.exists('something.html'), output run_browser('something.html', 'You should not see animating gears.', '/report_gl_result?false') + def test_emcc_l_link(self): + # Linking with -lLIBNAME and -L/DIRNAME should work + + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' + extern void printey(); + int main() { + printey(); + return 0; + } + ''') + + try: + os.makedirs(os.path.join(self.get_dir(), 'libdir')); + except: + pass + + open(os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), 'w').write(''' + #include <stdio.h> + void printey() { + printf("hello from lib\\n"); + } + ''') + + Popen([EMCC, os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), '-c'], stdout=PIPE, stderr=STDOUT).communicate() + shutil.move(os.path.join(self.get_dir(), 'libfile.o'), os.path.join(self.get_dir(), 'libdir', 'libfile.so')) + Popen([EMCC, os.path.join(self.get_dir(), 'main.cpp'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile'], stdout=PIPE, stderr=STDOUT).communicate() + self.assertContained('hello from lib', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_emcc_embed_file(self): + open(os.path.join(self.get_dir(), 'somefile.txt'), 'w').write('''hello from a file with lots of data and stuff in it thank you very much''') + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' + #include <stdio.h> + int main() { + FILE *f = fopen("somefile.txt", "r"); + char buf[100]; + fread(buf, 1, 20, f); + buf[20] = 0; + fclose(f); + printf("|%s|\n", buf); + return 0; + } + ''') + + Popen([EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--embed-file', 'somefile.txt']).communicate() + self.assertContained('|hello from a file wi|', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_emcc_multidynamic_link(self): + # Linking the same dynamic library in will error, normally, since we statically link it, causing dupe symbols + # A workaround is to use --ignore-dynamic-linking, see emcc --help for details + + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(r''' + #include <stdio.h> + extern void printey(); + extern void printother(); + int main() { + printf("*"); + printey(); + printf("\n"); + printother(); + printf("\n"); + printf("*"); + return 0; + } + ''') + + try: + os.makedirs(os.path.join(self.get_dir(), 'libdir')); + except: + pass + + open(os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), 'w').write(''' + #include <stdio.h> + void printey() { + printf("hello from lib"); + } + ''') + + open(os.path.join(self.get_dir(), 'libdir', 'libother.cpp'), 'w').write(''' + #include <stdio.h> + extern void printey(); + void printother() { + printf("|"); + printey(); + printf("|"); + } + ''') + + # This lets us link the same dynamic lib twice. We will need to link it in manually at the end. + compiler = [EMCC, '--ignore-dynamic-linking'] + + # Build libfile normally into an .so + Popen(compiler + [os.path.join(self.get_dir(), 'libdir', 'libfile.cpp'), '-o', os.path.join(self.get_dir(), 'libdir', 'libfile.so')]).communicate() + # Build libother and dynamically link it to libfile - but add --ignore-dynamic-linking + Popen(compiler + [os.path.join(self.get_dir(), 'libdir', 'libother.cpp'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile', '-o', os.path.join(self.get_dir(), 'libdir', 'libother.so')]).communicate() + # Build the main file, linking in both the libs + Popen(compiler + [os.path.join(self.get_dir(), 'main.cpp'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile', '-lother', '-c']).communicate() + + # The normal build system is over. We need to do an additional step to link in the dynamic libraries, since we ignored them before + Popen([EMCC, os.path.join(self.get_dir(), 'main.o'), '-L' + os.path.join(self.get_dir(), 'libdir'), '-lfile', '-lother']).communicate() + + self.assertContained('*hello from lib\n|hello from lib|\n*', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + + def test_emcc_js_link(self): + open(os.path.join(self.get_dir(), 'main.cpp'), 'w').write(''' + #include <stdio.h> + int main() { + printf("hello from main\\n"); + return 0; + } + ''') + open(os.path.join(self.get_dir(), 'before.js'), 'w').write(''' + var MESSAGE = 'hello from js'; + if (typeof Module != 'undefined') throw 'This code should run before anything else!'; + ''') + open(os.path.join(self.get_dir(), 'after.js'), 'w').write(''' + print(MESSAGE); + ''') + + Popen([EMCC, os.path.join(self.get_dir(), 'main.cpp'), '--pre-js', 'before.js', '--post-js', 'after.js']).communicate() + self.assertContained('hello from main\nhello from js\n', run_js(os.path.join(self.get_dir(), 'a.out.js'))) + def test_eliminator(self): input = open(path_from_root('tools', 'eliminator', 'eliminator-test.js')).read() expected = open(path_from_root('tools', 'eliminator', 'eliminator-test-output.js')).read() @@ -5936,6 +6366,7 @@ elif 'benchmark' in str(sys.argv): pass finally: os.chdir(d) + fingerprint.append('llvm: ' + LLVM_ROOT) print 'Running Emscripten benchmarks... [ %s ]' % ' | '.join(fingerprint) sys.argv = filter(lambda x: x != 'benchmark', sys.argv) diff --git a/tests/time/output.txt b/tests/time/output.txt index 7ef34821..45b7d4bd 100644 --- a/tests/time/output.txt +++ b/tests/time/output.txt @@ -12,11 +12,11 @@ yday: 358 dst: 0 off: 0 zone: GMT -timegm <-> gmtime: 0 +timegm <-> gmtime: 1 old year: 102 new year: 70 old year again: 102 -localtime timezone: 0 +localtime timezone: 1 localtime daylight: 1 localtime tzname: 1 localtime <-> mktime: 1 diff --git a/tests/unistd/isatty.out b/tests/unistd/isatty.out index 9e7f41e8..6823c1f0 100644 --- a/tests/unistd/isatty.out +++ b/tests/unistd/isatty.out @@ -1,10 +1,10 @@ -read: -1 +read: 0 errno: 25 -write: -1 +write: 0 errno: 25 -all: -1 +all: 0 errno: 25 -folder: -1 +folder: 0 errno: 25 -file: -1 +file: 0 errno: 25 diff --git a/third_party/closure-compiler/COPYING b/third_party/closure-compiler/COPYING new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/third_party/closure-compiler/COPYING @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/closure-compiler/README b/third_party/closure-compiler/README new file mode 100644 index 00000000..e6d12c4a --- /dev/null +++ b/third_party/closure-compiler/README @@ -0,0 +1,292 @@ +/* + * Copyright 2009 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Contents +// + +The Closure Compiler performs checking, instrumentation, and +optimizations on JavaScript code. The purpose of this README is to +explain how to build and run the Closure Compiler. + +The Closure Compiler requires Java 6 or higher. +http://www.java.com/ + + +// +// Building The Closure Compiler +// + +There are three ways to get a Closure Compiler executable. + +1) Use one we built for you. + +Pre-built Closure binaries can be found at +http://code.google.com/p/closure-compiler/downloads/list + + +2) Check out the source and build it with Apache Ant. + +First, check out the full source tree of the Closure Compiler. There +are instructions on how to do this at the project site. +http://code.google.com/p/closure-compiler/source/checkout + +Apache Ant is a cross-platform build tool. +http://ant.apache.org/ + +At the root of the source tree, there is an Ant file named +build.xml. To use it, navigate to the same directory and type the +command + +ant jar + +This will produce a jar file called "build/compiler.jar". + + +3) Check out the source and build it with Eclipse. + +Eclipse is a cross-platform IDE. +http://www.eclipse.org/ + +Under Eclipse's File menu, click "New > Project ..." and create a +"Java Project." You will see an options screen. Give the project a +name, select "Create project from existing source," and choose the +root of the checked-out source tree as the existing directory. Verify +that you are using JRE version 6 or higher. + +Eclipse can use the build.xml file to discover rules. When you +navigate to the build.xml file, you will see all the build rules in +the "Outline" pane. Run the "jar" rule to build the compiler in +build/compiler.jar. + + +// +// Running The Closure Compiler +// + +Once you have the jar binary, running the Closure Compiler is straightforward. + +On the command line, type + +java -jar compiler.jar + +This starts the compiler in interactive mode. Type + +var x = 17 + 25; + +then hit "Enter", then hit "Ctrl-Z" (on Windows) or "Ctrl-D" (on Mac or Linux) +and "Enter" again. The Compiler will respond: + +var x=42; + +The Closure Compiler has many options for reading input from a file, +writing output to a file, checking your code, and running +optimizations. To learn more, type + +java -jar compiler.jar --help + +You can read more detailed documentation about the many flags at +http://code.google.com/closure/compiler/docs/gettingstarted_app.html + + +// +// Compiling Multiple Scripts +// + +If you have multiple scripts, you should compile them all together with +one compile command. + +java -jar compiler.jar --js=in1.js --js=in2.js ... --js_output_file=out.js + +The Closure Compiler will concatenate the files in the order they're +passed at the command line. + +If you need to compile many, many scripts together, you may start to +run into problems with managing dependencies between scripts. You +should check out the Closure Library. It contains functions for +enforcing dependencies between scripts, and a tool called calcdeps.py +that knows how to give scripts to the Closure Compiler in the right +order. + +http://code.google.com/p/closure-library/ + +// +// Licensing +// + +Unless otherwise stated, all source files are licensed under +the Apache License, Version 2.0. + + +----- +Code under: +src/com/google/javascript/rhino +test/com/google/javascript/rhino + +URL: http://www.mozilla.org/rhino +Version: 1.5R3, with heavy modifications +License: Netscape Public License and MPL / GPL dual license + +Description: A partial copy of Mozilla Rhino. Mozilla Rhino is an +implementation of JavaScript for the JVM. The JavaScript parser and +the parse tree data structures were extracted and modified +significantly for use by Google's JavaScript compiler. + +Local Modifications: The packages have been renamespaced. All code not +relavant to parsing has been removed. A JSDoc parser and static typing +system have been added. + + +----- +Code in: +lib/rhino + +Rhino +URL: http://www.mozilla.org/rhino +Version: Trunk +License: Netscape Public License and MPL / GPL dual license + +Description: Mozilla Rhino is an implementation of JavaScript for the JVM. + +Local Modifications: Minor changes to parsing JSDoc that usually get pushed +up-stream to Rhino trunk. + + +----- +Code in: +lib/args4j.jar + +Args4j +URL: https://args4j.dev.java.net/ +Version: 2.0.12 +License: MIT + +Description: +args4j is a small Java class library that makes it easy to parse command line +options/arguments in your CUI application. + +Local Modifications: None. + + +----- +Code in: +lib/guava.jar + +Guava Libraries +URL: http://code.google.com/p/guava-libraries/ +Version: r08 +License: Apache License 2.0 + +Description: Google's core Java libraries. + +Local Modifications: None. + + +----- +Code in: +lib/jsr305.jar + +Annotations for software defect detection +URL: http://code.google.com/p/jsr-305/ +Version: svn revision 47 +License: BSD License + +Description: Annotations for software defect detection. + +Local Modifications: None. + + +----- +Code in: +lib/jarjar.jar + +Jar Jar Links +URL: http://jarjar.googlecode.com/ +Version: 1.1 +License: Apache License 2.0 + +Description: +A utility for repackaging Java libraries. + +Local Modifications: None. + + +---- +Code in: +lib/junit.jar + +JUnit +URL: http://sourceforge.net/projects/junit/ +Version: 4.8.2 +License: Common Public License 1.0 + +Description: A framework for writing and running automated tests in Java. + +Local Modifications: None. + + +--- +Code in: +lib/protobuf-java.jar + +Protocol Buffers +URL: http://code.google.com/p/protobuf/ +Version: 2.3.0 +License: New BSD License + +Description: Supporting libraries for protocol buffers, +an encoding of structured data. + +Local Modifications: None + + +--- +Code in: +lib/ant.jar +lib/ant-launcher.jar + +URL: http://ant.apache.org/bindownload.cgi +Version: 1.8.1 +License: Apache License 2.0 +Description: + Ant is a Java based build tool. In theory it is kind of like "make" + without make's wrinkles and with the full portability of pure java code. + +Local Modifications: None + + +--- +Code in: +lib/json.jar +URL: http://json.org/java/index.html +Version: JSON version 20090211 +License: MIT license +Description: +JSON is a set of java files for use in transmitting data in JSON format. + +Local Modifications: None + +--- +Code in: +tools/maven-ant-tasks-2.1.1.jar +URL: http://maven.apache.org +Version 2.1.1 +License: Apache License 2.0 +Description: + Maven Ant tasks are used to manage dependencies and to install/deploy to + maven repositories. + +Local Modifications: None diff --git a/third_party/closure-compiler/compiler.jar b/third_party/closure-compiler/compiler.jar Binary files differnew file mode 100644 index 00000000..d958a618 --- /dev/null +++ b/third_party/closure-compiler/compiler.jar diff --git a/tools/bindings_generator.py b/tools/bindings_generator.py index e1c1ab4b..4731abf3 100755 --- a/tools/bindings_generator.py +++ b/tools/bindings_generator.py @@ -95,24 +95,38 @@ all_h.write(text) all_h.close() parsed = CppHeaderParser.CppHeader(all_h_name) -for classname, clazz in parsed.classes.iteritems(): - print 'zz see', classname +print 'zz dir: ', parsed.__dict__.keys() +for classname, clazz in parsed.classes.items() + parsed.structs.items(): + print 'zz see', classname, clazz, type(clazz) classes[classname] = clazz - clazz['methods'] = clazz['methods']['public'] # CppHeaderParser doesn't have 'public' etc. in structs. so equalize to that + if type(clazz['methods']) == dict: + clazz['saved_methods'] = clazz['methods'] + clazz['methods'] = clazz['methods']['public'] # CppHeaderParser doesn't have 'public' etc. in structs. so equalize to that if '::' in classname: assert classname.count('::') == 1 parents[classname.split('::')[1]] = classname.split('::')[0] - for sname, struct in clazz._public_structs.iteritems(): - parents[sname] = classname - classes[classname + '::' + sname] = struct - struct['name'] = sname # Missing in CppHeaderParser - print 'zz seen struct %s in %s' % (sname, classname) - + if hasattr(clazz, '_public_structs'): # This is a class + for sname, struct in clazz._public_structs.iteritems(): + parents[sname] = classname + classes[classname + '::' + sname] = struct + struct['name'] = sname # Missing in CppHeaderParser + print 'zz seen struct %s in %s' % (sname, classname) + + if 'fields' in clazz: # This is a struct + print 'zz add properties!' + clazz['properties'] = { 'public': clazz['fields'] } + clazz['name'] = classname + clazz['inherits'] = [] print 'zz parents: ', parents -for classname, clazz in classes.iteritems(): +def check_has_constructor(clazz): + for method in clazz['methods']: + if method['constructor'] and not method['destructor']: return True + return False + +for classname, clazz in parsed.classes.items() + parsed.structs.items(): # Various precalculations print 'zz precalc', classname for method in clazz['methods'][:]: @@ -157,7 +171,7 @@ for classname, clazz in classes.iteritems(): print 'zz subsubsub ', classname, method['name'], method['parameters'][0] method['name'] = 'op_sub' if len(method['parameters'][0]) == 0: - method['operator'] = ' return -*self; // %d' % len(method['parameters'][0]) + method['operator'] = ' static %s ret; ret = -*self; return ret;' % method['returns'] else: method['operator'] = ' return *self -= arg0; // %d : %s' % (len(method['parameters'][0]), method['parameters'][0][0]['name']) elif 'imul' in method['name']: @@ -237,21 +251,31 @@ for classname, clazz in classes.iteritems(): }]], }) - # Add destroyer - if not clazz.get('abstract'): - clazz['methods'].append({ - 'destroyer': True, - 'name': '__destroy__', - 'constructor': False, - 'destructor': False, - 'static': False, - 'returns': 'void', - 'returns_text': 'void', - 'returns_reference': False, - 'returns_pointer': False, - 'pure_virtual': False, - 'parameters': [[]], - }) + print 'zz is effectively abstract?', clazz['name'], classname, '0' + if 'saved_methods' in clazz and not check_has_constructor(clazz): + print 'zz is effectively abstract?', clazz['name'], '1' + # Having a private constructor and no public constructor means you are, in effect, abstract + for private_method in clazz['saved_methods']['private']: + print 'zz is effectively abstract?', clazz['name'], '2' + if private_method['constructor']: + print 'zz is effectively abstract?', clazz['name'], '3' + clazz['effectively_abstract'] = True + + # Add destroyer + if not clazz.get('abstract') and not clazz.get('effectively_abstract'): + clazz['methods'].append({ + 'destroyer': True, + 'name': '__destroy__', + 'constructor': False, + 'destructor': False, + 'static': False, + 'returns': 'void', + 'returns_text': 'void', + 'returns_reference': False, + 'returns_pointer': False, + 'pure_virtual': False, + 'parameters': [[]], + }) clazz['methods'] = filter(lambda method: not method.get('ignore'), clazz['methods']) @@ -277,7 +301,7 @@ def copy_args(args): ret.append(copiedarg) return ret -for classname, clazz in parsed.classes.iteritems(): +for classname, clazz in parsed.classes.items() + parsed.structs.items(): clazz['final_methods'] = {} def explore(subclass, template_name=None, template_value=None): @@ -286,10 +310,16 @@ for classname, clazz in parsed.classes.iteritems(): print classname, 'exploring', subclass['name'], '::', method['name'] if method['constructor']: - if clazz != subclass: continue # Subclasses cannot directly use their parent's constructors - if method['destructor']: continue # Nothing to do there + if clazz != subclass: + print "zz Subclasses cannot directly use their parent's constructors" + continue + if method['destructor']: + print 'zz Nothing to do there' + continue - if method.get('operator') and subclass is not clazz: continue # Do not use parent class operators. Cast to that class if you need those operators (castObject) + if method.get('operator') and subclass is not clazz: + print 'zz Do not use parent class operators. Cast to that class if you need those operators (castObject)' + continue if method['name'] not in clazz['final_methods']: copied = clazz['final_methods'][method['name']] = {} @@ -485,6 +515,7 @@ def generate_wrapping_code(classname): # %(classname)s.prototype['fields'] = Runtime.generateStructInfo(null, '%(classname)s'); - consider adding this def generate_class(generating_classname, classname, clazz): # TODO: deprecate generating? + print 'zz generating:', generating_classname, classname generating_classname_head = generating_classname.split('::')[-1] classname_head = classname.split('::')[-1] @@ -498,6 +529,7 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge gen_js.write('''Module['%s'] = %s; ''' % (generating_classname_head, generating_classname_head)) + print 'zz methods: ', clazz['final_methods'].keys() for method in clazz['final_methods'].itervalues(): mname = method['name'] if classname_head + '::' + mname in ignored: @@ -509,7 +541,7 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge destructor = method['destructor'] static = method['static'] - #print 'zz generating %s::%s. gets %s and returns %s' % (generating_classname, method['name'], str([arg['type'] for arg in method['parameters']]), method['returns_text']) + print 'zz generating %s::%s' % (generating_classname, method['name']) if destructor: continue if constructor and inherited: continue @@ -644,7 +676,7 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge print 'zz making return', classname, method['name'], method['returns'], return_value if method['returns'] in classes: # Generate a wrapper - calls += 'return wrapPointer(%s, %s);' % (return_value, method['returns'].split('::')[-1]) + calls += '''return wrapPointer(%s, Module['%s']);''' % (return_value, method['returns'].split('::')[-1]) else: # Normal return calls += ('return ' if ret != 'void' else '') + return_value + ';' @@ -686,7 +718,7 @@ Module['%s'] = %s; # Main loop -for classname, clazz in classes.iteritems(): +for classname, clazz in parsed.classes.items() + parsed.structs.items(): if any([name in ignored for name in classname.split('::')]): print 'zz ignoring', classname continue @@ -727,18 +759,17 @@ for classname, clazz in classes.iteritems(): print 'zz ignoring pure virtual class', classname, 'due to', method['name'] return True - clazz['abstract'] = check_pure_virtual(clazz, []) + clazz['abstract'] = check_pure_virtual(clazz, []) or clazz.get('effectively_abstract') + print 'zz', classname, 'is abstract?', clazz['abstract'] #if check_pure_virtual(clazz, []): # continue # Add a constructor if none exist - has_constructor = False - for method in clazz['methods']: - has_constructor = has_constructor or (method['constructor'] and not method['destructor']) - + has_constructor = check_has_constructor(clazz) + print 'zz', classname, 'has constructor?', has_constructor - + if not has_constructor: if not clazz['abstract']: print 'zz no constructor for', classname, 'and not abstract, so ignoring' diff --git a/tools/eliminator/eliminator-test-output.js b/tools/eliminator/eliminator-test-output.js index d914a5da..aac21e87 100644 --- a/tools/eliminator/eliminator-test-output.js +++ b/tools/eliminator/eliminator-test-output.js @@ -117,4 +117,13 @@ function f3($s, $tree, $k) { } HEAP32[($s + 2908 + ($storemerge_in << 2) | 0) >> 2] = $0; } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py", "r", "t", "f2", "f3"] +function llvm3_1() { + while (check()) { + if ($curri_01 % $zj_0 == 0) { + break; + } + var $j_0 = $aj_0 + 1; + run($j_0 / 2); + } +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py", "r", "t", "f2", "f3", "llvm3_1"] diff --git a/tools/eliminator/eliminator-test.js b/tools/eliminator/eliminator-test.js index 35bed0bb..55f74d67 100644 --- a/tools/eliminator/eliminator-test.js +++ b/tools/eliminator/eliminator-test.js @@ -130,5 +130,14 @@ function f3($s, $tree, $k) { } HEAP32[($s + 2908 + ($storemerge_in << 2) | 0) >> 2] = $0; } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py", "r", "t", "f2", "f3"] - +function llvm3_1() { + while (check()) { + var $inc = $aj_0 + 1; + if ($curri_01 % $zj_0 == 0) { + break; + } + var $j_0 = $inc; + run($j_0 / 2); + } +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["f", "g", "h", "py", "r", "t", "f2", "f3", "llvm3_1"] diff --git a/tools/eliminator/eliminator.coffee b/tools/eliminator/eliminator.coffee index b78a5a7e..ba91aa89 100644 --- a/tools/eliminator/eliminator.coffee +++ b/tools/eliminator/eliminator.coffee @@ -49,9 +49,6 @@ NODES_WITHOUT_SIDE_EFFECTS = # Nodes which may break control flow. Moving a variable beyond them may have # side effects. CONTROL_FLOW_NODES = - return: true - break: true - continue: true new: true throw: true call: true diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js index d84c664a..7094cdfc 100644 --- a/tools/js-optimizer.js +++ b/tools/js-optimizer.js @@ -15,6 +15,7 @@ var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIR if (ENVIRONMENT_IS_NODE) { // Expose functionality in the same simple way that the shells work + // Note that we pollute the global namespace here, otherwise we break in node print = function(x) { process['stdout'].write(x + '\n'); }; @@ -33,26 +34,30 @@ if (ENVIRONMENT_IS_NODE) { return ret; }; + load = function(f) { + globalEval(read(f)); + }; + arguments_ = process['argv'].slice(2); } else if (ENVIRONMENT_IS_SHELL) { // Polyfill over SpiderMonkey/V8 differences if (!this['read']) { - read = function(f) { snarf(f) }; + this['read'] = function(f) { snarf(f) }; } - if (!this['arguments']) { + if (typeof scriptArgs != 'undefined') { arguments_ = scriptArgs; - } else { + } else if (typeof arguments != 'undefined') { arguments_ = arguments; } } else if (ENVIRONMENT_IS_WEB) { - print = printErr = function(x) { + this['print'] = printErr = function(x) { console.log(x); }; - read = function(url) { + this['read'] = function(url) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, false); xhr.send(null); @@ -65,7 +70,7 @@ if (ENVIRONMENT_IS_NODE) { } else if (ENVIRONMENT_IS_WORKER) { // We can do very little here... - load = importScripts; + this['load'] = importScripts; } else { throw 'Unknown runtime environment. Where are we?'; @@ -76,17 +81,17 @@ function globalEval(x) { } if (typeof load == 'undefined' && typeof read != 'undefined') { - load = function(f) { + this['load'] = function(f) { globalEval(read(f)); }; } if (typeof printErr === 'undefined') { - printErr = function(){}; + this['printErr'] = function(){}; } if (typeof print === 'undefined') { - print = printErr; + this['print'] = printErr; } // *** Environment setup code *** @@ -239,7 +244,7 @@ function emptyNode() { // Dump the AST. Useful for debugging. For example, // node tools/js-optimizer.js ABSOLUTE_PATH_TO_FILE dumpAst function dumpAst(ast) { - printErr(JSON.stringify(ast)); + printErr(JSON.stringify(ast, null, ' ')); } function dumpSrc(ast) { @@ -576,16 +581,10 @@ function optimizeShiftsInternal(ast, conservative) { var name = node[2][1]; var data = vars[name]; var parent = stack[stack.length-3]; - var parentIndex; - if (parent[0] == 'defun') { - parentIndex = 3; - } else if (parent[0] == 'block') { - parentIndex = 1; - } else { - throw 'Invalid parent for assign-shift: ' + dump(parent); - } - var i = parent[parentIndex].indexOf(stack[stack.length-2]); - parent[parentIndex].splice(i+1, 0, ['stat', ['assign', true, ['name', name + '$s' + data.primaryShift], ['binary', '>>', ['name', name, true], ['num', data.primaryShift]]]]); + var statements = getStatements(parent); + assert(statements, 'Invalid parent for assign-shift: ' + dump(parent)); + var i = statements.indexOf(stack[stack.length-2]); + statements.splice(i+1, 0, ['stat', ['assign', true, ['name', name + '$s' + data.primaryShift], ['binary', '>>', ['name', name, true], ['num', data.primaryShift]]]]); } else if (node[0] == 'var') { var args = node[1]; for (var i = 0; i < args.length; i++) { @@ -801,18 +800,24 @@ function vacuum(ast) { if (node[0] == 'block' && (!node[1] || (typeof node[1] != 'object') || node[1].length == 0 || (node[1].length == 1 && isEmpty(node[1])))) return true; return false; } - function simplifyList(node, i) { + function simplifyList(node, si) { var changed = false; - var pre = node[i].length; - node[i] = node[i].filter(function(node) { return !isEmpty(node) }); - if (node[i].length < pre) changed = true; - // Also, seek blocks with single items we can simplify - node[i] = node[i].map(function(subNode) { - if (subNode[0] == 'block' && typeof subNode[1] == 'object' && subNode[1].length == 1 && subNode[1][0][0] == 'if') { - return subNode[1][0]; + // Merge block items into this list, thus removing unneeded |{ .. }|'s + var statements = node[si]; + var i = 0; + while (i < statements.length) { + var subNode = statements[i]; + if (subNode[0] == 'block') { + statements.splice.apply(statements, [i, 1].concat(subNode[1] || [])); + changed = true; + } else { + i++; } - return subNode; - }); + } + // Remove empty items + var pre = node[si].length; + node[si] = node[si].filter(function(node) { return !isEmpty(node) }); + if (node[si].length < pre) changed = true; if (changed) { return node; } @@ -1002,6 +1007,35 @@ function hoistMultiples(ast) { }); vacuum(ast); + + // Afterpass: Reduce + // if (..) { .. break|continue } else { .. } + // to + // if (..) { .. break|continue } .. + traverseGenerated(ast, function(container, type) { + var statements = getStatements(container); + if (!statements) return; + for (var i = 0; i < statements.length; i++) { + var node = statements[i]; + if (node[0] == 'if' && node[2][0] == 'block' && node[3] && node[3][0] == 'block') { + var stat1 = node[2][1], stat2 = node[3][1]; + // If break|continue in the latter and not the former, reverse them + if (!(stat1[stat1.length-1][0] in LOOP_FLOW) && (stat2[stat2.length-1][0] in LOOP_FLOW)) { + var temp = node[3]; + node[3] = node[2]; + node[2] = temp; + node[1] = ['unary-prefix', '!', node[1]]; + simplifyNotComps(node[1]); // bake the ! into the expression + stat1 = node[2][1]; + stat2 = node[3][1]; + } + if (stat1[stat1.length-1][0] in LOOP_FLOW) { + statements.splice.apply(statements, [i+1, 0].concat(stat2)); + node[3] = null; + } + } + } + }); } // Simplifies loops diff --git a/tools/make_minigzip.py b/tools/make_minigzip.py new file mode 100644 index 00000000..cdd9c2ab --- /dev/null +++ b/tools/make_minigzip.py @@ -0,0 +1,13 @@ +import os, sys +from subprocess import Popen, PIPE, STDOUT + +import shared + +print 'Building zlib' + +zlib = shared.Building.build_library('zlib', shared.EMSCRIPTEN_TEMP_DIR, shared.EMSCRIPTEN_TEMP_DIR, ['libz.a'], make_args=['libz.a'], copy_project=True, source_dir=shared.path_from_root('tests', 'zlib'))[0] + +print 'Building minigzip' + +Popen([shared.EMCC, '-O2', shared.path_from_root('tests', 'zlib', 'minigzip.c'), zlib, '-o', shared.path_from_root('tools', 'minigzip.js')]).communicate() + diff --git a/tools/scan_ll.py b/tools/scan_ll.py new file mode 100644 index 00000000..d7d42057 --- /dev/null +++ b/tools/scan_ll.py @@ -0,0 +1,18 @@ +''' +Finds why an .ll file is large by printing functions by size +''' + +import os, sys + +funcs = [] +i = 0 +for line in open(sys.argv[1]): + i += 1 + if line.startswith('define '): + inside = line.replace('define ', '').replace('\n', '') + start = i + elif line.startswith('}'): + funcs.append((inside, i-start)) + +print '\n'.join(map(lambda func: str(func[1]) + ':' + func[0], sorted(funcs, key=lambda func: -func[1]))) + diff --git a/tools/scons/site_scons/site_tools/emscripten/__init__.py b/tools/scons/site_scons/site_tools/emscripten/__init__.py new file mode 100644 index 00000000..8ae2288e --- /dev/null +++ b/tools/scons/site_scons/site_tools/emscripten/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python + +from emscripten import exists, generate diff --git a/tools/scons/site_scons/site_tools/emscripten/emscripten.py b/tools/scons/site_scons/site_tools/emscripten/emscripten.py new file mode 100644 index 00000000..cb14b58e --- /dev/null +++ b/tools/scons/site_scons/site_tools/emscripten/emscripten.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +import os + +def generate(env, emscripten_path=None, **kw): + """ SCons tool entry point """ + + if emscripten_path is None: + # Try to find emscripten + # Use same method as Emscripten's shared.py + EM_CONFIG = os.environ.get('EM_CONFIG') + if not EM_CONFIG: + EM_CONFIG = '~/.emscripten' + + CONFIG_FILE = os.path.expanduser(EM_CONFIG) + try: + exec(open(CONFIG_FILE, 'r').read()) + except Exception, e: + print >> sys.stderr, 'Error in evaluating %s (at %s): %s' % (EM_CONFIG, CONFIG_FILE, str(e)) + sys.exit(1) + + emscripten_path = EMSCRIPTEN_ROOT + + try: + emscPath = emscripten_path.abspath + except: + emscPath = emscripten_path + + env.Replace(CC = os.path.join(emscPath, "emcc" )) + env.Replace(CXX = os.path.join(emscPath, "em++" )) + env.Replace(LINK = os.path.join(emscPath, "emld" )) + # SHLINK and LDMODULE should use LINK so no + # need to change them here + + env.Replace(AR = os.path.join(emscPath, "emar" )) + env.Replace(RANLIB = os.path.join(emscPath, "emranlib")) + + env.Replace(OBJSUFFIX = [".js", ".bc", ".o"][2]) + env.Replace(LIBSUFFIX = [".js", ".bc", ".o"][2]) + env.Replace(PROGSUFFIX = [".html", ".js" ][1]) + +def exists(env): + """ NOOP method required by SCons """ + return 1 diff --git a/tools/shared.py b/tools/shared.py index b5ae1ae1..7570473c 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -139,6 +139,11 @@ except: print 'ERROR: %s does not seem to have JS_ENGINES or JS_ENGINE set up' % EM_CONFIG raise +try: + CLOSURE_COMPILER +except: + CLOSURE_COMPILER = path_from_root('third_party', 'closure-compiler', 'compiler.jar') + # Additional compiler options try: @@ -607,11 +612,10 @@ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)''' \ if unsafe: if not Building.can_inline(): opts.append('-disable-inlining') - # -Ox opts do -globaldce, which removes stuff that is needed for libraries and linkables - if Building.can_build_standalone(): - opts.append('-O%d' % optimization_level) - else: - opts.append('-std-compile-opts') + if not Building.can_build_standalone(): + # -O1 does not have -gobaldce, which removes stuff that is needed for libraries and linkables + optimization_level = min(1, optimization_level) + opts.append('-O%d' % optimization_level) #print '[unsafe: %s]' % ','.join(opts) else: allow_nonportable = False diff --git a/tools/test-js-optimizer-output.js b/tools/test-js-optimizer-output.js index 17c06a95..ca76cae1 100644 --- a/tools/test-js-optimizer-output.js +++ b/tools/test-js-optimizer-output.js @@ -68,9 +68,7 @@ function loopy() { something(); } while (0); next(); - { - something(); - } + something(); } function ignoreLoopy() { b$for_cond$4 : while (1) { @@ -141,12 +139,11 @@ function hoisting() { } pause(7); while (1) { - if ($i < $N) { - somethingElse(); - } else { + if ($i >= $N) { __label__ = 3; break; } + somethingElse(); if ($i < $N) { somethingElse(); } @@ -166,15 +163,43 @@ function hoisting() { } __label__ = 39; break; - } else { - __label__ = 38; } + __label__ = 38; } while (0); if (__label__ == 38) { var $79 = $_pr6; } pause(9); var $cmp70 = ($call69 | 0) != 0; + pause(10); + while (check()) { + if ($i < $N) { + callOther(); + break; + } + somethingElse(); + if ($i1 < $N) { + callOther(); + continue; + } + somethingElse(); + if ($i2 >= $N) { + somethingElse(); + break; + } + callOther(); + if ($i3 >= $N) { + somethingElse(); + continue; + } + callOther(); + if ($i4 < $N) { + callOther(); + break; + } + somethingElse(); + continue; + } } function innerShouldAlsoBeHoisted() { function hoisting() { @@ -208,9 +233,7 @@ function sleep() { } function demangle($cmp) { do { - if ($cmp) { - __label__ = 3; - } else { + if (!$cmp) { if (something()) { __label__ = 3; break; @@ -218,6 +241,7 @@ function demangle($cmp) { more(); break; } + __label__ = 3; } while (0); if (__label__ == 3) { final(); @@ -259,25 +283,21 @@ function moreLabels() { } if ($cmp1) { break; - } else { - inc(); } + inc(); } pause(999); $while_body$$while_end$31 : do { if ($cmp3) { var $6 = $5; - { - while (1) { - var $6; - $iter = $6 + 3; - if (FHEAP[$iter + 1] < $pct_addr) { - var $6 = $iter; - } else { - var $_lcssa = $iter; - break $while_body$$while_end$31; - } + while (1) { + var $6; + $iter = $6 + 3; + if (FHEAP[$iter + 1] >= $pct_addr) { + var $_lcssa = $iter; + break $while_body$$while_end$31; } + var $6 = $iter; } } else { var $_lcssa = $5; @@ -286,4 +306,9 @@ function moreLabels() { var $_lcssa; cheez(); } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting", "demangle", "lua", "moreLabels"] +function notComps() { + if (HEAP32[$incdec_ptr71_i + 8 >> 2] != 0) { + shoo(); + } +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting", "demangle", "lua", "moreLabels", "notComps"] diff --git a/tools/test-js-optimizer.js b/tools/test-js-optimizer.js index 95985bc8..18c9ac75 100644 --- a/tools/test-js-optimizer.js +++ b/tools/test-js-optimizer.js @@ -226,6 +226,65 @@ function hoisting() { $if_then72$$if_end73$126 : do { if (__label__ == 40) {} else if (__label__ == 41) {} } while (0); + pause(10); + while(check()) { + if ($i < $N) { + __label__ = 2; + } else { + __label__ = 3; + } + if (__label__ == 2) { + callOther(); + break; + } else if (__label__ == 3) { + somethingElse(); + } + if ($i1 < $N) { + __label__ = 2; + } else { + __label__ = 3; + } + if (__label__ == 2) { + callOther(); + continue; + } else if (__label__ == 3) { + somethingElse(); + } + if ($i2 < $N) { + __label__ = 2; + } else { + __label__ = 3; + } + if (__label__ == 2) { + callOther(); + } else if (__label__ == 3) { + somethingElse(); + break; + } + if ($i3 < $N) { + __label__ = 2; + } else { + __label__ = 3; + } + if (__label__ == 2) { + callOther(); + } else if (__label__ == 3) { + somethingElse(); + continue; + } + if ($i4 < $N) { + __label__ = 2; + } else { + __label__ = 3; + } + if (__label__ == 2) { + callOther(); + break; + } else if (__label__ == 3) { + somethingElse(); + continue; + } + } } function innerShouldAlsoBeHoisted() { function hoisting() { @@ -358,4 +417,9 @@ function moreLabels() { var $_lcssa; cheez(); } -// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting", "demangle", "lua", "moreLabels"] +function notComps() { + if (!(HEAP32[$incdec_ptr71_i + 8 >> 2] == 0)) { + shoo(); + } +} +// EMSCRIPTEN_GENERATED_FUNCTIONS: ["abc", "xyz", "xyz2", "expr", "loopy", "bits", "maths", "hoisting", "demangle", "lua", "moreLabels", "notComps"] |