diff options
author | Alon Zakai <alonzakai@gmail.com> | 2012-03-03 12:03:48 -0800 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2012-03-03 12:03:48 -0800 |
commit | d6cff2177ec065aa14f228ab547abc29ef37b248 (patch) | |
tree | 285c3d737500e8a3ed6be56dcaf9357ffd5c7cd9 | |
parent | dfd9488e0094a6b5433b406517338e0f757c1e27 (diff) | |
parent | 6f57ea8f0eeb220fc81726b4e3a3c02f4232b667 (diff) |
Merge branch 'master' into llvmsvn
57 files changed, 17508 insertions, 477 deletions
@@ -1,4 +1,5 @@ *.diff *.pyc *~ +*.bc @@ -99,8 +99,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 +152,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 +177,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 +189,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: @@ -205,6 +226,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 +264,10 @@ 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] @@ -308,7 +334,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): @@ -337,17 +367,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] @@ -376,12 +424,16 @@ 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 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): @@ -395,8 +447,34 @@ 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 '' ] + # 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) + assert len(input_files) > 0, 'emcc: no input files' newargs += CC_ADDITIONAL_ARGS @@ -449,7 +527,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) @@ -533,10 +611,24 @@ 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 temp_file in temp_files: @@ -557,7 +649,7 @@ try: # 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 SHAREDLIB_SUFFIXES) and shared.Building.is_ar(temp_files[0])): + (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, @@ -619,6 +711,25 @@ 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') @@ -660,7 +771,9 @@ try: if DEBUG: save_intermediate('eliminator') # js optimizer pre-pass - js_optimizer_queue += ['simplifyExpressionsPre', 'optimizeShiftsAggressive'] + 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') diff --git a/src/analyzer.js b/src/analyzer.js index 14a4f7de..8ded86f1 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -120,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)); @@ -140,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++; } @@ -182,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); @@ -218,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) { @@ -268,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]; @@ -296,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); @@ -353,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', @@ -364,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, } }; @@ -375,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; @@ -385,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; @@ -406,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': { @@ -418,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': { @@ -429,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; } @@ -469,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; @@ -497,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) { @@ -536,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) { @@ -548,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); } @@ -855,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 @@ -1096,18 +1151,61 @@ 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.lines.forEach(function(line) { if (line.intertype == 'indirectbr') { @@ -1136,7 +1234,7 @@ function analyzer(data, sidePass) { if (line.intertype == 'call' && line.ident == setjmp) { // Add a new label var oldIdent = label.ident; - var newIdent = oldIdent + '$$' + i; + var newIdent = func.labelIdCounter++; if (!func.setjmpTable) func.setjmpTable = []; func.setjmpTable.push([oldIdent, newIdent, line.assignTo]); func.labels.splice(i+1, 0, { @@ -1330,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 = []; @@ -1436,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; @@ -1487,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: diff --git a/src/compiler.js b/src/compiler.js index d37bc68b..134ac5bd 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,12 +38,16 @@ 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']) { @@ -52,11 +57,11 @@ if (ENVIRONMENT_IS_NODE) { } } 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..bd7b70f9 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -728,11 +728,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 +800,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 567520db..d4d57fc6 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -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; @@ -574,13 +574,13 @@ 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! 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 += '"' + getLabelId(triple[0]) + '": ' + 'function(value) { __label__ = ' + getLabelId(triple[1]) + '; ' + triple[2] + ' = value },'; }); ret += 'dummy: 0'; ret += '};\n'; @@ -591,7 +591,7 @@ function JSify(data, functionsOnly, givenFunctions) { } 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 + '}'; @@ -770,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) { @@ -788,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 = ''; @@ -808,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;'; } } @@ -924,7 +927,7 @@ function JSify(data, functionsOnly, givenFunctions) { } 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(item.ident, value, item.type) }).join(' || ') + ') {\n'; ret += ' ' + getPhiSetsForLabel(phiSets, targetLabel) + makeBranch(targetLabel, item.currLabelId || null) + '\n'; ret += '}\n'; } @@ -969,7 +972,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; }); @@ -979,6 +991,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]); @@ -988,9 +1001,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); @@ -1046,10 +1058,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 0d756608..817f87e2 100644 --- a/src/library.js +++ b/src/library.js @@ -2298,7 +2298,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 +2433,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)); @@ -3485,12 +3485,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 +4368,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 +4379,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 +4426,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 +4467,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 +4582,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 { @@ -5835,6 +5916,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 5a92335e..ff578c0a 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,7 +547,7 @@ 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 @@ -548,7 +560,7 @@ function splitI64(value) { } } function mergeI64(value) { - assert(I64_MODE == 1); + assert(USE_TYPED_ARRAYS == 2); if (legalizedI64s) { return RuntimeGenerator.makeBigInt(value + '$0', value + '$1'); } else { @@ -559,12 +571,12 @@ function mergeI64(value) { // 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)'; } @@ -672,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 @@ -689,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'; @@ -754,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))); @@ -771,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; @@ -1102,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 @@ -1330,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' }); } @@ -1442,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') { @@ -1462,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. } @@ -1550,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) { @@ -1606,56 +1620,56 @@ 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 + ' || ' + @@ -1679,31 +1693,39 @@ function processMathop(item) { 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]) + '/' + mergeI64(idents[1]), bits, op[0] === 's'))); + case 'mul': warnI64_1(); return finish(splitI64(mergeI64(idents[0]) + '*' + mergeI64(idents[1]))); + case 'urem': case 'srem': warnI64_1(); return finish(splitI64(mergeI64(idents[0]) + '%' + mergeI64(idents[1]))); 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); @@ -1712,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; } @@ -1787,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) { @@ -1811,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': { @@ -1821,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; } @@ -1855,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; @@ -1884,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/preamble.js b/src/preamble.js index 54e3b04c..98d12a43 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 @@ -377,11 +366,11 @@ var globalScope = this; // -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', or 'pointer' for any type of C pointer. +// @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 (except for 'pointer', which is a 'number'). +// @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 (except for 'pointer', which is a 'number'). +// @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') { @@ -414,6 +403,20 @@ function ccall(ident, returnType, argTypes, args) { } 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 @@ -535,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 @@ -580,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; @@ -666,7 +669,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 } diff --git a/src/settings.js b/src/settings.js index 6bca0174..4ceb3750 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 diff --git a/src/shell.js b/src/shell.js index d67cc45a..bc527192 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,12 +30,16 @@ 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']) { @@ -44,11 +49,11 @@ if (ENVIRONMENT_IS_NODE) { } } 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/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/legalizer_ta2.ll b/tests/cases/legalizer_ta2.ll index 1249c7eb..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 @@ -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..766e848e 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,12 +27,16 @@ 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']) { @@ -41,11 +46,11 @@ if (ENVIRONMENT_IS_NODE) { } } 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/runner.py b/tests/runner.py index 1a79da7c..26ae51f5 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> @@ -678,6 +665,72 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv): 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_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_unaligned(self): if Settings.QUANTUM_SIZE == 1: return self.skip('No meaning to unaligned addresses in q1') @@ -1506,8 +1559,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() @@ -1609,6 +1661,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> @@ -2189,9 +2243,6 @@ def process(filename): self.do_run(src, '*96,97,98,101,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) { @@ -3373,15 +3424,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(), @@ -3948,7 +3996,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 = ''' @@ -4949,6 +4999,11 @@ def process(filename): 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() @@ -4957,7 +5012,7 @@ def process(filename): 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*\n', post_build=post) + 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') @@ -5540,7 +5595,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 @@ -5553,7 +5608,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*') @@ -5561,7 +5616,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*') @@ -5690,11 +5745,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) @@ -5728,9 +5778,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') @@ -5806,7 +5853,6 @@ Options that are modified or new in %s include: 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') - shutil.copyfile('a.a', '/home/alon/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')) @@ -5903,12 +5949,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'), ]: @@ -6045,13 +6091,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() 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/js-optimizer.js b/tools/js-optimizer.js index a0595b88..213e2257 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,12 +34,16 @@ 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']) { @@ -48,11 +53,11 @@ if (ENVIRONMENT_IS_NODE) { } } 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 *** @@ -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++) { 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/shared.py b/tools/shared.py index b5ae1ae1..fd51282b 100644 --- a/tools/shared.py +++ b/tools/shared.py @@ -607,11 +607,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 |