diff options
author | max99x <max99x@gmail.com> | 2011-08-17 07:44:59 +0300 |
---|---|---|
committer | max99x <max99x@gmail.com> | 2011-08-17 07:44:59 +0300 |
commit | 25630c272ddd8d260f4bd8d60db5f2b01b6a00e8 (patch) | |
tree | 8708398c75748234299abae35dbdb3f8a095a5c6 | |
parent | ee4b6ebf353585fb0eb0809a4b677f5694a9ba79 (diff) | |
parent | c2d71f807a84cb1cfad154c59e9d6859fff3e631 (diff) |
Merge remote-tracking branch 'upstream/master'
-rw-r--r-- | src/analyzer.js | 14 | ||||
-rw-r--r-- | src/intertyper.js | 8 | ||||
-rw-r--r-- | src/jsifier.js | 10 | ||||
-rw-r--r-- | src/parseTools.js | 12 | ||||
-rw-r--r-- | src/preamble.js | 6 | ||||
-rw-r--r-- | src/runtime.js | 6 | ||||
-rw-r--r-- | src/settings.js | 8 | ||||
-rw-r--r-- | tests/cases/storestruct.ll | 84 | ||||
-rw-r--r-- | tests/cases/storestruct.txt | 3 | ||||
-rw-r--r-- | tests/runner.py | 26 | ||||
-rwxr-xr-x | tools/bindings_generator.py | 217 | ||||
-rwxr-xr-x | tools/exec_llvm.py | 7 |
12 files changed, 352 insertions, 49 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index b7660281..96e6297b 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -375,10 +375,8 @@ function analyzer(data) { if (USE_TYPED_ARRAYS !== 2) return; function seekIdent(item, obj) { -//if (item.intertype === 'value') print('seeeeeeek ' + dump(item)); if (item.ident === obj.ident) { obj.found++; -//print('zz FOUNDZEY'); } } @@ -395,14 +393,18 @@ function analyzer(data) { item.functions.forEach(function(func) { func.lines.forEach(function(line, i) { if (line.intertype === 'assign' && line.value.intertype === 'load') { - var total = func.variables[line.ident].uses; + var data = func.variables[line.ident] + if (data.type === 'i1') { + line.value.unsigned = true; + return; + } + + var total = data.uses; if (total === 0) return; var obj = { ident: line.ident, found: 0, unsigned: 0, signed: 0, total: total }; -//print('zz SIGNALYZE ' + line.lineNum + ' : ' + dump(obj)); // in loops with phis, we can also be used *before* we are defined var j = i-1, k = i+1; while(1) { -//print(' ' + [j >= 0 ? func.lines[j].lineNum : null, k < func.lines.length ? func.lines[k].lineNum : null, obj.found, obj.total]); assert(j >= 0 || k < func.lines.length, 'Signalyzer ran out of space to look for sign indications for line ' + line.lineNum); if (j >= 0 && walkInterdata(func.lines[j], seekIdent, seekMathop, obj)) break; if (k < func.lines.length && walkInterdata(func.lines[k], seekIdent, seekMathop, obj)) break; @@ -410,7 +412,7 @@ function analyzer(data) { j -= 1; k += 1; } -//print('zz signz: ' + dump(obj)); + // unsigned+signed might be < total, since the same ident can appear multiple times in the same mathop. // found can actually be > total, since we currently have the same ident in a GEP (see cubescript test) // in the GEP item, and a child item (we have the ident copied onto the GEP item as a convenience). diff --git a/src/intertyper.js b/src/intertyper.js index 95b1fcd3..232025cb 100644 --- a/src/intertyper.js +++ b/src/intertyper.js @@ -632,11 +632,17 @@ function intertyper(data, parseFunctions, baseLineNum) { } }); // 'alloca' + var allocaPossibleVars = ['allocatedNum']; substrate.addActor('Alloca', { processItem: function(item) { item.intertype = 'alloca'; item.allocatedType = item.tokens[1].text; - item.allocatedNum = (item.tokens.length > 3 && Runtime.isNumberType(item.tokens[3].text)) ? toNiceIdent(item.tokens[4].text) : 1; + if (item.tokens.length > 3 && Runtime.isNumberType(item.tokens[3].text)) { + item.allocatedNum = toNiceIdent(item.tokens[4].text); + item.possibleVars = allocaPossibleVars; + } else { + item.allocatedNum = 1; + } item.type = addPointing(item.tokens[1].text); // type of pointer we will get Types.needAnalysis[item.type] = 0; item.type2 = item.tokens[1].text; // value we will create, and get a pointer to diff --git a/src/jsifier.js b/src/jsifier.js index c6f73d50..3da296f1 100644 --- a/src/jsifier.js +++ b/src/jsifier.js @@ -685,12 +685,15 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { makeFuncLineActor('invoke', function(item) { // Wrapping in a function lets us easily return values if we are // in an assignment + var call_ = makeFunctionCall(item.ident, item.params, item.funcData); + var branch = makeBranch(item.toLabel, item.currLabelId); + if (DISABLE_EXCEPTIONS) return call_ + '; ' + branch; var ret = '(function() { try { __THREW__ = false; return ' - + makeFunctionCall(item.ident, item.params, item.funcData) + ' ' + + call_ + ' ' + '} catch(e) { ' + 'if (ABORT) throw e; __THREW__ = true; ' + (EXCEPTION_DEBUG ? 'print("Exception: " + e + ", currently at: " + (new Error().stack)); ' : '') - + 'return null } })(); if (!__THREW__) { ' + makeBranch(item.toLabel, item.currLabelId) + + 'return null } })(); if (!__THREW__) { ' + branch + ' } else { ' + makeBranch(item.unwindLabel, item.currLabelId) + ' }'; return ret; }); @@ -793,7 +796,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { return makeFunctionCall(item.ident, item.params, item.funcData) + (item.standalone ? ';' : ''); }); - makeFuncLineActor('unreachable', function(item) { return 'throw "Reached an unreachable! Original .ll line: ' + item.lineNum + '";' }); + makeFuncLineActor('unreachable', function(item) { return 'throw "Reached an unreachable!"' }); // Original .ll line: ' + item.lineNum + '";' }); // Final combiner @@ -837,6 +840,7 @@ function JSify(data, functionsOnly, givenFunctions, givenGlobalVariables) { if (RUNTIME_TYPE_INFO) { Types.cleanForRuntime(); print('Runtime.typeInfo = ' + JSON.stringify(Types.types)); + print('Runtime.structMetadata = ' + JSON.stringify(Types.structMetadata)); } generated.forEach(function(item) { print(indentify(item.JS || '', 2)); }); print(Functions.generateIndexing()); diff --git a/src/parseTools.js b/src/parseTools.js index 91956cad..6eb95593 100644 --- a/src/parseTools.js +++ b/src/parseTools.js @@ -730,6 +730,11 @@ function makeSetValue(ptr, pos, value, type, noNeedFirst, ignore) { if (isStructType(type)) { var typeData = Types.types[type]; var ret = []; + // We can receive either an object - an object literal that was in the .ll - or a string, + // which is the ident of an aggregate struct + if (typeof value === 'string') { + value = range(typeData.fields.length).map(function(i) { return value + '.f' + i }); + } for (var i = 0; i < typeData.fields.length; i++) { ret.push(makeSetValue(ptr, pos + typeData.flatIndexes[i], value[i], typeData.fields[i], noNeedFirst)); } @@ -1278,6 +1283,13 @@ function walkInterdata(item, pre, post, obj) { if (walkInterdata(item.params[i], pre, post, obj)) return true; } } + if (item.possibleVars) { // other attributes that might contain interesting data; here, variables + var box = { intertype: 'value', ident: '' }; + for (i = 0; i <= item.possibleVars.length; i++) { + box.ident = item[item.possibleVars[i]]; + if (walkInterdata(box, pre, post, obj)) return true; + } + } return post(item, originalObj, obj); } diff --git a/src/preamble.js b/src/preamble.js index 8feb43e9..0d802409 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -320,6 +320,7 @@ function setValue(ptr, value, type) { default: abort('invalid type for setValue: ' + type); } } +this['setValue'] = setValue; // Parallel to setValue. @@ -337,6 +338,7 @@ function getValue(ptr, type) { } return null; } +this['getValue'] = getValue; // Allocates memory for some data and initializes it properly. @@ -446,6 +448,10 @@ if (HAS_TYPED_ARRAYS) { HEAPU16 = new Uint16Array(buffer); HEAPU32 = new Uint32Array(buffer); HEAPF32 = new Float32Array(buffer); + + // Endianness check (note: assumes compiler arch was little-endian) + HEAP32[0] = 255; + assert(HEAPU8[0] === 255 && HEAPU8[3] === 0, 'Typed arrays 2 must be run on a little-endian system'); #endif } else #endif diff --git a/src/runtime.js b/src/runtime.js index ad3c973d..abeb0d2a 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -183,11 +183,11 @@ Runtime = { var type, alignment; if (typeName) { offset = offset || 0; - type = typeof Types === 'undefined' ? Runtime.typeInfo[typeName] : Types.types[typeName]; + type = (typeof Types === 'undefined' ? Runtime.typeInfo : Types.types)[typeName]; if (!type) return null; - if (!struct) struct = Types.structMetadata[typeName.replace(/.*\./, '')]; + if (!struct) struct = (typeof Types === 'undefined' ? Runtime : Types).structMetadata[typeName.replace(/.*\./, '')]; if (!struct) return null; - assert(type.fields.length === struct.length, 'Number of named fields must match the type for ' + typeName); + assert(type.fields.length === struct.length, 'Number of named fields must match the type for ' + typeName + '. Perhaps due to inheritance, which is not supported yet?'); alignment = type.flatIndexes; } else { var type = { fields: struct.map(function(item) { return item[0] }) }; diff --git a/src/settings.js b/src/settings.js index 226c30b2..ed3437b1 100644 --- a/src/settings.js +++ b/src/settings.js @@ -63,6 +63,14 @@ SAFE_HEAP_LOG = 0; // Log out all SAFE_HEAP operations LABEL_DEBUG = 0; // Print out labels and functions as we enter them EXCEPTION_DEBUG = 1; // Print out exceptions in emscriptened code +DISABLE_EXCEPTIONS = 0; // Disables generating code to actually catch exceptions. If the code you + // are compiling does not actually rely on catching exceptions (but the + // compiler generates code for it, maybe because of stdlibc++ stuff), + // then this can make it much faster. If an exception actually happens, + // it will not be caught and the program will halt (so this will not + // introduce silent failures, which is good). + // TODO: Make this also remove cxa_begin_catch etc., optimize relooper + // for it, etc. (perhaps do all of this as preprocessing on .ll?) EXECUTION_TIMEOUT = -1; // Throw an exception after X seconds - useful to debug infinite loops CHECK_OVERFLOWS = 0; // Add code that checks for overflows in integer math operations. // There is currently not much to do to handle overflows if they occur. diff --git a/tests/cases/storestruct.ll b/tests/cases/storestruct.ll new file mode 100644 index 00000000..15022e2f --- /dev/null +++ b/tests/cases/storestruct.ll @@ -0,0 +1,84 @@ +; ModuleID = '/dev/shm/tmp/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" +target triple = "i386-pc-linux-gnu" + +; Load and store an entire structure as a whole (and also load as a whole, extract values and save separately, etc.) + +%struct.X = type { i32, i32 } + +@.str = private unnamed_addr constant [9 x i8] c"*%d,%d*\0A\00" ; [#uses=1] + +; [#uses=0] +define i32 @main() { +entry: + %retval = alloca i32, align 4 ; [#uses=1] + %x = alloca %struct.X, align 4 ; [#uses=2] + %y = alloca %struct.X, align 4 ; [#uses=2] + store i32 0, i32* %retval + call void @llvm.dbg.declare(metadata !{%struct.X* %x}, metadata !6), !dbg !13 + call void @llvm.dbg.declare(metadata !{%struct.X* %y}, metadata !14), !dbg !15 + %a = getelementptr inbounds %struct.X* %x, i32 0, i32 0, !dbg !16 ; [#uses=1] + store i32 5, i32* %a, align 4, !dbg !16 + %b = getelementptr inbounds %struct.X* %x, i32 0, i32 1, !dbg !17 ; [#uses=1] + store i32 22, i32* %b, align 4, !dbg !17 + + %allx = load %struct.X* %x, align 4, !dbg !13 ; [#uses=1] + store %struct.X %allx, %struct.X* %y, align 4, !dbg !15 + + %a1 = getelementptr inbounds %struct.X* %y, i32 0, i32 0, !dbg !18 ; [#uses=1] + %tmp = load i32* %a1, align 4, !dbg !18 ; [#uses=1] + %b2 = getelementptr inbounds %struct.X* %y, i32 0, i32 1, !dbg !18 ; [#uses=1] + %tmp3 = load i32* %b2, align 4, !dbg !18 ; [#uses=1] + %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0), i32 %tmp, i32 %tmp3), !dbg !18 ; [#uses=0] + + store i32 7, i32* %a, align 4, !dbg !16 + store i32 96, i32* %b, align 4, !dbg !17 + %allx2 = load %struct.X* %x, align 4, !dbg !13 ; [#uses=1] + + %x_a = extractvalue %struct.X %allx2, 0 ; [#uses=1] + store i32 %x_a, i32* %a1, align 4 + %x_b = extractvalue %struct.X %allx2, 1 ; [#uses=1] + store i32 %x_b, i32* %b2, align 4 + + %tmp5 = load i32* %a1, align 4, !dbg !18 ; [#uses=1] + %tmp6 = load i32* %b2, align 4, !dbg !18 ; [#uses=1] + %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0), i32 %tmp5, i32 %tmp6), !dbg !18 ; [#uses=0] + + %ptr = inttoptr i32 52 to i32* ; [#uses=1] + store %struct.X { i32 ptrtoint (i32* getelementptr inbounds (i32* %ptr, i32 1, i32 0) to i32), i32 3 }, %struct.X* %y, align 4 ; store entire struct at once + + %tmp5 = load i32* %a1, align 4, !dbg !18 ; [#uses=1] + %tmp6 = load i32* %b2, align 4, !dbg !18 ; [#uses=1] + %call = call i32 (i8*, ...)* @printf(i8* getelementptr inbounds ([9 x i8]* @.str, i32 0, i32 0), i32 %tmp5, i32 %tmp6), !dbg !18 ; [#uses=0] + + ret i32 0, !dbg !19 +} + +; [#uses=2] +declare void @llvm.dbg.declare(metadata, metadata) nounwind readnone + +; [#uses=1] +declare i32 @printf(i8*, ...) + +!llvm.dbg.sp = !{!0} + +!0 = metadata !{i32 589870, i32 0, metadata !1, metadata !"main", metadata !"main", metadata !"", metadata !1, i32 5, metadata !3, i1 false, i1 true, i32 0, i32 0, i32 0, i32 256, i1 false, i32 ()* @main} ; [ DW_TAG_subprogram ] +!1 = metadata !{i32 589865, metadata !"/dev/shm/tmp/src.cpp", metadata !"/dev/shm/tmp", metadata !2} ; [ DW_TAG_file_type ] +!2 = metadata !{i32 589841, i32 0, i32 4, metadata !"/dev/shm/tmp/src.cpp", metadata !"/dev/shm/tmp", metadata !"clang version 2.9 (tags/RELEASE_29/final)", i1 true, i1 false, metadata !"", i32 0} ; [ DW_TAG_compile_unit ] +!3 = metadata !{i32 589845, metadata !1, metadata !"", metadata !1, i32 0, i64 0, i64 0, i32 0, i32 0, i32 0, metadata !4, i32 0, i32 0} ; [ DW_TAG_subroutine_type ] +!4 = metadata !{metadata !5} +!5 = metadata !{i32 589860, metadata !2, metadata !"int", null, i32 0, i64 32, i64 32, i64 0, i32 0, i32 5} ; [ DW_TAG_base_type ] +!6 = metadata !{i32 590080, metadata !7, metadata !"x", metadata !1, i32 6, metadata !8, i32 0} ; [ DW_TAG_auto_variable ] +!7 = metadata !{i32 589835, metadata !0, i32 5, i32 11, metadata !1, i32 0} ; [ DW_TAG_lexical_block ] +!8 = metadata !{i32 589826, metadata !2, metadata !"X", metadata !1, i32 3, i64 64, i64 32, i32 0, i32 0, null, metadata !9, i32 0, null, metadata !12} ; [ DW_TAG_class_type ] +!9 = metadata !{metadata !10, metadata !11} +!10 = metadata !{i32 589837, metadata !1, metadata !"a", metadata !1, i32 3, i64 32, i64 32, i64 0, i32 0, metadata !5} ; [ DW_TAG_member ] +!11 = metadata !{i32 589837, metadata !1, metadata !"b", metadata !1, i32 3, i64 32, i64 32, i64 32, i32 0, metadata !5} ; [ DW_TAG_member ] +!12 = metadata !{i32 0} +!13 = metadata !{i32 6, i32 3, metadata !7, null} +!14 = metadata !{i32 590080, metadata !7, metadata !"y", metadata !1, i32 6, metadata !8, i32 0} ; [ DW_TAG_auto_variable ] +!15 = metadata !{i32 6, i32 6, metadata !7, null} +!16 = metadata !{i32 7, i32 1, metadata !7, null} +!17 = metadata !{i32 8, i32 1, metadata !7, null} +!18 = metadata !{i32 9, i32 13, metadata !7, null} +!19 = metadata !{i32 10, i32 13, metadata !7, null} diff --git a/tests/cases/storestruct.txt b/tests/cases/storestruct.txt new file mode 100644 index 00000000..e52a6bf5 --- /dev/null +++ b/tests/cases/storestruct.txt @@ -0,0 +1,3 @@ +*5,22* +*7,96* +*56,3* diff --git a/tests/runner.py b/tests/runner.py index e7cdcff1..ec47416f 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -60,7 +60,12 @@ class RunnerCore(unittest.TestCase): # Similar to LLVM::createStandardModulePasses() def pick_llvm_opts(self, optimization_level, optimize_size, allow_nonportable=False): - global LLVM_OPT_OPTS + global LLVM_OPT_OPTS, USE_TYPED_ARRAYS + + #if USE_TYPED_ARRAYS == 2: # unsafe optimizations. TODO: fix all issues blocking this from being used + # LLVM_OPT_OPTS = ['-O3'] + # return + LLVM_OPT_OPTS = pick_llvm_opts(optimization_level, optimize_size, allow_nonportable) # Emscripten optimizations that we run on the .ll file @@ -173,7 +178,7 @@ class RunnerCore(unittest.TestCase): def do_emscripten(self, filename, output_processor=None, append_ext=True, extra_args=[]): # Run Emscripten exported_settings = {} - for setting in ['QUANTUM_SIZE', 'RELOOP', 'OPTIMIZE', 'ASSERTIONS', 'USE_TYPED_ARRAYS', 'SAFE_HEAP', 'CHECK_OVERFLOWS', 'CORRECT_OVERFLOWS', 'CORRECT_SIGNS', 'CHECK_SIGNS', 'CORRECT_OVERFLOWS_LINES', 'CORRECT_SIGNS_LINES', 'CORRECT_ROUNDINGS', 'CORRECT_ROUNDINGS_LINES', 'INVOKE_RUN', 'SAFE_HEAP_LINES', 'INIT_STACK', 'AUTO_OPTIMIZE', 'EXPORTED_FUNCTIONS', 'EXPORTED_GLOBALS', 'BUILD_AS_SHARED_LIB', 'INCLUDE_FULL_LIBRARY', 'RUNTIME_TYPE_INFO']: + for setting in ['QUANTUM_SIZE', 'RELOOP', 'OPTIMIZE', 'ASSERTIONS', 'USE_TYPED_ARRAYS', 'SAFE_HEAP', 'CHECK_OVERFLOWS', 'CORRECT_OVERFLOWS', 'CORRECT_SIGNS', 'CHECK_SIGNS', 'CORRECT_OVERFLOWS_LINES', 'CORRECT_SIGNS_LINES', 'CORRECT_ROUNDINGS', 'CORRECT_ROUNDINGS_LINES', 'INVOKE_RUN', 'SAFE_HEAP_LINES', 'INIT_STACK', 'AUTO_OPTIMIZE', 'EXPORTED_FUNCTIONS', 'EXPORTED_GLOBALS', 'BUILD_AS_SHARED_LIB', 'INCLUDE_FULL_LIBRARY', 'RUNTIME_TYPE_INFO', 'DISABLE_EXCEPTIONS']: try: value = eval(setting) exported_settings[setting] = value @@ -950,6 +955,10 @@ if 'benchmark' not in sys.argv: ''' self.do_test(src, '*throw...caught!infunc...done!*') + global DISABLE_EXCEPTIONS + DISABLE_EXCEPTIONS = 1 + self.do_test(src, 'Compiled code throwing an exception') + def test_class(self): src = ''' #include <stdio.h> @@ -2724,6 +2733,7 @@ if 'benchmark' not in sys.argv: global CHECK_OVERFLOWS; CHECK_OVERFLOWS = 0 self.do_test(path_from_root('tests', 'cubescript'), '*\nTemp is 33\n9\n5\nhello, everyone\n*', main_file='command.cpp') + #build_ll_hook=self.do_autodebug) def test_gcc_unmangler(self): self.do_test(path_from_root('third_party'), '*d_demangle(char const*, int, unsigned int*)*', args=['_ZL10d_demanglePKciPj'], main_file='gcc_demangler.c') @@ -3159,6 +3169,8 @@ if 'benchmark' not in sys.argv: # Way 2: use CppHeaderParser + global RUNTIME_TYPE_INFO; RUNTIME_TYPE_INFO = 1 + header = ''' #include <stdio.h> @@ -3334,13 +3346,16 @@ Child2:9 print('|' + Runtime.typeInfo.UserStruct.fields + '|' + Runtime.typeInfo.UserStruct.flatIndexes + '|'); var t = Runtime.generateStructInfo(['x', { us: ['x', 'y', 'z'] }, 'y'], 'Encloser') print('|' + [t.x, t.us.x, t.us.y, t.us.z, t.y] + '|'); + print('|' + JSON.stringify(Runtime.generateStructInfo(null, 'UserStruct')) + '|'); } else { print('No type info.'); } ''' ) open(filename, 'w').write(src) - self.do_test(src, '*ok:5*\n|i32,i8,i16|0,4,6|\n|0,4,8,10,12|', post_build=post) + self.do_test(src, + '*ok:5*\n|i32,i8,i16|0,4,6|\n|0,4,8,10,12|\n|{"__size__":8,"x":0,"y":4,"z":6}|', + post_build=post) # Make sure that without the setting, we don't spam the .js with the type info RUNTIME_TYPE_INFO = 0 @@ -3668,7 +3683,7 @@ Child2:9 exec(''' class %s(T): def setUp(self): - global COMPILER, QUANTUM_SIZE, RELOOP, OPTIMIZE, ASSERTIONS, USE_TYPED_ARRAYS, LLVM_OPTS, SAFE_HEAP, CHECK_OVERFLOWS, CORRECT_OVERFLOWS, CORRECT_OVERFLOWS_LINES, CORRECT_SIGNS, CORRECT_SIGNS_LINES, CHECK_SIGNS, COMPILER_TEST_OPTS, CORRECT_ROUNDINGS, CORRECT_ROUNDINGS_LINES, INVOKE_RUN, SAFE_HEAP_LINES, INIT_STACK, AUTO_OPTIMIZE, RUNTIME_TYPE_INFO + global COMPILER, QUANTUM_SIZE, RELOOP, OPTIMIZE, ASSERTIONS, USE_TYPED_ARRAYS, LLVM_OPTS, SAFE_HEAP, CHECK_OVERFLOWS, CORRECT_OVERFLOWS, CORRECT_OVERFLOWS_LINES, CORRECT_SIGNS, CORRECT_SIGNS_LINES, CHECK_SIGNS, COMPILER_TEST_OPTS, CORRECT_ROUNDINGS, CORRECT_ROUNDINGS_LINES, INVOKE_RUN, SAFE_HEAP_LINES, INIT_STACK, AUTO_OPTIMIZE, RUNTIME_TYPE_INFO, DISABLE_EXCEPTIONS COMPILER = '%s' llvm_opts = %d @@ -3691,6 +3706,7 @@ class %s(T): CHECK_SIGNS = 0 #1-(embetter or llvm_opts) INIT_STACK = 0 RUNTIME_TYPE_INFO = 0 + DISABLE_EXCEPTIONS = 0 if LLVM_OPTS: self.pick_llvm_opts(3, True) COMPILER_TEST_OPTS = ['-g'] @@ -3742,7 +3758,7 @@ else: QUANTUM_SIZE = 1 RELOOP = OPTIMIZE = 1 USE_TYPED_ARRAYS = 0 - ASSERTIONS = SAFE_HEAP = CHECK_OVERFLOWS = CORRECT_OVERFLOWS = CHECK_SIGNS = INIT_STACK = AUTO_OPTIMIZE = RUNTIME_TYPE_INFO = 0 + ASSERTIONS = SAFE_HEAP = CHECK_OVERFLOWS = CORRECT_OVERFLOWS = CHECK_SIGNS = INIT_STACK = AUTO_OPTIMIZE = RUNTIME_TYPE_INFO = DISABLE_EXCEPTIONS = 0 INVOKE_RUN = 1 CORRECT_SIGNS = 0 CORRECT_ROUNDINGS = 0 diff --git a/tools/bindings_generator.py b/tools/bindings_generator.py index 171c1d10..076c1106 100755 --- a/tools/bindings_generator.py +++ b/tools/bindings_generator.py @@ -94,6 +94,7 @@ for classname, clazz in parsed.classes.iteritems(): print 'zz see', classname classes[classname] = clazz 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] @@ -145,25 +146,97 @@ for classname, clazz in classes.iteritems(): if method.get('returns_reference'): method['returns_text'] += '&' method['returns_text'] = type_processor(method['returns_text']) + print 'zz %s::%s gets %s and returns %s' % (classname, method['name'], str([arg['type'] for arg in method['parameters']]), method['returns_text']) + + # Add getters/setters for public members + for prop in clazz['properties']['public']: + if classname + '::' + prop['name'] in ignored: continue + if prop.get('array_dimensions'): + print 'zz warning: ignoring getter/setter for array', classname + '::' + prop['name'] + continue + type_ = prop['type'].replace('mutable ', '')#.replace(' ', '') + if '<' in prop['name'] or '<' in type_: + print 'zz warning: ignoring getter/setter for templated class', classname + '::' + prop['name'] + continue + reference = type_ in classes # a raw struct or class as a prop means we need to work with a ref + clazz['methods'].append({ + 'getter': True, + 'name': 'get_' + prop['name'], + 'constructor': False, + 'destructor': False, + 'static': False, + 'returns': type_.replace(' *', '').replace('*', ''), + 'returns_text': type_ + ('&' if reference else ''), + 'returns_reference': reference, + 'returns_pointer': '*' in type_, + 'pure_virtual': False, + 'num_args': set([0]), + 'parameters': [], + }) + clazz['methods'].append({ + 'setter': True, + 'name': 'set_' + prop['name'], + 'constructor': False, + 'destructor': False, + 'static': False, + 'returns': 'void', + 'returns_text': 'void', + 'returns_reference': False, + 'returns_pointer': False, + 'pure_virtual': False, + 'num_args': set([1]), + 'parameters': [{ + 'type': type_ + ('&' if reference else ''), + 'name': 'value', + }], + }) + # Explore all functions we need to generate, including parent classes, handling of overloading, etc. +def clean_type(t): + return t.replace('const ', '').replace('struct ', '').replace('&', '').replace('*', '').replace(' ', '') + +def fix_template_value(t): # Not sure why this is needed, might be a bug in CppHeaderParser + if t == 'unsignedshortint': + return 'unsigned short int' + elif t == 'unsignedint': + return 'unsigned int' + return t + for classname, clazz in parsed.classes.iteritems(): clazz['final_methods'] = {} - def explore(subclass): + def explore(subclass, template_name=None, template_value=None): # Do our functions first, and do not let later classes override for method in subclass['methods']: + 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 method['name'] not in clazz['final_methods']: - clazz['final_methods'][method['name']] = {} - for key in ['name', 'constructor', 'static', 'returns', 'returns_text', 'destructor', 'pure_virtual']: - clazz['final_methods'][method['name']][key] = method[key] - clazz['final_methods'][method['name']]['num_args'] = method['num_args'].copy() - clazz['final_methods'][method['name']]['parameters'] = method['parameters'][:] - clazz['final_methods'][method['name']]['origin'] = subclass + copied = clazz['final_methods'][method['name']] = {} + for key in ['name', 'constructor', 'static', 'returns', 'returns_text', 'returns_reference', 'returns_pointer', 'destructor', 'pure_virtual', + 'getter', 'setter']: + copied[key] = method.get(key) + copied['num_args'] = method['num_args'].copy() + copied['origin'] = subclass + copied['parameters'] = []; + # Copy the arguments, since templating may cause them to be altered + for arg in method['parameters'][:]: + copiedarg = { + 'type': arg['type'], + 'name': arg['name'], + } + copied['parameters'].append(copiedarg) + if template_name: + # Set template values + copied['returns'] = copied['returns'].replace(template_name, template_value) + copied['returns_text'] = copied['returns_text'].replace(template_name, template_value) + for arg in copied['parameters']: + arg['type'] = arg['type'].replace(template_name, template_value) + else: # Merge the new function in the best way we can. Shared arguments must match! @@ -185,10 +258,20 @@ for classname, clazz in parsed.classes.iteritems(): # Recurse if subclass.get('inherits'): for parent in subclass['inherits']: - if parent['class'] not in classes: + parent = parent['class'] + template_name = None + template_value = None + if '<' in parent: + parent, template = parent.split('<') + template_name = classes[parent]['template_typename'] + template_value = fix_template_value(template.replace('>', '')) + print 'template', template_value, 'for', classname, '::', parent, ' | ', template_name + if parent not in classes and '::' in classname: # They might both be subclasses in the same parent + parent = classname.split('::')[0] + '::' + parent + if parent not in classes: print 'Warning: parent class', parent, 'not a known class. Ignoring.' return - explore(classes[parent['class']]) + explore(classes[parent], template_name, template_value) explore(clazz) @@ -207,6 +290,26 @@ gen_js = open(basename + '.js', 'w') gen_c.write('extern "C" {\n') +# Use this when calling a binding function when you want to pass a null pointer. +# Having this object saves us needing to do checks for the object being null each time in the bindings code. +gen_js.write(''' +function wrapPointer(ptr) { return { ptr: ptr } }; +this['wrapPointer'] = wrapPointer; +this['NULL'] = wrapPointer(0); +''') + +def generate_wrapping_code(classname): + return '''var %(classname)s__cache__ = {}; +function %(classname)s__wrap__(ptr) { + var ret = %(classname)s__cache__[ptr]; + if (ret) return ret; + var ret = Object.create(%(classname)s.prototype); + ret.ptr = ptr; + return %(classname)s__cache__[ptr] = ret; +} +''' % { 'classname': classname } +# %(classname)s.prototype['fields'] = Runtime.generateStructInfo(null, '%(classname)s'); - consider adding this + def generate_class(generating_classname, classname, clazz): # TODO: deprecate generating? generating_classname_head = generating_classname.split('::')[-1] classname_head = classname.split('::')[-1] @@ -215,18 +318,24 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge if clazz['abstract']: # For abstract base classes, add a function definition on top. There is no constructor - gen_js.write('\nfunction ' + generating_classname_head + '(){}\n') + gen_js.write('\nfunction ' + generating_classname_head + ('(){ throw "%s is abstract!" }\n' % generating_classname_head) + generate_wrapping_code(generating_classname_head)) + if export: + gen_js.write('''this['%s'] = %s; +''' % (generating_classname_head, generating_classname_head)) + for method in clazz['final_methods'].itervalues(): mname = method['name'] - if classname_head + '::' + mname in ignored: continue + if classname_head + '::' + mname in ignored: + print 'zz ignoring', mname + continue args = method['parameters'] constructor = method['constructor'] destructor = method['destructor'] static = method['static'] - print "zz generating:", generating_classname, classname, mname, constructor, method['returns'], method['returns_text'] + 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']) if destructor: continue if constructor and inherited: continue @@ -262,11 +371,13 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge ret = ((classname + ' *') if constructor else method['returns_text'])#.replace('virtual ', '') callprefix = 'new ' if constructor else ('self->' if not static else (classname + '::')) - actualmname = '' + actualmname = '' # mname used in C if '__operator__' in mname: continue # TODO: operators else: actualmname = classname if constructor else (method.get('truename') or mname) + if method.get('getter') or method.get('setter'): + actualmname = actualmname[4:] need_self = not constructor and not static typedargs = ([] if not need_self else [classname + ' * self']) + map(lambda arg: arg['type'] + ' ' + arg['name'], args) @@ -288,20 +399,52 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge if constructor: generating_classname_suffixed += suffix - argfixes = '\n'.join(map(lambda arg: ''' %s = (%s && %s.ptr) ? %s.ptr : %s;''' % (arg['name'], arg['name'], arg['name'], arg['name'], arg['name']), args)) + # C for i in method['num_args']: - # C - + # If we are returning a *copy* of an object, we return instead to a ref of a static held here. This seems the best compromise + staticize = not constructor and ret.replace(' ', '') != 'void' and method['returns'] in classes and (not method['returns_reference'] and not method['returns_pointer']) gen_c.write(''' -%s %s_p%d(%s) { +%s %s_p%d(%s) {''' % (ret if not staticize else (ret + '&'), fullname, i, + ', '.join(typedargs[:i + (0 if not need_self else 1)]))) + if not staticize: + if method.get('getter'): + gen_c.write(''' + return self->%s; +''' % actualmname) + elif method.get('setter'): + gen_c.write(''' + self->%s = value; +''' % actualmname) + else: # normal method + gen_c.write(''' %s%s%s(%s); -} -''' % (ret, fullname, i, ', '.join(typedargs[:i + (0 if not need_self else 1)]), 'return ' if ret.replace(' ', '') != 'void' else '', callprefix, actualmname, ', '.join(justargs[:i]))) +''' % ('return ' if ret.replace(' ', '') != 'void' else '', + callprefix, actualmname, ', '.join(justargs[:i]))) + + gen_c.write('}') + else: + gen_c.write(''' + static %s ret = %s%s(%s); + return ret; +}''' % (method['returns'], + callprefix, actualmname, + ', '.join(justargs[:i]))) c_funcs.append(fullname + '_p' + str(i)) # JS + + print 'zz types:', map(lambda arg: arg['type'], args) + + # We can assume that NULL is passed for null pointers, so object arguments can always + # have .ptr done on them + justargs_fixed = justargs[:] + for i in range(len(args)): + arg = args[i] + if clean_type(arg['type']) in classes: + justargs_fixed[i] += '.ptr' + calls = '' print 'js loopin', method['num_args'], '|', len(args)#, args for i in method['num_args']: @@ -314,16 +457,16 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge if constructor: if not dupe: calls += '''this.ptr = _%s_p%d(%s); -''' % (fullname, i, ', '.join(justargs[:i])) +''' % (fullname, i, ', '.join(justargs_fixed[:i])) else: calls += '''this.ptr = _%s_p%d(%s); -''' % (fullname, i, ', '.join(justargs[:i])) +''' % (fullname, i, ', '.join(justargs_fixed[:i])) else: - return_value = '''_%s_p%d(%s)''' % (fullname, i, ', '.join((['this.ptr'] if need_self else []) + justargs[:i])) + return_value = '''_%s_p%d(%s)''' % (fullname, i, ', '.join((['this.ptr'] if need_self else []) + justargs_fixed[:i])) print 'zz making return', classname, method['name'], method['returns'], return_value if method['returns'] in classes: # Generate a wrapper - calls += '{ var ptr = ' + return_value + '; if (!ptr) return null; var ret = Object.create(' + method['returns'] + '.prototype); ret.ptr = ptr; return ret; }' + calls += 'return %s__wrap__(%s);' % (method['returns'].split('::')[-1], return_value) else: # Normal return calls += ('return ' if ret != 'void' else '') + return_value + ';' @@ -335,17 +478,15 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge js_text = ''' function %s(%s) { %s -%s } -''' % (mname_suffixed, ', '.join(justargs), argfixes, calls) +%s''' % (mname_suffixed, ', '.join(justargs), calls, generate_wrapping_code(generating_classname_head)) else: js_text = ''' function %s(%s) { %s -%s } %s.prototype = %s.prototype; -''' % (mname_suffixed, ', '.join(justargs), argfixes, calls, mname_suffixed, classname) +''' % (mname_suffixed, ', '.join(justargs), calls, mname_suffixed, classname) if export: js_text += ''' @@ -356,9 +497,8 @@ this['%s'] = %s; js_text = ''' %s.prototype%s = function(%s) { %s -%s } -''' % (generating_classname_head, ('.' + mname_suffixed) if not export else ("['" + mname_suffixed + "']"), ', '.join(justargs), argfixes, calls) +''' % (generating_classname_head, ('.' + mname_suffixed) if not export else ("['" + mname_suffixed + "']"), ', '.join(justargs), calls) js_text = js_text.replace('\n\n', '\n').replace('\n\n', '\n') gen_js.write(js_text) @@ -366,16 +506,30 @@ this['%s'] = %s; # Main loop for classname, clazz in classes.iteritems(): - if any([name in ignored for name in classname.split('::')]): continue + if any([name in ignored for name in classname.split('::')]): + print 'zz ignoring', classname + continue + + if clazz.get('template_typename'): + print 'zz ignoring templated base class', classname + continue # Nothing to generate for pure virtual classes XXX actually this is not so. We do need to generate wrappers for returned objects, # they are of a concrete class of course, but an known one, so we create a wrapper for an abstract base class. + possible_prefix = (classname.split('::')[0] + '::') if '::' in classname else '' + def check_pure_virtual(clazz, progeny): #if not clazz.get('inherits'): return False # If no inheritance info, not a class, this is a CppHeaderParser struct print 'Checking pure virtual for', clazz['name'], clazz['inherits'] # If we do not recognize any of the parent classes, assume this is pure virtual - ignore it - if any([((not pare |